diff --git a/CHANGELOG.md b/CHANGELOG.md
index faa668e6461481db7ee0c4dfe9aee378ecba85bb..4c06113b59fab532939026c1253305610dd6f728 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,7 +17,23 @@
 - No modifications / Keep major and minors versions in sync with all parts of the project
 -->
 
-## 3.2.0 (???)
+
+## 3.3.0 (???)
+
+### ✨ Feature
+- Add a run command on assignment (to run the assignment as an exercise)
+- Show an information message when a new version of the CLI is available (and not required)
+- Add a new optional field on `results.json` file to provide some flexible informations display for the teaching staff
+
+### 🎨 Interface
+- Add a new level of verbose for exercise / assignment run command
+
+### 🐛 Bugfix
+- Fix an error when providing template for assignment creation
+- Fix docker daemon detection error (thanks to @tom.andrivet)
+
+
+## 3.2.0 (2023-12-19)
 
 ### ✨ Feature
 - **AssignmentCheck**: Add linter for help improve the quality of the assignment (the linter will not throw errors but warnings)
diff --git a/NodeApp/.env.vault b/NodeApp/.env.vault
index ee797ee12ea991372b88e3f45f29142da9b2d76f..ffc4a4f41e9fa5f43212461e948295d51ba1ce8f 100644
--- a/NodeApp/.env.vault
+++ b/NodeApp/.env.vault
@@ -4,11 +4,11 @@
 #/--------------------------------------------------/
 
 # development
-DOTENV_VAULT_DEVELOPMENT="+X2VvBPV+1rmI5WUTNg1xcdpPDQlea8x/n19GqoF+wRuqpftUFH/bjbi5J7PonIY6PtVYi62NiGFW1cQlhKhEJC6vP1QGRgTTayTdRTxqfuWS/MGmBBBh8utzdDO73DK/aI60xXipytbp+couaF4i8v2C1ooac3sq4Jte41zeiqENInbliKaiJmXfyXPsAetU9wyGF0XYLF3WgO3LsYr8ff8l1vYyUaYzW6hkAM8m4Iw6ZSKfyApKhbbWJpMuCb//OFJ20Eo5weGbjj9ja9OUX/XR0oS8MvzkwBFrznSm6SIhIZmNPKsEoHAJqnVg0pLWjXnlWGWPJm+17tEyi/I0EmoUe3NQ3Bqtg/c0/8NQ6F53lI7OURrGlnjJqNJQtjc8bIpZHrQSVxfhZGLCqFuZMe15+ZWgUBZGShHttz2Qvmum0pZQ9hhudpK84bWdiB/snTRJ145h7SsBuP3liG1bP0mfq3ZE95+rKSzCMYnx4f7Bl1ohFd5nfiVrYWzZ7Bp5bll0eUFBpYoM2+gWveE374vreyCjGIshDxowzwbvugD57JcLIyoc9/BWrEit1bsrpB/cRGyQeBvPvsY1a76uhCn65vsVHe358zQ+mjWY3c2GB8EE8l5zAxFaZ/CFeQdwewhiPqQKrl1SUVuRdPnRCeUN0dia8ck2+cMGtCjcNEiQ8EH1U5YZnT8vcbOtfUXYSNQwEzTCUwN11zXq8SlePHluCDiYbXy2JffeRy/LBGXpfQkYstgxVOAnFE9fDm3rFqOSso3Bypz5ounyIYlleM/LWuHh2V/qTcRVjkTR211ALtYPErttmrkXQqrddShl0d2frl9gY6pLUuNdHJVjtVtQqSGIdnFEyCZ+jVB+VkoLR0xDuw2alEpRhehm4JJSatChfuQjOjaxZXe6dHfqP0ffW3vbPhgBWtEsgmTQvMQ0gYfNDahEnOxF4XK+PH+NdMYklPaHwuW2LJBPiTcYZuZvZGjnaActBIr3E5schXENM5oNWBQVcDuRFoh3WWwhsmRqUPfU2+wpuarI2zSfSKm3RXvWFFsOkTcOmKlS1lOxobBnG5clroegJFTvphceLk/JSV49qe9Xrg+1hPi2YF0cyI+Lb/V96WQ7i3o43+zlgsTkfYHfSrkadW7Bjiqh4s9QQo8n+dXCcuG7rFsrkjCUU6gp+tORvIsEmCO5tq5OYG/yJG42DWdUiYzYHk7oADSlAKKSIUao9g0D5QwuNSKzvlduEKR38oQXuGWyePcoG5zJEMjvk+zkf6CUTC2vo4SlvnBknyC4nN8gsMAFKkyrVRrEJtTiCr/TcwkxB59JX9j6yiq6SjHZBivIdBGm5mxLSv2QAdkt9Z5UhRyiCZnNJ6LmJzUVjCMdDY67c35wB4GFfEg/oVZh2UGsMdx0Hn+Cn5GHhzb7psVFajB5utdCbPBBuhBDx8Gew+z6nJUTMcshZPE6xnBkII04OzX1PtCG+uPgeO1gOfbfGId8BEmjuoQjj51zhws2oCQ8dk3uql9pcK60CqqOQyMZsA4bW3iprJFRJ2t4KWAcXsB8mRWWk0JW0kECSHAHOYPY47p9Ej/TNSZe04bgYhlopswsJ4Js7xGx2+Z4kvKLeq6nU4S0TY9+lx4aawa4n3E/bdTSr5hSSmDIewCMGKAnyZq9Co+XfqRWBUDExHUz514O7yCHh2PH9+SvzHeysHo4r4y3TB+YwksUwH4lSVafBlOj9jv8xB0Rj7eCrBKbWNPLCDTkdatZhBky9lb/21x+7iOImM="
+DOTENV_VAULT_DEVELOPMENT="PGJQsAT7+pw+zHgrU35pJxkO83WNtOh/z5giXsMe3d0pBAhbG1tGCsja0YnME7Zy+WJgqVsELLdbzz/0+SHJ40LkTljDxTDVWVoskfk6eNHZ7KqxhFn2AH8jS9c6LSbN7knbpzp7oO5ErS1YDQ+yAKnchq4KLhtpTYAFu05MCQZmsNohvmz4YJoJhI0wK6A5Hx8LHmVGyuzDlLTb8NzR7Aoj2fkJdRMRt1jf617EGcdBLTkiFic9VxsNR9omP8fd9SoGukfuxf+XTgl7ub+F1UWYlHynHuAdCSqsaqCWZwof8BTrzry5mnYteZdMgTU1WoZT5WI9BaQMrRCuQ+Kbi74hEo++xHmK8fkmhYYpcv+Qv44w66LyAmzW0uDn1IfA09hk5uOC9SxQPEN+evdO6MauN52O1v1XQ5kvsHg6NZRSXytPQGDsorieM6cKdR5A6d7SMt/BVEqoXmpGBULl5UYkwIxWy1THVnnRKUHZx95yb4vTMHOMct2132UalquIr/sdZdp5litU2Xip3xGE6SDfT/FrokaMlLo7f9M5Tmv48PA8vrj9Wd38amY3FyHBjTQiB0EcU3Ski4oC/vR4TduNZcORcTY6p3OCDmixZhJngMIP0ElCN+TWcoHJ5qdJ23M2javmRlkDi3aolqihGURfkauhsaJXzUuln7M5hLHtWgg3krtHmHN7sGfnymsUJD89ROYPJR7HpwWUOMrTcflqcn/u8/7i+UdImsOq4SZyiiTETA6D2+smrzKZ+5VhUE73otgo6cRWQdpLyrddHjBW0IsHDCadDvGBTpiuOcslfeFkTQ2PQQAGEgCL/7UJQ4nZlzgVeToO90EhjNTgx7Uwp74Paj4cPYu3nzv98nem5cuqAE2VqoU6ai51duJA/qQg87eQLUN0t0A3rpO02JrvAi2pmoZNksXkYsxEKoT6miVAhj6aanFAUZ3dHCoRE3fex6Txt9S04JoPJ89UpMXMC70Q+/NXAanDyQQavLy0LohpBcfgLQbL5OnmjPwLUebFMa7EjDS5MkHVf1aCQ45V1Ou+qiMQj1pW9SJmQ7zeZWxgPa6d2E0AaHxN4dNyrzaxvJkcy6vREsnK6cCZQ6czLfUDQoYA1CE9zUn2mxo0KGBsOzgpzjX+1ey/Zz8wkAC9YYEGN9l8qprv7QtgYGhb6p9O74XFRXUqxzk7e6GVxDijWovmEST+z1cW2eZkDLsETtEx+v6peoBFFWYY5ShxZFiL3oA3JB7dbiNMW4Mvemb4pzmc6PhooLL04nFzcBdK7HB7200XXhLNkvCCQspvAFYNb7H9dvqciNAO/YaTUHzw+Hh1XDQm2S6G8No+AuYwohT2NqPGufbspu0j81WQ8LqOWfMdr7AGaNA3uy85beZ/R14D1Lpaws5+WiQUWfe/HmLWSYmdWUMNgrKuoFTyBsXe6cNU9H/syNvtiQAi7Qp+KaR1Qsb3islEaxr+Jng/fVIeDnKD94b6fURh/ZFB2NQRiEycSwPswhrs6jsYrip9UMWLdv+agUmd6u4VEWr99fWUQqovl0zAdgWm7viTymqxMKSI2S0coiJ2lScSMV8tt1tqToyWAC81h3yGj7KCD5TS82cFTPYcpqdjHij2jeDt/EU5vASPTqwEB+yOoI6SRR8VFs9IiQqCIjFnVAuIkeGMETNEerDDhrAgm0+8JOWBEM4p3/p0I6A7CovgvY2FqRLh0MfLDxhFv+rG7nOeUaIAVgF5KXNzozJjGtUVrgqHVWBHmOFfb8jgIGWU4k8oyOd4Yfh0WAVt+qhqUQmeWCcP75Hqs9QaduHiGe/iJbdbsIAu21qxHHes8nDpq8I83Q5CR4qvtAa4Swr9I4M5a8KCUHvGI7LolZnOFDADL0Zr4eQ="
 
 # production
-DOTENV_VAULT_PRODUCTION="oWrK1gJdJMqCg+x+RRncAXdyGKNYWo1CqZTYprv0TZX1LEQlyJh/PuhaSmmiagCyWQC1OwLRwz1Yox9ndTZn23pp1hbNVOZc1sv+y56Y1GJh3fC2FOy+u6cb0p743k+WqMVakGbeC8aAXAgwS6CoRy+xanswjcIekoYx9ZuBe2n7sOwTOCzt0IH4UFQ7rdu5f4nPix7Nt0NUnLJO2EYQ93jZvXZg4NhLbkPmqs0b/P4x14BjQlUBSGHjb7kqYmiRhunO2+HynfGLGypiSCVV1RgfDBF7R4jiwvt3kCzMDsKuw4Os2SOb5zJ2ezcv92f+PqeVNgFctFkXBvDsY9AMr0BeB7jz8CkpQvfUtKB6OVneRt3V4lZ0Jjn4Os/swkQStuJQn4H1aoBOZ3uFG399R4FAKz5bNqdmZuxk7ljHelSjjWhjR7qeQevsfA04ZH97WwyWoksI9uF1v8yEz4iBNGmUEomZS2LOdU9jhuuDmZiSb5r057ZbCTFNI0HwSTvFV2U6T2bf3ISlbwoTHrAeKJGjZGNs+mRFx+eVMkjVtpmPXAgUbfkIU/n1/ytHuX8xVvq3n9zj1hZFPDLUTRMRRXlG540tpb5FppEaetglZKEfrJJXzHYQ2/rLX6o2ro8J78qqkD9+E4vMiU+e10oGO1aDcQt7JYSrbB7gY6Pb5zFt7xo90fedeeDWrsQJBrQ2L6VRRIxFGA+45wpyMmAAMoMUNaoJ03sjWK4qobT0/0qmE0KKTO9H36Qxymo9rWFefIoJ9S7wU9r8fCqPeTvnQbKzw6BpxNa4fT3bevza93i8gmzBArC2lL4At0EVVAOCtu9wkSae9Si3NFqGHeBVum/db12xCwQh9USarwbOtRDCp/Z6xhH5QrwkvhOv44zvoDAOhwlQR8RUVYjmx1nE7TdSeGLI4xodeuWsZZHlTWXgx+Ga2oKqST4bK4wWdWDVfY33FNBlZCD9p+VWg6y9vP5tGHxDbWj9ViSSPsTQnz2p/AD6ktmHlo1Tg0DeQerZ8mJTavW6a1X9go+F5cTkaLtSZ0XDdHvg5mghir5ZmonyYJ/uLQAavY+QujKO3g0/LedvIzXpSVCXSquEiLMZIQpg/Z7tuYs3Tb4cM55i+rwz/cBtVE1Mb18ITPn3fWlGGud7uOzeCibb5HN9zXCHWDgyTqUbp1u0bd6INp8v5LjfHU7Bxh8KROS1BnOAYXE8FbQRbmkrxF7AATDfYWhvzIcPQWfbGr9BT8n+n3klBqDuYtl///PHul4ghHOaBrMzLE+d5Iz2fH51gEwbTD+QZtfvnKML+NK8LYXBwE17Jo/4kHeJ8sFUwCY0qD7KWBWieWf8Zen31Mxtg+dLnFd4Sn7/YT9uAkmUGqhjW9f2WEtuvP/SX3SmAZjdR3apU2Cv3VK738UlX7z9uiaYKbZSOHH7dX+WOfCVebZIakBiY/ljnc4sblXUrWq/P1MLo1wlLUdYit18JTRkk8WLhi5z25hJY8AnQ2QR4kHIYLXJa5j30IGz8qV6o6Y9NoChdeqh6J9ZaW0UThkgl8yszwM6fz3wSjsmL/IS2U5Y98+eftfdk8Ws2MigGeF+FdTJcoFquceQZ8ezYQUny8htTHCXdBrY9Fczqi1IYF1CqbAUJLS/sLsyrNSbeg=="
+DOTENV_VAULT_PRODUCTION="WBdIavLQxe/v8Yxg4DkESllPpVGVVfXbAjDe2Br7RKq15bxIEGGTloHALe4/53aX3RGLzeSkSdCBAQYpnFXOZyQdqRYXzJQm7Q/8dNUZoA9axnSlsJeysAagq5/MKHQo7NHfL3q+y8bM4EmW41VSmp2LkQH4s4Y/C8bvxcxZ8FSneB/xSMorT9/5+MZ9GS9kjKHAYAbNuXuKbnoNieVaSijbRaD1ue+fhwxs+/ucgGjatyxQ424JtuUtledqdGMaAKgWUOKTg5odJLDlf3LVknsLt3FxkvYJMMNSHeE1SJHJRVyOlCwdAUlUPJoYQENkQ7Z9NgE/EaSTMzXcqtiv6pzPrJ3p0kNhvGUi4mZ0rItph8/LQn1JDQQCS8lpjCtNkaeDCfHrjBWwtnZdSMV8A8Z/BzQb4oOqzcaL664GdcdoC5mkTGFxFbMZNgAnHHAGrBeq3GXzQY+L15eRPra/ckkK9+oxHanDKpX8OEXkD0tKMwcffs/EOqqds6XKJqBjABlMjiXG0Ihqrjx7CRkDWH3NnIjd4UG8cNJlgj6cykUK891AtJkyj8EK55ESExTJJpDF9G21MjYpOn9nXilA1Sr+46/Zt2X1na2DREd8XXAwZUxRoNk7BeVDRNKwSDwFHTCLTLdG/lA6myNpWhuvTu8rAMiXmyY2I14zHKJ0BlwbSH0U+nSAOrqagb4B4tnE9nYdhHrFwl9YoxJW/bmJLxlrQT4Go9ERN7PBuR+H4Tzr5FUC0OjWXveUdiEe+7DhT1UGVRjlLQmyn0YgZvkTuj920nhL7SIXu41w/tk2sA0A3pdKd3RjNHC6dL5aEyvPEfldWexMjbQeXsTmEJnBVvx4kNLYMeLtjuZ70Ks9qVaCxnroupMQ8EPi3bvcgOjv5DMxQQtJ5eH1viQMYRc7bVrSpde6eN3YM0lwgguTYJ+mKfZiBzyeB6OEhDf60A/WqJBdmt1LxzPADK7P/BVhAD3VV4QZkEdXFQXNJePm8G3O+K5XnsU5ZxIef75sRtjUL3A5awzD2ldETDcSqcGypOZ/TuusvEWQ1V9xDaxmG4fIFYzOcYzt7dkf7/lSTEA0eOb75JymCtzg1Gcj3VzEbYbCf6BAxQnFXdA8aHqeRe5Q4MeYP/CbrtXuHisZT9uwI4wNKeUOiBJnK0+nG0kgE6p6GmPOoKfagctQe/xTciwIUSuK89esgghJycXtfOaHeJVKzIicsbETVdf3myqqdV+BJgSQfKD4YeJMSehKpc7JK56lLd/E73KXFrOjgdY5BPJpnmWNc6qcuZ0ZzYHybTfiG7FWSRKFNKX/ks+XrK5OeeFr2IiOG2AzvYWboU4lP3SM7SD58dY8K//iNi9/8BT99zLrMrMhhOjjVPKoSlyvQYW8mHkx/28cPGUtUWrDT+pcrsDqLgBZnZGBO1zv3ym0MysZsDizrw8eG+HJHnFwrrtmIQVP+NMIpVwipnh4Gd5isjWqpeg0z3rjkLt1D6vYx0E4fieCJ1jPgHpDCqJVUWOT8N3/+Ynw+f/f2PbHNKISFtGSh/1/GEuDSRGfiJngOYmK85mggk2tH4Oh3tWKUi10yWID22zybMW5L2XUonue7e2hGKoDhgIpjeZHf7j1a4NRfTW0eCr3724Y+yNxDu1I+Ll28F783t4h4TbFOy5YrpIa8as/mkeesf/6EeNnS/7G5L4aqTYn6CauJyuFXUs51fsFvWrRfeIxg44ONGKr6wWC74iXu20EXV2CuTOEKguchmAlU64fKw=="
 
 # test
-DOTENV_VAULT_TEST="BXB3Ba35Ou7y/DX3gWH/BtaC5QL2VLY1/ZUpZp4AgvCgQEnADiwYvCeaWJ039VZGeY915MKldYf0VCmDOdTixdeG4xWd8c+TvjvG22l/TIzYkvsjzqF90tRwrQjpFdi+s+z6q8PlDGDoyoHtfUeBfxHk/c3Q7QckZC0im9UO2JLvOLNux9SFk0zvc8ov36fIxKcniUwH6ctPo4CKhVt1eT6r9fPt4HS8hEFYpQ7kbs+tKfL4htk94HcXu+aRony5fn3cawg/6+akMG8eF8jhrrvbJWlCR2W3krb6ApY3dbD9Ve9IGRTyM7yfQ3xjey97hEMLf7rEGtoO5a/ywPfOmMUAQcTSuE4a12jRXflFXbbfjarM+oUTbMTVi6hSEgY7V4yQouer5U3VVyCYJudzEgfWFFZdLBEhb+FGsCzSUxIwO/5fUg2E982ZUkd6Oz/77fT2/8oywkFZkAFJV9A9WWFOoM+2fvMLd2XbGnRq3zOMYjj+H16u1IBh31VnMuKFLBwGUSLjTGtDLR3VgMBWJCWGPto7yUUHCtQA5eBzgYL/QEEKM2gmpnBryPB/V7p3YTAQELlQWhhnTNDhXs9lt3mmhl9FP1LURHY4lgpRulFDpFdCKujLOfir87WhF53YnPGkODS/+Zywk1GC2hY2+nmUhwhClqRw9+mYcPFhK9ACdwPCTdK8Vs4I6lTsQMFXKLJL96V1bbes7fspfSrB2f5mZbrMN1B+GOU2OvkA1W7+rFDFVJxDWjV4rjVHlEFX8H1IVcNm0D45F1F5dAgMGiwo7RXka3rM/SWxiWkOINIFraeFYP4wf5mJC806i+3NNOx5wROve9XP2Z88/+Ol5vm0hu41Qdry3dYg7lTp1+RX5wavIxDSSgF9e5VZLTTj9WcZ8/r04nWTvRG5D1DFcznoPUTB3APhphE/b/wTuRz4uM+g2q0SrVt7UE/NYnQw2XGOaTu8MtFibwhvXZ//iLnChVhMMRVB6m54GMCr3YcUiOyNceqFTzx80+5W2MruuCEc/m3RBEzxzjttYHpPPxm/NY+Qmb9X1jwVeWUJkKE9fiKlCzWx3lEgpnWemB+JZUKvG1cbV/OgZDBIuHvx8aTYg7gMG3V0Do4tXQBHoPaHvomesp4efTppcYB2gpsFkZpRhjrCBjo5STfNUkFO6RuBi8y9VbeDXdtcbFwNptKDr3zqOGHK5KFhtSMEYxcA0E9CdGEznEMt1klW9QkDCNxD5EqCqX/IVUxhd9q2RIwb0HmUF3Qh/CMCTyZIPN2JYHcZRCPc8uCiET9i7jW++YnICKvt0X55CpWJgLdldtvn69slB8w++DSlIliWBDJG0MNNWrF84jOQY6DEMW/cigoBaQPgl/al8L02zq1q5pughZGZ+zLVrkUqQNTL3cK2KKf22UcfwwLy7uor6CorA7m96JSlwHVBqGjwYG/5LqPZ1C46sxkWrSMKbkKcd23AH+Jla/Xrz9tW5Erdc4iym2iQqZHIPTqRbXoVskzMuQ9pS8NS1pSflztpFy31+vo5idw7SlxISQ4bE1bKTsKCO+hCkGTMF2659Kh4yUwuZzfN0BzmCF8JQ0eFj3L1ejJX+D2Tq9jelqPNYD/j0306YnXNbyjH0/57DX5Cdul6hIWz9bWGaDMZi6V6xUz4dwCDfe4AOP/vpVjoIX2X7IFachUTyXjCR5Q34A=="
+DOTENV_VAULT_TEST="7iuLqR00OyNGd75KojqFNXF6E/to+EI07PwCn99NhMUhMwtPWY7Q5Eq+nQxKJzutGaK1J49CyFd5ecnW542ZvRF+L0gPhZgmgMrSMAAhMFbvNDQly5L9Ue7w6WO4U2AXfaSSA1RiOTs+ifeYgMjRSmZZvSDkSyuNsDvUXde6kLrzvVxF4wVceA68q0CccPulchlsODbNNiM8RTvJVbIg7oDZzlw1s9EuxNaReUwOz38B4d1M+d6dGkn/7YjDUzNKPSHX9Xb7rtEo6J0BFS03hxENmp5BkZh7g4rWvdICHPizvy3FvIuWE7kYyU0VE+svCujdnNn5EYgv7UM5IXzz6HuY4BNATk7Txx+IVwg3pped2KYtAVxE/lzd0ckXj8GHGnyvhpIekvTAgubW59GuFOHSB3/BnhZeFn/kluERtPZSPRA2YqOVMLCPmsSvA5wnls+ins7P2Wzo7VhGrjXN1ryS21w/SDgCY0QWwS0tbgyeWWrl92Dknm0FmKaQWEY9GgPvcqG5GHoE8mwpgSvU56Gzq8vtSJ7/SCuSDze0XBQ4f8SEASqmjhLFkYQDIqlr9U/eaBAoqpmvbi5QLcGL0GAv3TaXJMRCEnf1YAUIJLYoB335IUY/3JBGPMQzVC0/5GWYr7masxWVp8KV2ZRWExai1Kxf6SCOGJ0zdBp53LUJdHJVY07a5QiQHdyPlIddRf6RTgukU06t16G4RgGIuC/jLyencD3wEMW9EEv/SG0vCMstD1FHlhDGMEkZagN9um9RPKjnN0VbqgA/y4lllrhc2FRm+ZDdTO1vQebzYE8d+laAD5pZkO7FjNc49CMUsIWkHTrfqzdoAU53g0MbASmlWjLOUUN0JLxJA9mdrhKjf3VcVjNaFjWMgNb4w+rtRaWsWw170vyOQQ31VmwRd3paBW+2ClvL1NEwRmLsLLbBYcVwJ2u4kSOudAPhYoguK0Mej1YjFntL7v4rrLgwnNI0PmPCFAR9BvnkJ+z6o0RrTttuSMw6IM78ERZyxNQTfZBvQi/F5UeqgMrT1YCG+g09gfgft1KUzSeWB584oNyRhxp57StZuVAeccBhm9RPKRuSzBNyyLOWVFu3vqmoaDmEE3vpMjjazJK+sjPAjQil9RkgytJL2AagZcC3RQz55K+ls2rqvf0IbTd1qQP7cLpXIIjadiZjLOzxHbnEFa1laC0WPzVlb08eIrBr9tsB28n8An7tiqCytdSpy864IhnLm1mjzKeJj5zI59S6bec+5zcw4/prPlPfaQ1ChA5XUeeCpIpowVqBOrleGp3YPI7Xq5jpsAc7+IEqxOUtB4DO0LqZkmkt9mV9rcpHW00wkESHMUJ+h1zfjYLn6vwkgnqj7YHjr6xksm7tKGN9Ko2GLQnR0ruMKjL+fGLwFYD4UOOgUro+MDyJ9Ls8rBiRjB03Ikat5XG0iRxxHSSNR5drWdDSJM/khSA6X5aHRzO4L163Ooo647xtQFXQrS6rHkvBtZzcWyHyXoyh4jnNyWD7vUrSj05cvF+S+UbAVdhRPtkMyGNEqieISwm4nfAL/w1KSKvPUlSSxkyim0I8vB1/mICyZp2oTBm9PbemDoLGiNyvGg85q31PJ1+qk6XsXq/uxfUQokD1/GHhr6fKMdLcWYQ+GLY6VWPpk22ZLhrYgRsNdX46ZPF/5h312LiwG++5GTBpI9p52yk1R2I4Fvrg8fJzA/DNMWHo0EHYFP1VieMJ2MF/BS2vUMUMyCw3nX6iSyQZ6Y6Rc73WBvPvOJYfTew27cGYoR5JRrLQrFXAhuNk8vqItobW8obfTQ=="
 
diff --git a/NodeApp/.gitlab-ci/01_functions.yml b/NodeApp/.gitlab-ci/01_functions.yml
index eb19c1337dfcdf5ad302ebf0b76c1e1b2b0a6e23..08bddf31c6f3805135cc0916127343ea14a726bc 100644
--- a/NodeApp/.gitlab-ci/01_functions.yml
+++ b/NodeApp/.gitlab-ci/01_functions.yml
@@ -137,4 +137,66 @@
             for deletePath in $packagesToDelete; do
               echo "Deleting package at path : ${deletePath}"
               curl --request DELETE --header "JOB-TOKEN: $CI_JOB_TOKEN" "${deletePath}"
-            done
\ No newline at end of file
+            done
+
+
+.release_gitlab:
+    script:
+        - !reference [ .get_packages_url, script ]
+
+        # Extract description from CHANGELOG.md
+        - CHANGELOG_LINE_START=`awk '/##\ [0-9]+\.[0-9]+\.[0-9]+/{print NR; exit;}' CHANGELOG.md`
+        - CHANGELOG_LINE_END=`awk '/##\ [0-9]+\.[0-9]+\.[0-9]+/{ count++; if(count>1) {print NR; exit;}}' CHANGELOG.md`
+        - DESCRIPTION=`awk 'NR > '$CHANGELOG_LINE_START' && NR < '$CHANGELOG_LINE_END'' CHANGELOG.md`
+
+        # Create Release (can't be done by release_step of gitlab image because it don't have access to env var defined in script_step)
+        - >
+            RELEASE_DATA=$(jq --null-input --arg version "$VERSION" --arg description "# Changelog (version $VERSION) $DESCRIPTION" --arg tag_name "$RELEASE_NAME" --arg ref "$CI_COMMIT_SHORT_SHA" '{
+                "name": $tag_name,
+                "description": $description,
+                "tag_name": $tag_name,
+                "ref": $ref,
+                "assets": {
+                    "links": [
+                        {
+                          "name": "Windows (ARM64) binary",
+                          "url": "'${PACKAGE_URL_WINDOWS_ARM64_BIN}'",
+                        },{
+                            "name": "Windows (x64) binary",
+                            "url": "'${PACKAGE_URL_WINDOWS_X64_BIN}'",
+                        },{
+                            "name": "Linux (ARM64) binary",
+                            "url": "'${PACKAGE_URL_LINUX_ARM64_BIN}'",
+                        },{
+                            "name": "Linux (x64) binary",
+                            "url": "'${PACKAGE_URL_LINUX_X64_BIN}'",
+                        },{
+                            "name": "Debian / Ubuntu (ARM64) package",
+                            "url": "'${PACKAGE_URL_DEBIAN_ARM64_PKG}'",
+                        },{
+                            "name": "Debian / Ubuntu (x64) package",
+                            "url": "'${PACKAGE_URL_DEBIAN_X64_PKG}'",
+                        },{
+                            "name": "macOS (Intel) binary",
+                            "url": "'${PACKAGE_URL_MACOS_X64_BIN}'",
+                        },{
+                          "name": "macOS (Apple Silicon) binary",
+                          "url": "'${PACKAGE_URL_MACOS_ARM64_BIN}'",
+                        },{
+                            "name": "macOS (Intel) package",
+                            "url": "'${PACKAGE_URL_MACOS_X64_PKG}'",
+                        },{
+                            "name": "macOS (Apple Silicon) package",
+                            "url": "'${PACKAGE_URL_MACOS_ARM64_PKG}'",
+                        },{
+                          "name": "Wiki",
+                          "url": "'${PACKAGE_URL_WIKI}'",
+                        }
+                    ]                       
+                }
+            }')
+        - >
+            curl --data "${RELEASE_DATA}" \
+              --header "Content-Type: application/json" \
+              --header "JOB-TOKEN: $CI_JOB_TOKEN" \
+              --request POST "${GITLAB_API_PROJECT_URL}/releases"
\ No newline at end of file
diff --git a/NodeApp/.gitlab-ci/08_stageClean.yml b/NodeApp/.gitlab-ci/08_stageClean.yml
index 4eef3851aaa4b5b47ef46f6f3efa81e95216f059..b1becbf50f4fffa4909ab5d116827e8fa2196926 100644
--- a/NodeApp/.gitlab-ci/08_stageClean.yml
+++ b/NodeApp/.gitlab-ci/08_stageClean.yml
@@ -45,4 +45,28 @@ clean:dev:packages:
         - VERSION="${VERSION}${VERSION_DEV_SUFFIX}"
         - !reference [ .clean_packages, script ]
     rules:
-        -   if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
\ No newline at end of file
+        -   if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
+
+
+clean:latest:release:
+    stage: clean
+    tags:
+        - gitlab_clean
+    image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest
+    script:
+        - VERSION="Latest"
+        - !reference [ .clean_release, script ]
+    rules:
+        -   if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
+
+
+clean:pre-alpha:release:
+    stage: clean
+    tags:
+        - gitlab_clean
+    image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest
+    script:
+        - VERSION="Pre-alpha"
+        - !reference [ .clean_release, script ]
+    rules:
+        -   if: '$CI_COMMIT_REF_PROTECTED == "true" && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH'
\ No newline at end of file
diff --git a/NodeApp/.gitlab-ci/10_stageRelease.yml b/NodeApp/.gitlab-ci/10_stageRelease.yml
index e035e3481daf0152a01678acbf486842a61de4a2..ab82f3e2ec96fbaecc6accc1d71f045604d5e4ef 100644
--- a/NodeApp/.gitlab-ci/10_stageRelease.yml
+++ b/NodeApp/.gitlab-ci/10_stageRelease.yml
@@ -53,63 +53,33 @@ release:gitlab:
     image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest
     script:
         - !reference [ .get_version, script ]
-        - !reference [ .get_packages_url, script ]
+        - RELEASE_NAME=$VERSION
+        - !reference [ .release_gitlab, script ]
+    rules:
+        -   if: '$CI_COMMIT_REF_PROTECTED == "true"'
 
-        # Extract description from CHANGELOG.md
-        - CHANGELOG_LINE_START=`awk '/##\ [0-9]+\.[0-9]+\.[0-9]+/{print NR; exit;}' CHANGELOG.md`
-        - CHANGELOG_LINE_END=`awk '/##\ [0-9]+\.[0-9]+\.[0-9]+/{ count++; if(count>1) {print NR; exit;}}' CHANGELOG.md`
-        - DESCRIPTION=`awk 'NR > '$CHANGELOG_LINE_START' && NR < '$CHANGELOG_LINE_END'' CHANGELOG.md`
 
-        # Create Release (can't be done by release_step of gitlab image because it don't have access to env var defined in script_step)
-        - >
-            RELEASE_DATA=$(jq --null-input --arg version "$VERSION" --arg description "# Changelog (version $VERSION) $DESCRIPTION" --arg tag_name "$VERSION" --arg ref "$CI_COMMIT_SHORT_SHA" '{
-                "name": $version,
-                "description": $description,
-                "tag_name": $tag_name,
-                "ref": $ref,
-                "assets": {
-                    "links": [
-                        {
-                          "name": "Windows (ARM64) binary",
-                          "url": "'${PACKAGE_URL_WINDOWS_ARM64_BIN}'",
-                        },{
-                            "name": "Windows (x64) binary",
-                            "url": "'${PACKAGE_URL_WINDOWS_X64_BIN}'",
-                        },{
-                            "name": "Linux (ARM64) binary",
-                            "url": "'${PACKAGE_URL_LINUX_ARM64_BIN}'",
-                        },{
-                            "name": "Linux (x64) binary",
-                            "url": "'${PACKAGE_URL_LINUX_X64_BIN}'",
-                        },{
-                            "name": "Debian / Ubuntu (ARM64) package",
-                            "url": "'${PACKAGE_URL_DEBIAN_ARM64_PKG}'",
-                        },{
-                            "name": "Debian / Ubuntu (x64) package",
-                            "url": "'${PACKAGE_URL_DEBIAN_X64_PKG}'",
-                        },{
-                            "name": "macOS (Intel) binary",
-                            "url": "'${PACKAGE_URL_MACOS_X64_BIN}'",
-                        },{
-                          "name": "macOS (Apple Silicon) binary",
-                          "url": "'${PACKAGE_URL_MACOS_ARM64_BIN}'",
-                        },{
-                            "name": "macOS (Intel) package",
-                            "url": "'${PACKAGE_URL_MACOS_X64_PKG}'",
-                        },{
-                            "name": "macOS (Apple Silicon) package",
-                            "url": "'${PACKAGE_URL_MACOS_ARM64_PKG}'",
-                        },{
-                          "name": "Wiki",
-                          "url": "'${PACKAGE_URL_WIKI}'",
-                        }
-                    ]                       
-                }
-            }')
-        - >
-            curl --data "${RELEASE_DATA}" \
-              --header "Content-Type: application/json" \
-              --header "JOB-TOKEN: $CI_JOB_TOKEN" \
-              --request POST "${GITLAB_API_PROJECT_URL}/releases"
+release:latest:gitlab:
+    stage: release
+    tags:
+        - release
+    image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest
+    script:
+        - !reference [ .get_version, script ]
+        - RELEASE_NAME="Latest"
+        - !reference [ .release_gitlab, script ]
+    rules:
+        -   if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
+
+
+release:pre-alpha:gitlab:
+    stage: release
+    tags:
+        - release
+    image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest
+    script:
+        - !reference [ .get_version, script ]
+        - RELEASE_NAME="Pre-alpha"
+        - !reference [ .release_gitlab, script ]
     rules:
-        -   if: '$CI_COMMIT_REF_PROTECTED == "true"'
\ No newline at end of file
+        -   if: '$CI_COMMIT_REF_PROTECTED == "true" && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH'
\ No newline at end of file
diff --git a/NodeApp/package-lock.json b/NodeApp/package-lock.json
index 22d254cfdbd40a28109101927822f7b1d437a9c1..02fe3de63ed6c0ccc2dda7508a2d2d24783ab6d4 100644
--- a/NodeApp/package-lock.json
+++ b/NodeApp/package-lock.json
@@ -1,17 +1,16 @@
 {
     "name": "dojo_cli",
-    "version": "3.2.0",
+    "version": "3.3.0",
     "lockfileVersion": 3,
     "requires": true,
     "packages": {
         "": {
             "name": "dojo_cli",
-            "version": "3.2.0",
+            "version": "3.3.0",
             "license": "AGPLv3",
             "dependencies": {
-                "ajv": "^8.12.0",
                 "appdata-path": "^1.0.0",
-                "axios": "^1.6.2",
+                "axios": "^1.6.5",
                 "boxen": "^5.1.2",
                 "chalk": "^4.1.2",
                 "commander": "^11.1.0",
@@ -24,9 +23,12 @@
                 "jsonwebtoken": "^8.5.1",
                 "open": "^8.4.2",
                 "ora": "^5.4.1",
+                "semver": "^7.5.4",
                 "tar-stream": "^3.1.6",
                 "winston": "^3.11.0",
-                "yaml": "^2.3.4"
+                "yaml": "^2.3.4",
+                "zod": "^3.22.4",
+                "zod-validation-error": "^3.0.0"
             },
             "bin": {
                 "dojo": "dist/app.js"
@@ -36,15 +38,16 @@
                 "@types/inquirer": "^8.2.10",
                 "@types/jsonwebtoken": "^8.5.9",
                 "@types/node": "^18.19.2",
+                "@types/semver": "^7.5.6",
                 "@types/tar-stream": "^3.1.3",
-                "@typescript-eslint/eslint-plugin": "^6.13.2",
-                "@typescript-eslint/parser": "^6.13.2",
+                "@typescript-eslint/eslint-plugin": "^6.18.1",
+                "@typescript-eslint/parser": "^6.18.1",
                 "dotenv-vault": "^1.25.0",
-                "genversion": "^3.1.1",
+                "genversion": "^3.2.0",
                 "pkg": "^5.8.1",
                 "tiny-typed-emitter": "^2.1.0",
-                "ts-node": "^10.9.1",
-                "typescript": "^5.3.2"
+                "ts-node": "^10.9.2",
+                "typescript": "^5.3.3"
             }
         },
         "node_modules/@aashutoshrathi/word-wrap": {
@@ -72,9 +75,9 @@
             }
         },
         "node_modules/@babel/helper-string-parser": {
-            "version": "7.22.5",
-            "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
-            "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
+            "version": "7.23.4",
+            "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
+            "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
             "dev": true,
             "engines": {
                 "node": ">=6.9.0"
@@ -116,9 +119,9 @@
             }
         },
         "node_modules/@colors/colors": {
-            "version": "1.5.0",
-            "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
-            "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+            "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
             "engines": {
                 "node": ">=0.1.90"
             }
@@ -180,9 +183,9 @@
             }
         },
         "node_modules/@eslint/eslintrc": {
-            "version": "2.1.3",
-            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz",
-            "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==",
+            "version": "2.1.4",
+            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+            "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
             "dev": true,
             "peer": true,
             "dependencies": {
@@ -203,23 +206,6 @@
                 "url": "https://opencollective.com/eslint"
             }
         },
-        "node_modules/@eslint/eslintrc/node_modules/ajv": {
-            "version": "6.12.6",
-            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
-            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
-            "dev": true,
-            "peer": true,
-            "dependencies": {
-                "fast-deep-equal": "^3.1.1",
-                "fast-json-stable-stringify": "^2.0.0",
-                "json-schema-traverse": "^0.4.1",
-                "uri-js": "^4.2.2"
-            },
-            "funding": {
-                "type": "github",
-                "url": "https://github.com/sponsors/epoberezkin"
-            }
-        },
         "node_modules/@eslint/eslintrc/node_modules/argparse": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -227,6 +213,17 @@
             "dev": true,
             "peer": true
         },
+        "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+            "version": "1.1.11",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "balanced-match": "^1.0.0",
+                "concat-map": "0.0.1"
+            }
+        },
         "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
             "version": "4.1.0",
             "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@@ -240,30 +237,23 @@
                 "js-yaml": "bin/js-yaml.js"
             }
         },
-        "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
-            "version": "0.4.1",
-            "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-            "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
-            "dev": true,
-            "peer": true
-        },
-        "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
-            "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+        "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
             "dev": true,
             "peer": true,
-            "engines": {
-                "node": ">=8"
+            "dependencies": {
+                "brace-expansion": "^1.1.7"
             },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
+            "engines": {
+                "node": "*"
             }
         },
         "node_modules/@eslint/js": {
-            "version": "8.53.0",
-            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz",
-            "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==",
+            "version": "8.56.0",
+            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
+            "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
             "dev": true,
             "peer": true,
             "engines": {
@@ -271,20 +261,44 @@
             }
         },
         "node_modules/@humanwhocodes/config-array": {
-            "version": "0.11.13",
-            "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
-            "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
+            "version": "0.11.14",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+            "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
             "dev": true,
             "peer": true,
             "dependencies": {
-                "@humanwhocodes/object-schema": "^2.0.1",
-                "debug": "^4.1.1",
+                "@humanwhocodes/object-schema": "^2.0.2",
+                "debug": "^4.3.1",
                 "minimatch": "^3.0.5"
             },
             "engines": {
                 "node": ">=10.10.0"
             }
         },
+        "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
+            "version": "1.1.11",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "balanced-match": "^1.0.0",
+                "concat-map": "0.0.1"
+            }
+        },
+        "node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "brace-expansion": "^1.1.7"
+            },
+            "engines": {
+                "node": "*"
+            }
+        },
         "node_modules/@humanwhocodes/module-importer": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -300,9 +314,9 @@
             }
         },
         "node_modules/@humanwhocodes/object-schema": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
-            "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
+            "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
             "dev": true,
             "peer": true
         },
@@ -345,9 +359,9 @@
             "dev": true
         },
         "node_modules/@jridgewell/trace-mapping": {
-            "version": "0.3.19",
-            "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz",
-            "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==",
+            "version": "0.3.21",
+            "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz",
+            "integrity": "sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g==",
             "dev": true,
             "dependencies": {
                 "@jridgewell/resolve-uri": "^3.1.0",
@@ -762,9 +776,9 @@
             }
         },
         "node_modules/@oclif/screen": {
-            "version": "3.0.7",
-            "resolved": "https://registry.npmjs.org/@oclif/screen/-/screen-3.0.7.tgz",
-            "integrity": "sha512-jQBPHcMh5rcIPKdqA6xlzioLOmkaVnjg2MVyjMzBKV8hDhLWNSiZqx7NAWXpP70v2LFvGdVoV8BSbK9iID3eHg==",
+            "version": "3.0.8",
+            "resolved": "https://registry.npmjs.org/@oclif/screen/-/screen-3.0.8.tgz",
+            "integrity": "sha512-yx6KAqlt3TAHBduS2fMQtJDL2ufIHnDRArrJEOoTTuizxqmjLT+psGYOHpmMl3gvQpFJ11Hs76guUUktzAF9Bg==",
             "dev": true,
             "engines": {
                 "node": ">=12.0.0"
@@ -795,9 +809,9 @@
             "dev": true
         },
         "node_modules/@types/cli-progress": {
-            "version": "3.11.3",
-            "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.3.tgz",
-            "integrity": "sha512-/+C9xAdVtc+g5yHHkGBThgAA8rYpi5B+2ve3wLtybYj0JHEBs57ivR4x/zGfSsplRnV+psE91Nfin1soNKqz5Q==",
+            "version": "3.11.5",
+            "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.5.tgz",
+            "integrity": "sha512-D4PbNRbviKyppS5ivBGyFO29POlySLmA2HyUFE4p5QGazAMM3CwkKWcvTl8gvElSuxRh6FPKL8XmidX873ou4g==",
             "dev": true,
             "dependencies": {
                 "@types/node": "*"
@@ -830,9 +844,9 @@
             "dev": true
         },
         "node_modules/@types/jsonfile": {
-            "version": "6.1.2",
-            "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.2.tgz",
-            "integrity": "sha512-8t92P+oeW4d/CRQfJaSqEwXujrhH4OEeHRjGU3v1Q8mUS8GPF3yiX26sw4svv6faL2HfBtGTe2xWIoVgN3dy9w==",
+            "version": "6.1.4",
+            "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz",
+            "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==",
             "dev": true,
             "dependencies": {
                 "@types/node": "*"
@@ -848,9 +862,9 @@
             }
         },
         "node_modules/@types/node": {
-            "version": "18.19.2",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.2.tgz",
-            "integrity": "sha512-6wzfBdbWpe8QykUkXBjtmO3zITA0A3FIjoy+in0Y2K4KrCiRhNYJIdwAPDffZ3G6GnaKaSLSEa9ZuORLfEoiwg==",
+            "version": "18.19.8",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.8.tgz",
+            "integrity": "sha512-g1pZtPhsvGVTwmeVoexWZLTQaOvXwoSq//pTL0DHeNzUDrFnir4fgETdhjhIxjVnN+hKOuh98+E1eMLnUXstFg==",
             "dev": true,
             "dependencies": {
                 "undici-types": "~5.26.4"
@@ -872,30 +886,30 @@
             }
         },
         "node_modules/@types/through": {
-            "version": "0.0.31",
-            "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.31.tgz",
-            "integrity": "sha512-LpKpmb7FGevYgXnBXYs6HWnmiFyVG07Pt1cnbgM1IhEacITTiUaBXXvOR3Y50ksaJWGSfhbEvQFivQEFGCC55w==",
+            "version": "0.0.33",
+            "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz",
+            "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==",
             "dev": true,
             "dependencies": {
                 "@types/node": "*"
             }
         },
         "node_modules/@types/triple-beam": {
-            "version": "1.3.3",
-            "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.3.tgz",
-            "integrity": "sha512-6tOUG+nVHn0cJbVp25JFayS5UE6+xlbcNF9Lo9mU7U0zk3zeUShZied4YEQZjy1JBF043FSkdXw8YkUJuVtB5g=="
+            "version": "1.3.5",
+            "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
+            "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
         },
         "node_modules/@typescript-eslint/eslint-plugin": {
-            "version": "6.13.2",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz",
-            "integrity": "sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==",
+            "version": "6.19.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.19.0.tgz",
+            "integrity": "sha512-DUCUkQNklCQYnrBSSikjVChdc84/vMPDQSgJTHBZ64G9bA9w0Crc0rd2diujKbTdp6w2J47qkeHQLoi0rpLCdg==",
             "dev": true,
             "dependencies": {
                 "@eslint-community/regexpp": "^4.5.1",
-                "@typescript-eslint/scope-manager": "6.13.2",
-                "@typescript-eslint/type-utils": "6.13.2",
-                "@typescript-eslint/utils": "6.13.2",
-                "@typescript-eslint/visitor-keys": "6.13.2",
+                "@typescript-eslint/scope-manager": "6.19.0",
+                "@typescript-eslint/type-utils": "6.19.0",
+                "@typescript-eslint/utils": "6.19.0",
+                "@typescript-eslint/visitor-keys": "6.19.0",
                 "debug": "^4.3.4",
                 "graphemer": "^1.4.0",
                 "ignore": "^5.2.4",
@@ -921,15 +935,15 @@
             }
         },
         "node_modules/@typescript-eslint/parser": {
-            "version": "6.13.2",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.2.tgz",
-            "integrity": "sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==",
+            "version": "6.19.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.19.0.tgz",
+            "integrity": "sha512-1DyBLG5SH7PYCd00QlroiW60YJ4rWMuUGa/JBV0iZuqi4l4IK3twKPq5ZkEebmGqRjXWVgsUzfd3+nZveewgow==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/scope-manager": "6.13.2",
-                "@typescript-eslint/types": "6.13.2",
-                "@typescript-eslint/typescript-estree": "6.13.2",
-                "@typescript-eslint/visitor-keys": "6.13.2",
+                "@typescript-eslint/scope-manager": "6.19.0",
+                "@typescript-eslint/types": "6.19.0",
+                "@typescript-eslint/typescript-estree": "6.19.0",
+                "@typescript-eslint/visitor-keys": "6.19.0",
                 "debug": "^4.3.4"
             },
             "engines": {
@@ -949,13 +963,13 @@
             }
         },
         "node_modules/@typescript-eslint/scope-manager": {
-            "version": "6.13.2",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz",
-            "integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==",
+            "version": "6.19.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz",
+            "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/types": "6.13.2",
-                "@typescript-eslint/visitor-keys": "6.13.2"
+                "@typescript-eslint/types": "6.19.0",
+                "@typescript-eslint/visitor-keys": "6.19.0"
             },
             "engines": {
                 "node": "^16.0.0 || >=18.0.0"
@@ -966,13 +980,13 @@
             }
         },
         "node_modules/@typescript-eslint/type-utils": {
-            "version": "6.13.2",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz",
-            "integrity": "sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==",
+            "version": "6.19.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.19.0.tgz",
+            "integrity": "sha512-mcvS6WSWbjiSxKCwBcXtOM5pRkPQ6kcDds/juxcy/727IQr3xMEcwr/YLHW2A2+Fp5ql6khjbKBzOyjuPqGi/w==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/typescript-estree": "6.13.2",
-                "@typescript-eslint/utils": "6.13.2",
+                "@typescript-eslint/typescript-estree": "6.19.0",
+                "@typescript-eslint/utils": "6.19.0",
                 "debug": "^4.3.4",
                 "ts-api-utils": "^1.0.1"
             },
@@ -993,9 +1007,9 @@
             }
         },
         "node_modules/@typescript-eslint/types": {
-            "version": "6.13.2",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz",
-            "integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==",
+            "version": "6.19.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz",
+            "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==",
             "dev": true,
             "engines": {
                 "node": "^16.0.0 || >=18.0.0"
@@ -1006,16 +1020,17 @@
             }
         },
         "node_modules/@typescript-eslint/typescript-estree": {
-            "version": "6.13.2",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz",
-            "integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==",
+            "version": "6.19.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz",
+            "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/types": "6.13.2",
-                "@typescript-eslint/visitor-keys": "6.13.2",
+                "@typescript-eslint/types": "6.19.0",
+                "@typescript-eslint/visitor-keys": "6.19.0",
                 "debug": "^4.3.4",
                 "globby": "^11.1.0",
                 "is-glob": "^4.0.3",
+                "minimatch": "9.0.3",
                 "semver": "^7.5.4",
                 "ts-api-utils": "^1.0.1"
             },
@@ -1033,17 +1048,17 @@
             }
         },
         "node_modules/@typescript-eslint/utils": {
-            "version": "6.13.2",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz",
-            "integrity": "sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==",
+            "version": "6.19.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.0.tgz",
+            "integrity": "sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==",
             "dev": true,
             "dependencies": {
                 "@eslint-community/eslint-utils": "^4.4.0",
                 "@types/json-schema": "^7.0.12",
                 "@types/semver": "^7.5.0",
-                "@typescript-eslint/scope-manager": "6.13.2",
-                "@typescript-eslint/types": "6.13.2",
-                "@typescript-eslint/typescript-estree": "6.13.2",
+                "@typescript-eslint/scope-manager": "6.19.0",
+                "@typescript-eslint/types": "6.19.0",
+                "@typescript-eslint/typescript-estree": "6.19.0",
                 "semver": "^7.5.4"
             },
             "engines": {
@@ -1058,12 +1073,12 @@
             }
         },
         "node_modules/@typescript-eslint/visitor-keys": {
-            "version": "6.13.2",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz",
-            "integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==",
+            "version": "6.19.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz",
+            "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/types": "6.13.2",
+                "@typescript-eslint/types": "6.19.0",
                 "eslint-visitor-keys": "^3.4.1"
             },
             "engines": {
@@ -1082,9 +1097,9 @@
             "peer": true
         },
         "node_modules/acorn": {
-            "version": "8.10.0",
-            "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
-            "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+            "version": "8.11.3",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+            "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
             "dev": true,
             "bin": {
                 "acorn": "bin/acorn"
@@ -1104,9 +1119,9 @@
             }
         },
         "node_modules/acorn-walk": {
-            "version": "8.2.0",
-            "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
-            "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+            "version": "8.3.2",
+            "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
+            "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
             "dev": true,
             "engines": {
                 "node": ">=0.4.0"
@@ -1125,13 +1140,15 @@
             }
         },
         "node_modules/ajv": {
-            "version": "8.12.0",
-            "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
-            "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+            "version": "6.12.6",
+            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+            "dev": true,
+            "peer": true,
             "dependencies": {
                 "fast-deep-equal": "^3.1.1",
-                "json-schema-traverse": "^1.0.0",
-                "require-from-string": "^2.0.2",
+                "fast-json-stable-stringify": "^2.0.0",
+                "json-schema-traverse": "^0.4.1",
                 "uri-js": "^4.2.2"
             },
             "funding": {
@@ -1239,9 +1256,9 @@
             }
         },
         "node_modules/async": {
-            "version": "3.2.4",
-            "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
-            "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ=="
+            "version": "3.2.5",
+            "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
+            "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
         },
         "node_modules/asynckit": {
             "version": "0.4.0",
@@ -1258,11 +1275,11 @@
             }
         },
         "node_modules/axios": {
-            "version": "1.6.2",
-            "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
-            "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
+            "version": "1.6.5",
+            "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
+            "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
             "dependencies": {
-                "follow-redirects": "^1.15.0",
+                "follow-redirects": "^1.15.4",
                 "form-data": "^4.0.0",
                 "proxy-from-env": "^1.1.0"
             }
@@ -1329,13 +1346,12 @@
             }
         },
         "node_modules/brace-expansion": {
-            "version": "1.1.11",
-            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
             "dev": true,
             "dependencies": {
-                "balanced-match": "^1.0.0",
-                "concat-map": "0.0.1"
+                "balanced-match": "^1.0.0"
             }
         },
         "node_modules/braces": {
@@ -1497,9 +1513,9 @@
             }
         },
         "node_modules/cli-spinners": {
-            "version": "2.9.1",
-            "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz",
-            "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==",
+            "version": "2.9.2",
+            "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
+            "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
             "engines": {
                 "node": ">=6"
             },
@@ -1892,16 +1908,16 @@
             }
         },
         "node_modules/eslint": {
-            "version": "8.53.0",
-            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz",
-            "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==",
+            "version": "8.56.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
+            "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
             "dev": true,
             "peer": true,
             "dependencies": {
                 "@eslint-community/eslint-utils": "^4.2.0",
                 "@eslint-community/regexpp": "^4.6.1",
-                "@eslint/eslintrc": "^2.1.3",
-                "@eslint/js": "8.53.0",
+                "@eslint/eslintrc": "^2.1.4",
+                "@eslint/js": "8.56.0",
                 "@humanwhocodes/config-array": "^0.11.13",
                 "@humanwhocodes/module-importer": "^1.0.1",
                 "@nodelib/fs.walk": "^1.2.8",
@@ -1976,23 +1992,6 @@
                 "url": "https://opencollective.com/eslint"
             }
         },
-        "node_modules/eslint/node_modules/ajv": {
-            "version": "6.12.6",
-            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
-            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
-            "dev": true,
-            "peer": true,
-            "dependencies": {
-                "fast-deep-equal": "^3.1.1",
-                "fast-json-stable-stringify": "^2.0.0",
-                "json-schema-traverse": "^0.4.1",
-                "uri-js": "^4.2.2"
-            },
-            "funding": {
-                "type": "github",
-                "url": "https://github.com/sponsors/epoberezkin"
-            }
-        },
         "node_modules/eslint/node_modules/argparse": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -2000,17 +1999,15 @@
             "dev": true,
             "peer": true
         },
-        "node_modules/eslint/node_modules/glob-parent": {
-            "version": "6.0.2",
-            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
-            "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+        "node_modules/eslint/node_modules/brace-expansion": {
+            "version": "1.1.11",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
             "dev": true,
             "peer": true,
             "dependencies": {
-                "is-glob": "^4.0.3"
-            },
-            "engines": {
-                "node": ">=10.13.0"
+                "balanced-match": "^1.0.0",
+                "concat-map": "0.0.1"
             }
         },
         "node_modules/eslint/node_modules/js-yaml": {
@@ -2026,12 +2023,18 @@
                 "js-yaml": "bin/js-yaml.js"
             }
         },
-        "node_modules/eslint/node_modules/json-schema-traverse": {
-            "version": "0.4.1",
-            "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-            "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+        "node_modules/eslint/node_modules/minimatch": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
             "dev": true,
-            "peer": true
+            "peer": true,
+            "dependencies": {
+                "brace-expansion": "^1.1.7"
+            },
+            "engines": {
+                "node": "*"
+            }
         },
         "node_modules/espree": {
             "version": "9.6.1",
@@ -2135,7 +2138,9 @@
         "node_modules/fast-deep-equal": {
             "version": "3.1.3",
             "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
-            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+            "dev": true,
+            "peer": true
         },
         "node_modules/fast-fifo": {
             "version": "1.3.2",
@@ -2143,9 +2148,9 @@
             "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="
         },
         "node_modules/fast-glob": {
-            "version": "3.3.1",
-            "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
-            "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
+            "version": "3.3.2",
+            "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+            "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
             "dev": true,
             "dependencies": {
                 "@nodelib/fs.stat": "^2.0.2",
@@ -2158,6 +2163,18 @@
                 "node": ">=8.6.0"
             }
         },
+        "node_modules/fast-glob/node_modules/glob-parent": {
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+            "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+            "dev": true,
+            "dependencies": {
+                "is-glob": "^4.0.1"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
         "node_modules/fast-json-stable-stringify": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -2184,9 +2201,9 @@
             }
         },
         "node_modules/fastq": {
-            "version": "1.15.0",
-            "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
-            "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+            "version": "1.16.0",
+            "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz",
+            "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==",
             "dev": true,
             "dependencies": {
                 "reusify": "^1.0.4"
@@ -2241,15 +2258,6 @@
                 "minimatch": "^5.0.1"
             }
         },
-        "node_modules/filelist/node_modules/brace-expansion": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
-            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
-            "dev": true,
-            "dependencies": {
-                "balanced-match": "^1.0.0"
-            }
-        },
         "node_modules/filelist/node_modules/minimatch": {
             "version": "5.1.6",
             "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
@@ -2310,9 +2318,9 @@
             }
         },
         "node_modules/flat-cache": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz",
-            "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==",
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+            "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
             "dev": true,
             "peer": true,
             "dependencies": {
@@ -2321,7 +2329,7 @@
                 "rimraf": "^3.0.2"
             },
             "engines": {
-                "node": ">=12.0.0"
+                "node": "^10.12.0 || >=12.0.0"
             }
         },
         "node_modules/flatted": {
@@ -2337,9 +2345,9 @@
             "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
         },
         "node_modules/follow-redirects": {
-            "version": "1.15.3",
-            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
-            "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
+            "version": "1.15.5",
+            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
+            "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
             "funding": [
                 {
                     "type": "individual",
@@ -2434,13 +2442,23 @@
             "dev": true,
             "peer": true
         },
+        "node_modules/function-bind": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+            "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+            "dev": true,
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
         "node_modules/genversion": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/genversion/-/genversion-3.1.1.tgz",
-            "integrity": "sha512-/H861PMsihhjgX2qqhTN8egM11V04imhA+3JRFY3jjPua2Sy1NqaqqQPjSP8rdM9jZoKpFhVj9g3Fs9XPCjBYQ==",
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/genversion/-/genversion-3.2.0.tgz",
+            "integrity": "sha512-OIYSX6XYA8PHecLDCTri30hadSZfAjZ8Iq1+BBDXqLWP4dRLuJNLoNjsSWtTpw97IccK2LDWzkEstxAB8GdN7g==",
             "dev": true,
             "dependencies": {
                 "commander": "^7.2.0",
+                "ejs": "^3.1.9",
                 "find-package": "^1.0.0"
             },
             "bin": {
@@ -2505,21 +2523,46 @@
             }
         },
         "node_modules/glob-parent": {
-            "version": "5.1.2",
-            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
-            "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+            "version": "6.0.2",
+            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+            "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
             "dev": true,
+            "peer": true,
             "dependencies": {
-                "is-glob": "^4.0.1"
+                "is-glob": "^4.0.3"
             },
             "engines": {
-                "node": ">= 6"
+                "node": ">=10.13.0"
+            }
+        },
+        "node_modules/glob/node_modules/brace-expansion": {
+            "version": "1.1.11",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "balanced-match": "^1.0.0",
+                "concat-map": "0.0.1"
+            }
+        },
+        "node_modules/glob/node_modules/minimatch": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "brace-expansion": "^1.1.7"
+            },
+            "engines": {
+                "node": "*"
             }
         },
         "node_modules/globals": {
-            "version": "13.23.0",
-            "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz",
-            "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==",
+            "version": "13.24.0",
+            "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+            "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
             "dev": true,
             "peer": true,
             "dependencies": {
@@ -2580,6 +2623,18 @@
                 "node": ">=8"
             }
         },
+        "node_modules/hasown": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
+            "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+            "dev": true,
+            "dependencies": {
+                "function-bind": "^1.1.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
         "node_modules/http-call": {
             "version": "5.3.0",
             "resolved": "https://registry.npmjs.org/http-call/-/http-call-5.3.0.tgz",
@@ -2655,9 +2710,9 @@
             ]
         },
         "node_modules/ignore": {
-            "version": "5.2.4",
-            "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
-            "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+            "version": "5.3.0",
+            "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
+            "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
             "dev": true,
             "engines": {
                 "node": ">= 4"
@@ -2935,6 +2990,28 @@
                 "node": ">=10"
             }
         },
+        "node_modules/jake/node_modules/brace-expansion": {
+            "version": "1.1.11",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+            "dev": true,
+            "dependencies": {
+                "balanced-match": "^1.0.0",
+                "concat-map": "0.0.1"
+            }
+        },
+        "node_modules/jake/node_modules/minimatch": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+            "dev": true,
+            "dependencies": {
+                "brace-expansion": "^1.1.7"
+            },
+            "engines": {
+                "node": "*"
+            }
+        },
         "node_modules/js-yaml": {
             "version": "3.14.1",
             "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
@@ -2974,9 +3051,11 @@
             "dev": true
         },
         "node_modules/json-schema-traverse": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
-            "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+            "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+            "dev": true,
+            "peer": true
         },
         "node_modules/json-stable-stringify-without-jsonify": {
             "version": "1.0.1",
@@ -3206,23 +3285,25 @@
             }
         },
         "node_modules/logform": {
-            "version": "2.5.1",
-            "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz",
-            "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==",
+            "version": "2.6.0",
+            "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz",
+            "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==",
             "dependencies": {
-                "@colors/colors": "1.5.0",
+                "@colors/colors": "1.6.0",
                 "@types/triple-beam": "^1.3.2",
                 "fecha": "^4.2.0",
                 "ms": "^2.1.1",
                 "safe-stable-stringify": "^2.3.1",
                 "triple-beam": "^1.3.0"
+            },
+            "engines": {
+                "node": ">= 12.0.0"
             }
         },
         "node_modules/lru-cache": {
             "version": "6.0.0",
             "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
             "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
-            "dev": true,
             "dependencies": {
                 "yallist": "^4.0.0"
             },
@@ -3298,15 +3379,18 @@
             }
         },
         "node_modules/minimatch": {
-            "version": "3.1.2",
-            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+            "version": "9.0.3",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+            "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
             "dev": true,
             "dependencies": {
-                "brace-expansion": "^1.1.7"
+                "brace-expansion": "^2.0.1"
             },
             "engines": {
-                "node": "*"
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
         "node_modules/minimist": {
@@ -3380,9 +3464,9 @@
             }
         },
         "node_modules/node-abi": {
-            "version": "3.47.0",
-            "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.47.0.tgz",
-            "integrity": "sha512-2s6B2CWZM//kPgwnuI0KrYwNjfdByE25zvAaEpq9IH4zcNsarH8Ihu/UuX6XMPEogDAxkuUFeZn60pXNHAqn3A==",
+            "version": "3.54.0",
+            "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.54.0.tgz",
+            "integrity": "sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA==",
             "dev": true,
             "dependencies": {
                 "semver": "^7.3.5"
@@ -3822,9 +3906,11 @@
             }
         },
         "node_modules/punycode": {
-            "version": "2.3.0",
-            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
-            "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+            "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+            "dev": true,
+            "peer": true,
             "engines": {
                 "node": ">=6"
             }
@@ -3869,6 +3955,15 @@
                 "rc": "cli.js"
             }
         },
+        "node_modules/rc/node_modules/strip-json-comments": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+            "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
         "node_modules/readable-stream": {
             "version": "3.6.2",
             "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
@@ -3900,18 +3995,10 @@
                 "node": ">=0.10.0"
             }
         },
-        "node_modules/require-from-string": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
-            "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/resolve": {
-            "version": "1.22.6",
-            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz",
-            "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==",
+            "version": "1.22.8",
+            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+            "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
             "dev": true,
             "dependencies": {
                 "is-core-module": "^2.13.0",
@@ -3936,12 +4023,12 @@
             }
         },
         "node_modules/resolve/node_modules/is-core-module": {
-            "version": "2.13.0",
-            "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
-            "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
+            "version": "2.13.1",
+            "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
+            "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
             "dev": true,
             "dependencies": {
-                "has": "^1.0.3"
+                "hasown": "^2.0.0"
             },
             "funding": {
                 "url": "https://github.com/sponsors/ljharb"
@@ -4060,7 +4147,6 @@
             "version": "7.5.4",
             "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
             "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
-            "dev": true,
             "dependencies": {
                 "lru-cache": "^6.0.0"
             },
@@ -4235,9 +4321,9 @@
             }
         },
         "node_modules/streamx": {
-            "version": "2.15.1",
-            "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.1.tgz",
-            "integrity": "sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA==",
+            "version": "2.15.6",
+            "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.6.tgz",
+            "integrity": "sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw==",
             "dependencies": {
                 "fast-fifo": "^1.1.0",
                 "queue-tick": "^1.0.1"
@@ -4276,12 +4362,16 @@
             }
         },
         "node_modules/strip-json-comments": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
-            "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+            "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
             "dev": true,
+            "peer": true,
             "engines": {
-                "node": ">=0.10.0"
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
         "node_modules/supports-color": {
@@ -4440,9 +4530,9 @@
             }
         },
         "node_modules/ts-node": {
-            "version": "10.9.1",
-            "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
-            "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+            "version": "10.9.2",
+            "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+            "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
             "dev": true,
             "dependencies": {
                 "@cspotcode/source-map-support": "^0.8.0",
@@ -4524,9 +4614,9 @@
             }
         },
         "node_modules/typescript": {
-            "version": "5.3.2",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
-            "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
+            "version": "5.3.3",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
+            "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
             "dev": true,
             "bin": {
                 "tsc": "bin/tsc",
@@ -4543,9 +4633,9 @@
             "dev": true
         },
         "node_modules/universalify": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
-            "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+            "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
             "engines": {
                 "node": ">= 10.0.0"
             }
@@ -4554,6 +4644,8 @@
             "version": "4.4.1",
             "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
             "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+            "dev": true,
+            "peer": true,
             "dependencies": {
                 "punycode": "^2.1.0"
             }
@@ -4641,24 +4733,16 @@
             }
         },
         "node_modules/winston-transport": {
-            "version": "4.5.0",
-            "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz",
-            "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==",
+            "version": "4.6.0",
+            "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.6.0.tgz",
+            "integrity": "sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==",
             "dependencies": {
                 "logform": "^2.3.2",
                 "readable-stream": "^3.6.0",
                 "triple-beam": "^1.3.0"
             },
             "engines": {
-                "node": ">= 6.4.0"
-            }
-        },
-        "node_modules/winston/node_modules/@colors/colors": {
-            "version": "1.6.0",
-            "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
-            "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
-            "engines": {
-                "node": ">=0.1.90"
+                "node": ">= 12.0.0"
             }
         },
         "node_modules/wordwrap": {
@@ -4701,8 +4785,7 @@
         "node_modules/yallist": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-            "dev": true
+            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
         },
         "node_modules/yaml": {
             "version": "2.3.4",
@@ -4760,6 +4843,25 @@
             "funding": {
                 "url": "https://github.com/sponsors/sindresorhus"
             }
+        },
+        "node_modules/zod": {
+            "version": "3.22.4",
+            "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
+            "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==",
+            "funding": {
+                "url": "https://github.com/sponsors/colinhacks"
+            }
+        },
+        "node_modules/zod-validation-error": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.0.0.tgz",
+            "integrity": "sha512-x+agsJJG9rvC7axF0xqTEdZhJkLHyIZkdOAWDJSmwGPzxNHMHwtU6w2yDOAAP6yuSfTAUhAMJRBfhVGY64ySEQ==",
+            "engines": {
+                "node": ">=18.0.0"
+            },
+            "peerDependencies": {
+                "zod": "^3.18.0"
+            }
         }
     }
 }
diff --git a/NodeApp/package.json b/NodeApp/package.json
index 3a5fc6f97ea3ab085428984de777da75e1252276..ef5129535e233a31cc24b94dcb2f862fa929902c 100644
--- a/NodeApp/package.json
+++ b/NodeApp/package.json
@@ -1,7 +1,7 @@
 {
     "name"           : "dojo_cli",
     "description"    : "CLI of the Dojo project",
-    "version"        : "3.2.3",
+    "version"        : "3.3.0",
     "license"        : "AGPLv3",
     "author"         : "Michaël Minelli <dojo@minelli.me>",
     "main"           : "dist/app.js",
@@ -33,38 +33,41 @@
         "test"        : "echo \"Error: no test specified\" && exit 1"
     },
     "dependencies"   : {
-        "ajv"              : "^8.12.0",
-        "appdata-path"     : "^1.0.0",
-        "axios"            : "^1.6.2",
-        "boxen"            : "^5.1.2",
-        "chalk"            : "^4.1.2",
-        "commander"        : "^11.1.0",
-        "dotenv"           : "^16.3.1",
-        "dotenv-expand"    : "^10.0.0",
-        "fs-extra"         : "^11.2.0",
-        "http-status-codes": "^2.3.0",
-        "inquirer"         : "^8.2.6",
-        "json5"            : "^2.2.3",
-        "jsonwebtoken"     : "^8.5.1",
-        "open"             : "^8.4.2",
-        "ora"              : "^5.4.1",
-        "tar-stream"       : "^3.1.6",
-        "winston"          : "^3.11.0",
-        "yaml"             : "^2.3.4"
+        "appdata-path"        : "^1.0.0",
+        "axios"               : "^1.6.5",
+        "boxen"               : "^5.1.2",
+        "chalk"               : "^4.1.2",
+        "commander"           : "^11.1.0",
+        "dotenv"              : "^16.3.1",
+        "dotenv-expand"       : "^10.0.0",
+        "fs-extra"            : "^11.2.0",
+        "http-status-codes"   : "^2.3.0",
+        "inquirer"            : "^8.2.6",
+        "json5"               : "^2.2.3",
+        "jsonwebtoken"        : "^8.5.1",
+        "open"                : "^8.4.2",
+        "ora"                 : "^5.4.1",
+        "semver"              : "^7.5.4",
+        "tar-stream"          : "^3.1.6",
+        "winston"             : "^3.11.0",
+        "yaml"                : "^2.3.4",
+        "zod"                 : "^3.22.4",
+        "zod-validation-error": "^3.0.0"
     },
     "devDependencies": {
         "@types/fs-extra"                 : "^11.0.4",
         "@types/inquirer"                 : "^8.2.10",
         "@types/jsonwebtoken"             : "^8.5.9",
         "@types/node"                     : "^18.19.2",
+        "@types/semver"                   : "^7.5.6",
         "@types/tar-stream"               : "^3.1.3",
-        "@typescript-eslint/eslint-plugin": "^6.13.2",
-        "@typescript-eslint/parser"       : "^6.13.2",
+        "@typescript-eslint/eslint-plugin": "^6.18.1",
+        "@typescript-eslint/parser"       : "^6.18.1",
         "dotenv-vault"                    : "^1.25.0",
-        "genversion"                      : "^3.1.1",
+        "genversion"                      : "^3.2.0",
         "pkg"                             : "^5.8.1",
         "tiny-typed-emitter"              : "^2.1.0",
-        "ts-node"                         : "^10.9.1",
-        "typescript"                      : "^5.3.2"
+        "ts-node"                         : "^10.9.2",
+        "typescript"                      : "^5.3.3"
     }
 }
diff --git a/NodeApp/src/commander/CommanderApp.ts b/NodeApp/src/commander/CommanderApp.ts
index 91ba8fe932cea2e6085cf4ff554d030589cf7771..03814c8a2394417570e34c1cde3618c7e89cfd2b 100644
--- a/NodeApp/src/commander/CommanderApp.ts
+++ b/NodeApp/src/commander/CommanderApp.ts
@@ -1,8 +1,14 @@
-import { Command }         from 'commander';
+import { Command, Option } from 'commander';
 import SessionCommand      from './session/SessionCommand';
 import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig';
 import AssignmentCommand   from './assignment/AssignmentCommand';
 import ExerciseCommand     from './exercise/ExerciseCommand';
+import SharedConfig        from '../shared/config/SharedConfig';
+import boxen               from 'boxen';
+import { stateConfigFile } from '../config/ConfigFiles';
+import semver              from 'semver/preload';
+import { version }         from '../config/Version';
+import Config              from '../config/Config';
 
 
 class CommanderApp {
@@ -19,17 +25,65 @@ class CommanderApp {
                            sortOptions      : true,
                            sortSubcommands  : true
                        })
-        .option('-H, --host <string>', 'override the Dojo API endpoint', ClientsSharedConfig.apiURL);
+        .option('-H, --host <string>', 'override the Dojo API endpoint', ClientsSharedConfig.apiURL)
+        .addOption(new Option('--debug').hideHelp())
+        .hook('preAction', () => {
+            this.warnDevelopmentVersion();
+        }).hook('postAction', () => {
+            this.informNewVersion();
+        });
 
         this.program.on('option:host', () => {
             ClientsSharedConfig.apiURL = this.program.opts().host;
         });
 
+        this.program.on('option:debug', () => {
+            SharedConfig.debug = true;
+        });
+
         this.registerCommands();
 
         this.program.parse();
     }
 
+    private warnDevelopmentVersion() {
+        if ( !SharedConfig.production ) {
+            console.log(boxen(`This is a development (unstable) version of the DojoCLI.
+If you want to use the stable version, please install the package from the following url: 
+https://gitedu.hesge.ch/dojo_project/projects/ui/dojocli/-/releases/Latest`, {
+                title         : 'Warning',
+                titleAlignment: 'center',
+                borderColor   : 'red',
+                borderStyle   : 'bold',
+                margin        : 1,
+                padding       : 1,
+                textAlignment : 'left'
+            }));
+        }
+    }
+
+    private informNewVersion() {
+        if ( SharedConfig.production ) {
+            const latestDojoCliVersion = stateConfigFile.getParam('latestDojoCliVersion') as string | null || '0.0.0';
+            const latestDojoCliVersionNotification = stateConfigFile.getParam('latestDojoCliVersionNotification') as number | null || 0;
+            if ( semver.lt(version, latestDojoCliVersion) ) {
+                if ( (new Date()).getTime() - latestDojoCliVersionNotification >= Config.versionUpdateInformationPeriodHours * 60 * 60 * 1000 ) {
+                    console.log(boxen(`The ${ latestDojoCliVersion } version of the DojoCLI is available: 
+https://gitedu.hesge.ch/dojo_project/projects/ui/dojocli/-/releases/Latest`, {
+                        title         : 'Information',
+                        titleAlignment: 'center',
+                        borderColor   : 'blue',
+                        borderStyle   : 'bold',
+                        margin        : 1,
+                        padding       : 1,
+                        textAlignment : 'left'
+                    }));
+                    stateConfigFile.setParam('latestDojoCliVersionNotification', (new Date()).getTime());
+                }
+            }
+        }
+    }
+
     private registerCommands() {
         SessionCommand.registerOnCommand(this.program);
         AssignmentCommand.registerOnCommand(this.program);
diff --git a/NodeApp/src/commander/assignment/AssignmentCommand.ts b/NodeApp/src/commander/assignment/AssignmentCommand.ts
index 70f09e2ff18e17774067e71a3d6e773dbf201f18..90ae6167deaece3339766cc3f8a7b090263d2e79 100644
--- a/NodeApp/src/commander/assignment/AssignmentCommand.ts
+++ b/NodeApp/src/commander/assignment/AssignmentCommand.ts
@@ -3,6 +3,7 @@ import AssignmentCreateCommand    from './subcommands/AssignmentCreateCommand';
 import AssignmentPublishCommand   from './subcommands/AssignmentPublishCommand';
 import AssignmentUnpublishCommand from './subcommands/AssignmentUnpublishCommand';
 import AssignmentCheckCommand     from './subcommands/AssignmentCheckCommand';
+import AssignmentRunCommand       from './subcommands/AssignmentRunCommand';
 
 
 class AssignmentCommand extends CommanderCommand {
@@ -16,6 +17,7 @@ class AssignmentCommand extends CommanderCommand {
     protected defineSubCommands() {
         AssignmentCreateCommand.registerOnCommand(this.command);
         AssignmentCheckCommand.registerOnCommand(this.command);
+        AssignmentRunCommand.registerOnCommand(this.command);
         AssignmentPublishCommand.registerOnCommand(this.command);
         AssignmentUnpublishCommand.registerOnCommand(this.command);
     }
diff --git a/NodeApp/src/commander/assignment/subcommands/AssignmentCheckCommand.ts b/NodeApp/src/commander/assignment/subcommands/AssignmentCheckCommand.ts
index 718b7b97d11a74390d2de9e53753566f8f8565b1..b818c738ad25d31d38e2c848066018c50ad991ef 100644
--- a/NodeApp/src/commander/assignment/subcommands/AssignmentCheckCommand.ts
+++ b/NodeApp/src/commander/assignment/subcommands/AssignmentCheckCommand.ts
@@ -4,6 +4,8 @@ import ora                           from 'ora';
 import chalk                         from 'chalk';
 import AssignmentValidator           from '../../../sharedByClients/helpers/Dojo/AssignmentValidator';
 import ClientsSharedAssignmentHelper from '../../../sharedByClients/helpers/Dojo/ClientsSharedAssignmentHelper';
+import { Option }                    from 'commander';
+import SharedConfig                  from '../../../shared/config/SharedConfig';
 
 
 class AssignmentCheckCommand extends CommanderCommand {
@@ -13,11 +15,15 @@ class AssignmentCheckCommand extends CommanderCommand {
         this.command
         .description('locally run a check of an assignment')
         .option('-p, --path <value>', 'assignment path', Config.folders.defaultLocalExercise)
-        .option('-v, --verbose', 'verbose mode (display docker compose logs in live)')
+        .option('-v, --verbose', 'verbose mode - display principal container output in live')
+        .addOption(new Option('-w, --super-verbose', 'verbose mode - display all docker compose logs (build included) in live').conflicts('verbose'))
+        .addOption(new Option('--verbose-ssj2').hideHelp().implies({ superVerbose: true }))
         .action(this.commandAction.bind(this));
     }
 
-    protected async commandAction(options: { path: string, verbose: boolean }): Promise<void> {
+    protected async commandAction(options: { path: string, verbose: boolean, superVerbose: boolean }): Promise<void> {
+        const verbose: boolean = options.verbose || options.superVerbose || SharedConfig.debug;
+
         const localExercisePath: string = options.path ?? Config.folders.defaultLocalExercise;
 
         const assignmentValidator = new AssignmentValidator(localExercisePath);
@@ -26,10 +32,21 @@ class AssignmentCheckCommand extends CommanderCommand {
             await new Promise<void>((resolve, reject) => {
                 let spinner: ora.Ora;
 
-                if ( options.verbose ) {
-                    assignmentValidator.events.on('logs', (log: string, _error: boolean, displayable: boolean) => {
-                        if ( displayable ) {
-                            console.log(log);
+                if ( verbose ) {
+                    let buildPhase: boolean | undefined = undefined;
+                    assignmentValidator.events.on('logs', (log: string, error: boolean, displayable: boolean, _currentStep: string, currentSubStep: string) => {
+                        for ( const line of log.split('\n') ) {
+                            if ( displayable && buildPhase == undefined && line.startsWith('#') ) {
+                                buildPhase = true;
+                            }
+
+                            if ( currentSubStep == 'COMPOSE_RUN' && buildPhase === true && line != '' && !line.startsWith('#') ) {
+                                buildPhase = false;
+                            }
+
+                            if ( SharedConfig.debug || (displayable && (options.superVerbose || buildPhase === false)) ) {
+                                console.log(line);
+                            }
                         }
                     });
                 }
@@ -44,14 +61,14 @@ class AssignmentCheckCommand extends CommanderCommand {
                                       indent: 4
                                   }).start();
 
-                    if ( options.verbose && name == 'COMPOSE_RUN' ) {
+                    if ( verbose && name == 'COMPOSE_RUN' ) {
                         spinner.info();
                     }
                 });
 
                 assignmentValidator.events.on('endSubStep', (stepName: string, message: string, error: boolean) => {
                     if ( error ) {
-                        if ( options.verbose && stepName == 'COMPOSE_RUN' ) {
+                        if ( verbose && stepName == 'COMPOSE_RUN' ) {
                             ora({
                                     text  : message,
                                     indent: 4
@@ -60,7 +77,7 @@ class AssignmentCheckCommand extends CommanderCommand {
                             spinner.fail(message);
                         }
                     } else {
-                        if ( options.verbose && stepName == 'COMPOSE_RUN' ) {
+                        if ( verbose && stepName == 'COMPOSE_RUN' ) {
                             ora({
                                     text  : message,
                                     indent: 4
diff --git a/NodeApp/src/commander/assignment/subcommands/AssignmentRunCommand.ts b/NodeApp/src/commander/assignment/subcommands/AssignmentRunCommand.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9a7489f2e9db1792682554f343ebc0b1b62fa2ee
--- /dev/null
+++ b/NodeApp/src/commander/assignment/subcommands/AssignmentRunCommand.ts
@@ -0,0 +1,27 @@
+import CommanderCommand  from '../../CommanderCommand';
+import Config            from '../../../config/Config';
+import { Option }        from 'commander';
+import ExerciseRunHelper from '../../../helpers/Dojo/ExerciseRunHelper';
+
+
+class AssignmentRunCommand extends CommanderCommand {
+    protected commandName: string = 'run';
+
+    protected defineCommand() {
+        // This command is synced with the "exercise run" command
+        this.command
+        .description('locally run the assignment as an exercise')
+        .option('-p, --path <value>', 'exercise path', Config.folders.defaultLocalExercise)
+        .option('-v, --verbose', 'verbose mode - display principal container output in live')
+        .addOption(new Option('-w, --super-verbose', 'verbose mode - display all docker compose logs (build included) in live').conflicts('verbose'))
+        .addOption(new Option('--verbose-ssj2').hideHelp().implies({ superVerbose: true }))
+        .action(this.commandAction.bind(this));
+    }
+
+    protected async commandAction(options: { path: string, verbose: boolean, superVerbose: boolean }): Promise<void> {
+        await ExerciseRunHelper.run(options);
+    }
+}
+
+
+export default new AssignmentRunCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseRunCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseRunCommand.ts
index 4c1f47d627f475ba483b3ec9dabcd9ab5b224473..c4d6b2195d767955bafb319875993b8733e77176 100644
--- a/NodeApp/src/commander/exercise/subcommands/ExerciseRunCommand.ts
+++ b/NodeApp/src/commander/exercise/subcommands/ExerciseRunCommand.ts
@@ -1,261 +1,25 @@
-import CommanderCommand                     from '../../CommanderCommand';
-import Config                               from '../../../config/Config';
-import fs                                   from 'node:fs';
-import ora                                  from 'ora';
-import util                                 from 'util';
-import { exec }                             from 'child_process';
-import chalk                                from 'chalk';
-import * as os                              from 'os';
-import path                                 from 'path';
-import ClientsSharedConfig                  from '../../../sharedByClients/config/ClientsSharedConfig';
-import AssignmentFile                       from '../../../shared/types/Dojo/AssignmentFile';
-import ExerciseDockerCompose                from '../../../sharedByClients/helpers/Dojo/ExerciseDockerCompose';
-import SharedAssignmentHelper               from '../../../shared/helpers/Dojo/SharedAssignmentHelper';
-import ExerciseCheckerError                 from '../../../shared/types/Dojo/ExerciseCheckerError';
-import ClientsSharedExerciseHelper          from '../../../sharedByClients/helpers/Dojo/ClientsSharedExerciseHelper';
-import ExerciseResultsSanitizerAndValidator from '../../../sharedByClients/helpers/Dojo/ExerciseResultsSanitizerAndValidator';
-
-
-const execAsync = util.promisify(exec);
+import CommanderCommand  from '../../CommanderCommand';
+import Config            from '../../../config/Config';
+import ExerciseRunHelper from '../../../helpers/Dojo/ExerciseRunHelper';
+import { Option }        from 'commander';
 
 
 class ExerciseRunCommand extends CommanderCommand {
     protected commandName: string = 'run';
 
-    private readonly dateISOString: string = (new Date()).toISOString().replace(/:/g, '_').replace(/\./g, '_');
-
-    private readonly folderResultsVolume: string = path.join(os.homedir(), 'DojoExecutions', `dojo_execLogs_${ this.dateISOString }`);
-    private readonly folderResultsDojo: string = path.join(this.folderResultsVolume, `Dojo/`);
-    private readonly folderResultsExercise: string = path.join(this.folderResultsVolume, `Exercise/`);
-
-    private readonly projectName: string = `${ ClientsSharedConfig.dockerCompose.projectName }`;
-
-    private readonly fileComposeLogs: string = path.join(this.folderResultsDojo, `dockerComposeLogs.txt`);
-
     protected defineCommand() {
+        // This command is synced with the "assignment run" command
         this.command
         .description('locally run an exercise')
         .option('-p, --path <value>', 'exercise path', Config.folders.defaultLocalExercise)
-        .option('-v, --verbose', 'verbose mode (display docker compose logs in live)')
+        .option('-v, --verbose', 'verbose mode - display principal container output in live')
+        .addOption(new Option('-w, --super-verbose', 'verbose mode - display all docker compose logs (build included) in live').conflicts('verbose'))
+        .addOption(new Option('--verbose-ssj2').hideHelp().implies({ superVerbose: true }))
         .action(this.commandAction.bind(this));
     }
 
-    private displayExecutionLogs() {
-        ora({
-                text  : `${ chalk.magenta('Execution logs folder:') } ${ this.folderResultsVolume }`,
-                indent: 0
-            }).start().info();
-    }
-
-    protected async commandAction(options: { path: string, verbose: boolean }): Promise<void> {
-        const localExercisePath: string = options.path ?? Config.folders.defaultLocalExercise;
-
-        let assignmentFile: AssignmentFile;
-        let exerciseDockerCompose: ExerciseDockerCompose;
-        let exerciseResultsValidation: ExerciseResultsSanitizerAndValidator;
-
-        let haveResultsVolume: boolean;
-
-        // Step 1: Check requirements (if it's an exercise folder and if Docker daemon is running)
-        {
-            console.log(chalk.cyan('Please wait while we are checking and creating dependencies...'));
-
-            // Create result temp folder
-            fs.mkdirSync(this.folderResultsVolume, { recursive: true });
-            fs.mkdirSync(this.folderResultsDojo, { recursive: true });
-            fs.mkdirSync(this.folderResultsExercise, { recursive: true });
-
-
-            ora({
-                    text  : `Checking exercise content:`,
-                    indent: 4
-                }).start().info();
-
-            // Exercise folder
-            {
-                const spinner: ora.Ora = ora({
-                                                 text  : `Checking exercise folder`,
-                                                 indent: 8
-                                             }).start();
-
-                const files = fs.readdirSync(options.path);
-                const missingFiles = Config.exercise.neededFiles.map((file: string): [ string, boolean ] => [ file, files.includes(file) ]).filter((file: [ string, boolean ]) => !file[1]);
-
-                if ( missingFiles.length > 0 ) {
-                    spinner.fail(`The exercise folder is missing the following files: ${ missingFiles.map((file: [ string, boolean ]) => file[0]).join(', ') }`);
-                    return;
-                }
-
-                spinner.succeed(`The exercise folder contains all the needed files`);
-            }
-
-            // dojo_assignment.json validity
-            {
-                const spinner: ora.Ora = ora({
-                                                 text  : `Checking ${ ClientsSharedConfig.assignment.filename } file`,
-                                                 indent: 8
-                                             }).start();
-
-                const validationResults = SharedAssignmentHelper.validateDescriptionFile(path.join(options.path, ClientsSharedConfig.assignment.filename));
-                if ( !validationResults.isValid ) {
-                    spinner.fail(`The ${ ClientsSharedConfig.assignment.filename } file is invalid: ${ JSON.stringify(validationResults.errors) }`);
-                    return;
-                } else {
-                    assignmentFile = validationResults.results!;
-                }
-
-                haveResultsVolume = assignmentFile.result.volume !== undefined;
-
-                spinner.succeed(`The ${ ClientsSharedConfig.assignment.filename } file is valid`);
-            }
-
-            // Docker daemon
-            {
-                const spinner: ora.Ora = ora({
-                                                 text  : `Checking Docker daemon`,
-                                                 indent: 4
-                                             }).start();
-
-                try {
-                    await execAsync(`docker ps`);
-                } catch ( error ) {
-                    spinner.fail(`The Docker daemon is not running`);
-                    return;
-                }
-
-                spinner.succeed(`The Docker daemon is running`);
-            }
-        }
-
-
-        // Step 2: Run docker-compose file
-        {
-            console.log(chalk.cyan('Please wait while we are running the exercise...'));
-
-            let composeFileOverride: string[] = [];
-            const composeOverridePath: string = path.join(localExercisePath, 'docker-compose-override.yml');
-            if ( haveResultsVolume ) {
-                const composeOverride = fs.readFileSync(path.join(__dirname, '../../../../assets/docker-compose-override.yml'), 'utf8').replace('{{VOLUME_NAME}}', assignmentFile.result.volume!).replace('{{MOUNT_PATH}}', this.folderResultsExercise);
-                fs.writeFileSync(composeOverridePath, composeOverride);
-
-                composeFileOverride = [ composeOverridePath ];
-            }
-
-            exerciseDockerCompose = new ExerciseDockerCompose(this.projectName, assignmentFile, localExercisePath, composeFileOverride);
-
-            try {
-                await new Promise<void>((resolve, reject) => {
-                    let spinner: ora.Ora;
-
-                    if ( options.verbose ) {
-                        exerciseDockerCompose.events.on('logs', (log: string, _error: boolean, displayable: boolean) => {
-                            if ( displayable ) {
-                                console.log(log);
-                            }
-                        });
-                    }
-
-                    exerciseDockerCompose.events.on('step', (name: string, message: string) => {
-                        spinner = ora({
-                                          text  : message,
-                                          indent: 4
-                                      }).start();
-
-                        if ( options.verbose && name == 'COMPOSE_RUN' ) {
-                            spinner.info();
-                        }
-                    });
-
-                    exerciseDockerCompose.events.on('endStep', (stepName: string, message: string, error: boolean) => {
-                        if ( error ) {
-                            if ( options.verbose && stepName == 'COMPOSE_RUN' ) {
-                                ora({
-                                        text  : message,
-                                        indent: 4
-                                    }).start().fail();
-                            } else {
-                                spinner.fail(message);
-                            }
-                        } else {
-                            if ( options.verbose && stepName == 'COMPOSE_RUN' ) {
-                                ora({
-                                        text  : message,
-                                        indent: 4
-                                    }).start().succeed();
-                            } else {
-                                spinner.succeed(message);
-                            }
-                        }
-                    });
-
-                    exerciseDockerCompose.events.on('finished', (success: boolean) => {
-                        success ? resolve() : reject();
-                    });
-
-                    exerciseDockerCompose.run(true);
-                });
-            } catch ( error ) { /* empty */ }
-
-            fs.rmSync(composeOverridePath, { force: true });
-            fs.writeFileSync(this.fileComposeLogs, exerciseDockerCompose.allLogs);
-
-            if ( !exerciseDockerCompose.success ) {
-                this.displayExecutionLogs();
-                return;
-            }
-        }
-
-
-        // Step 3: Get results
-        {
-            console.log(chalk.cyan('Please wait while we are checking the results...'));
-
-            exerciseResultsValidation = new ExerciseResultsSanitizerAndValidator(this.folderResultsDojo, this.folderResultsExercise, exerciseDockerCompose.exitCode);
-
-            try {
-                await new Promise<void>((resolve, reject) => {
-                    let spinner: ora.Ora;
-
-                    exerciseResultsValidation.events.on('step', (name: string, message: string) => {
-                        spinner = ora({
-                                          text  : message,
-                                          indent: 4
-                                      }).start();
-                    });
-
-                    exerciseResultsValidation.events.on('endStep', (stepName: string, message: string, error: boolean) => {
-                        if ( error ) {
-                            if ( stepName == 'CHECK_SIZE' ) {
-                                spinner.warn(message);
-                            } else {
-                                spinner.fail(message);
-                            }
-                        } else {
-                            spinner.succeed(message);
-                        }
-                    });
-
-                    exerciseResultsValidation.events.on('finished', (success: boolean, exitCode: number) => {
-                        success || exitCode == ExerciseCheckerError.EXERCISE_RESULTS_FOLDER_TOO_BIG ? resolve() : reject();
-                    });
-
-                    exerciseResultsValidation.run();
-                });
-            } catch ( error ) {
-                this.displayExecutionLogs();
-                return;
-            }
-        }
-
-
-        // Step 4: Display results + Volume location
-        {
-            ClientsSharedExerciseHelper.displayExecutionResults(exerciseResultsValidation.exerciseResults!, exerciseDockerCompose.exitCode, {
-                INFO   : chalk.bold,
-                SUCCESS: chalk.green,
-                FAILURE: chalk.red
-            }, `\n\n${ chalk.bold('Execution results folder') } : ${ this.folderResultsVolume }`);
-        }
+    protected async commandAction(options: { path: string, verbose: boolean, superVerbose: boolean }): Promise<void> {
+        await ExerciseRunHelper.run(options);
     }
 }
 
diff --git a/NodeApp/src/config/Config.ts b/NodeApp/src/config/Config.ts
index 15b13e064b562939785bb669f6a8ab6c52e63542..b0d2df81c57e001728eeda4166110d153c2eaf96 100644
--- a/NodeApp/src/config/Config.ts
+++ b/NodeApp/src/config/Config.ts
@@ -3,9 +3,11 @@ import getAppDataPath from 'appdata-path';
 
 class Config {
     public readonly localConfig: {
-        folder: string; sessionFile: string;
+        folder: string; sessionFile: string; stateFile: string;
     };
 
+    public readonly versionUpdateInformationPeriodHours: number;
+
     public readonly gitlab: {
         cliReleasePage: string
     };
@@ -31,9 +33,12 @@ class Config {
     constructor() {
         this.localConfig = {
             folder     : getAppDataPath('DojoCLI'),
-            sessionFile: process.env.LOCAL_CONFIG_FILE_SESSION || ''
+            sessionFile: process.env.LOCAL_CONFIG_FILE_SESSION || '',
+            stateFile  : process.env.LOCAL_CONFIG_STATE || ''
         };
 
+        this.versionUpdateInformationPeriodHours = Number(process.env.VERSION_UPDATE_INFORMATION_PERIOD_HOURS || 24);
+
         this.gitlab = {
             cliReleasePage: process.env.GITLAB_CLI_RELEASE_PAGE || ''
         };
diff --git a/NodeApp/src/config/ConfigFiles.ts b/NodeApp/src/config/ConfigFiles.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3bce1b773c92e24ee6f80f5ae2a03c3aa1ad6794
--- /dev/null
+++ b/NodeApp/src/config/ConfigFiles.ts
@@ -0,0 +1,9 @@
+import LocalConfigFile from './LocalConfigFile';
+import Config          from './Config';
+
+
+const sessionConfigFile = new LocalConfigFile(Config.localConfig.sessionFile);
+const stateConfigFile = new LocalConfigFile(Config.localConfig.stateFile);
+
+
+export { sessionConfigFile, stateConfigFile };
\ No newline at end of file
diff --git a/NodeApp/src/config/LocalConfigFile.ts b/NodeApp/src/config/LocalConfigFile.ts
index c69c89c71a67fc2ff100c28c6e8b38987c0686a2..ecc0530b3ce4144a99a10f1d9ec87fcb11ab99c3 100644
--- a/NodeApp/src/config/LocalConfigFile.ts
+++ b/NodeApp/src/config/LocalConfigFile.ts
@@ -4,7 +4,9 @@ import JSON5   from 'json5';
 
 
 class LocalConfigFile {
-    constructor(private filename: string) {}
+    constructor(private filename: string) {
+        this.loadConfig();
+    }
 
     private get configPath(): string {
         return `${ Config.localConfig.folder }/${ this.filename }`;
diff --git a/NodeApp/src/helpers/Dojo/ExerciseRunHelper.ts b/NodeApp/src/helpers/Dojo/ExerciseRunHelper.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2db4ba26242ff5fbea45c80b82bbfaff1b27dfa7
--- /dev/null
+++ b/NodeApp/src/helpers/Dojo/ExerciseRunHelper.ts
@@ -0,0 +1,267 @@
+import ora                                  from 'ora';
+import chalk                                from 'chalk';
+import Config                               from '../../config/Config';
+import AssignmentFile                       from '../../shared/types/Dojo/AssignmentFile';
+import ExerciseDockerCompose                from '../../sharedByClients/helpers/Dojo/ExerciseDockerCompose';
+import ExerciseResultsSanitizerAndValidator from '../../sharedByClients/helpers/Dojo/ExerciseResultsSanitizerAndValidator';
+import fs                                   from 'node:fs';
+import ClientsSharedConfig                  from '../../sharedByClients/config/ClientsSharedConfig';
+import SharedAssignmentHelper               from '../../shared/helpers/Dojo/SharedAssignmentHelper';
+import path                                 from 'path';
+import ExerciseCheckerError                 from '../../shared/types/Dojo/ExerciseCheckerError';
+import ClientsSharedExerciseHelper          from '../../sharedByClients/helpers/Dojo/ClientsSharedExerciseHelper';
+import os                                   from 'os';
+import util                                 from 'util';
+import { exec }                             from 'child_process';
+import SharedConfig                         from '../../shared/config/SharedConfig';
+
+
+const execAsync = util.promisify(exec);
+
+
+class ExerciseRunHelper {
+    private readonly dateISOString: string = (new Date()).toISOString().replace(/:/g, '_').replace(/\./g, '_');
+
+    private readonly folderResultsVolume: string = path.join(os.homedir(), 'DojoExecutions', `dojo_execLogs_${ this.dateISOString }`);
+    private readonly folderResultsDojo: string = path.join(this.folderResultsVolume, `Dojo/`);
+    private readonly folderResultsExercise: string = path.join(this.folderResultsVolume, `Exercise/`);
+
+    private readonly projectName: string = `${ ClientsSharedConfig.dockerCompose.projectName }`;
+
+    private readonly fileComposeLogs: string = path.join(this.folderResultsDojo, `dockerComposeLogs.txt`);
+
+    private displayExecutionLogs() {
+        ora({
+                text  : `${ chalk.magenta('Execution logs folder:') } ${ this.folderResultsVolume }`,
+                indent: 0
+            }).start().info();
+    }
+
+    async run(options: { path: string, verbose: boolean, superVerbose: boolean }): Promise<void> {
+        const verbose: boolean = options.verbose || options.superVerbose || SharedConfig.debug;
+
+        const localExercisePath: string = options.path ?? Config.folders.defaultLocalExercise;
+
+        let assignmentFile: AssignmentFile;
+        let exerciseDockerCompose: ExerciseDockerCompose;
+        let exerciseResultsValidation: ExerciseResultsSanitizerAndValidator;
+
+        let haveResultsVolume: boolean;
+
+        // Step 1: Check requirements (if it's an exercise folder and if Docker daemon is running)
+        {
+            console.log(chalk.cyan('Please wait while we are checking and creating dependencies...'));
+
+            // Create result temp folder
+            fs.mkdirSync(this.folderResultsVolume, { recursive: true });
+            fs.mkdirSync(this.folderResultsDojo, { recursive: true });
+            fs.mkdirSync(this.folderResultsExercise, { recursive: true });
+
+
+            ora({
+                    text  : `Checking exercise content:`,
+                    indent: 4
+                }).start().info();
+
+            // Exercise folder
+            {
+                const spinner: ora.Ora = ora({
+                                                 text  : `Checking exercise folder`,
+                                                 indent: 8
+                                             }).start();
+
+                const files = fs.readdirSync(options.path);
+                const missingFiles = Config.exercise.neededFiles.map((file: string): [ string, boolean ] => [ file, files.includes(file) ]).filter((file: [ string, boolean ]) => !file[1]);
+
+                if ( missingFiles.length > 0 ) {
+                    spinner.fail(`The exercise folder is missing the following files: ${ missingFiles.map((file: [ string, boolean ]) => file[0]).join(', ') }`);
+                    return;
+                }
+
+                spinner.succeed(`The exercise folder contains all the needed files`);
+            }
+
+            // dojo_assignment.json validity
+            {
+                const spinner: ora.Ora = ora({
+                                                 text  : `Checking ${ ClientsSharedConfig.assignment.filename } file`,
+                                                 indent: 8
+                                             }).start();
+
+                const validationResults = SharedAssignmentHelper.validateDescriptionFile(path.join(options.path, ClientsSharedConfig.assignment.filename));
+                if ( !validationResults.isValid ) {
+                    spinner.fail(`The ${ ClientsSharedConfig.assignment.filename } file is invalid: ${ validationResults.error }`);
+                    return;
+                } else {
+                    assignmentFile = validationResults.content!;
+                }
+
+                haveResultsVolume = assignmentFile.result.volume !== undefined;
+
+                spinner.succeed(`The ${ ClientsSharedConfig.assignment.filename } file is valid`);
+            }
+
+            // Docker daemon
+            {
+                const spinner: ora.Ora = ora({
+                                                 text  : `Checking Docker daemon`,
+                                                 indent: 4
+                                             }).start();
+
+                try {
+                    await execAsync(`docker ps`);
+                } catch ( error ) {
+                    spinner.fail(`The Docker daemon is not running`);
+                    return;
+                }
+
+                spinner.succeed(`The Docker daemon is running`);
+            }
+        }
+
+
+        // Step 2: Run docker-compose file
+        {
+            console.log(chalk.cyan('Please wait while we are running the exercise...'));
+
+            let composeFileOverride: string[] = [];
+            const composeOverridePath: string = path.join(localExercisePath, 'docker-compose-override.yml');
+            if ( haveResultsVolume ) {
+                const composeOverride = fs.readFileSync(path.join(__dirname, '../../../assets/docker-compose-override.yml'), 'utf8').replace('{{VOLUME_NAME}}', assignmentFile.result.volume!).replace('{{MOUNT_PATH}}', this.folderResultsExercise);
+                fs.writeFileSync(composeOverridePath, composeOverride);
+
+                composeFileOverride = [ composeOverridePath ];
+            }
+
+            exerciseDockerCompose = new ExerciseDockerCompose(this.projectName, assignmentFile, localExercisePath, composeFileOverride);
+
+            try {
+                await new Promise<void>((resolve, reject) => {
+                    let spinner: ora.Ora;
+
+                    if ( verbose ) {
+                        let buildPhase: boolean | undefined = undefined;
+                        exerciseDockerCompose.events.on('logs', (log: string, _error: boolean, displayable: boolean, currentStep: string) => {
+                            for ( const line of log.split('\n') ) {
+                                if ( displayable && buildPhase == undefined && line.startsWith('#') ) {
+                                    buildPhase = true;
+                                }
+
+                                if ( currentStep == 'COMPOSE_RUN' && buildPhase === true && line != '' && !line.startsWith('#') ) {
+                                    buildPhase = false;
+                                }
+
+                                if ( SharedConfig.debug || (displayable && (options.superVerbose || buildPhase === false)) ) {
+                                    console.log(line);
+                                }
+                            }
+                        });
+                    }
+
+                    exerciseDockerCompose.events.on('step', (name: string, message: string) => {
+                        spinner = ora({
+                                          text  : message,
+                                          indent: 4
+                                      }).start();
+
+                        if ( verbose && name == 'COMPOSE_RUN' ) {
+                            spinner.info();
+                        }
+                    });
+
+                    exerciseDockerCompose.events.on('endStep', (stepName: string, message: string, error: boolean) => {
+                        if ( error ) {
+                            if ( verbose && stepName == 'COMPOSE_RUN' ) {
+                                ora({
+                                        text  : message,
+                                        indent: 4
+                                    }).start().fail();
+                            } else {
+                                spinner.fail(message);
+                            }
+                        } else {
+                            if ( verbose && stepName == 'COMPOSE_RUN' ) {
+                                ora({
+                                        text  : message,
+                                        indent: 4
+                                    }).start().succeed();
+                            } else {
+                                spinner.succeed(message);
+                            }
+                        }
+                    });
+
+                    exerciseDockerCompose.events.on('finished', (success: boolean) => {
+                        success ? resolve() : reject();
+                    });
+
+                    exerciseDockerCompose.run(true);
+                });
+            } catch ( error ) { /* empty */ }
+
+            fs.rmSync(composeOverridePath, { force: true });
+            fs.writeFileSync(this.fileComposeLogs, exerciseDockerCompose.allLogs);
+
+            if ( !exerciseDockerCompose.success ) {
+                this.displayExecutionLogs();
+                return;
+            }
+        }
+
+
+        // Step 3: Get results
+        {
+            console.log(chalk.cyan('Please wait while we are checking the results...'));
+
+            exerciseResultsValidation = new ExerciseResultsSanitizerAndValidator(this.folderResultsDojo, this.folderResultsExercise, exerciseDockerCompose.exitCode);
+
+            try {
+                await new Promise<void>((resolve, reject) => {
+                    let spinner: ora.Ora;
+
+                    exerciseResultsValidation.events.on('step', (_name: string, message: string) => {
+                        spinner = ora({
+                                          text  : message,
+                                          indent: 4
+                                      }).start();
+                    });
+
+                    exerciseResultsValidation.events.on('endStep', (stepName: string, message: string, error: boolean) => {
+                        if ( error ) {
+                            if ( stepName == 'CHECK_SIZE' ) {
+                                spinner.warn(message);
+                            } else {
+                                spinner.fail(message);
+                            }
+                        } else {
+                            spinner.succeed(message);
+                        }
+                    });
+
+                    exerciseResultsValidation.events.on('finished', (success: boolean, exitCode: number) => {
+                        success || exitCode == ExerciseCheckerError.EXERCISE_RESULTS_FOLDER_TOO_BIG ? resolve() : reject();
+                    });
+
+                    exerciseResultsValidation.run();
+                });
+            } catch ( error ) {
+                this.displayExecutionLogs();
+                return;
+            }
+        }
+
+
+        // Step 4: Display results + Volume location
+        {
+            const info = chalk.magenta.bold.italic;
+            ClientsSharedExerciseHelper.displayExecutionResults(exerciseResultsValidation.exerciseResults!, exerciseDockerCompose.exitCode, {
+                INFO   : info,
+                SUCCESS: chalk.green,
+                FAILURE: chalk.red
+            }, `\n\n${ info('Execution results folder: ') }${ this.folderResultsVolume }`);
+        }
+    }
+}
+
+
+export default new ExerciseRunHelper();
diff --git a/NodeApp/src/managers/HttpManager.ts b/NodeApp/src/managers/HttpManager.ts
index a0f54387fe3c53b166aa9b8a14f8c44a0071edcd..6c75099ca1b1d5bc86f33336502b7d29cad4be93 100644
--- a/NodeApp/src/managers/HttpManager.ts
+++ b/NodeApp/src/managers/HttpManager.ts
@@ -9,6 +9,7 @@ import DojoStatusCode                             from '../shared/types/Dojo/Doj
 import boxen                                      from 'boxen';
 import Config                                     from '../config/Config';
 import SharedConfig                               from '../shared/config/SharedConfig';
+import { stateConfigFile }                        from '../config/ConfigFiles';
 
 
 class HttpManager {
@@ -68,6 +69,16 @@ class HttpManager {
                 SessionManager.apiToken = response.data.sessionToken;
             }
 
+            if ( response.headers['dojocli-latest-version'] ) {
+                const latestDojoCliVersion = response.headers['dojocli-latest-version'];
+                const storedLatestDojoCliVersion = stateConfigFile.getParam('latestDojoCliVersion') as string | null || '0.0.0';
+
+                if ( latestDojoCliVersion !== storedLatestDojoCliVersion ) {
+                    stateConfigFile.setParam('latestDojoCliVersion', latestDojoCliVersion);
+                    stateConfigFile.setParam('latestDojoCliVersionNotification', 0);
+                }
+            }
+
             return response;
         }, async (error) => {
             if ( error.response ) {
@@ -103,7 +114,7 @@ class HttpManager {
                             this.requestError('Client not recognized by the server. Please contact the administrator.');
                             break;
                         case DojoStatusCode.CLIENT_VERSION_NOT_SUPPORTED:
-                            this.requestError(`CLI version not anymore supported by the server. Please update the CLI.\nYou can download the latest stable version (latest release without "-dev" suffix) on this page:\n${ Config.gitlab.cliReleasePage }`);
+                            this.requestError(`CLI version not anymore supported by the server. Please update the CLI.\nYou can download the latest stable version on this page:\n${ Config.gitlab.cliReleasePage }`);
                             break;
                         default:
                             break;
diff --git a/NodeApp/src/managers/SessionManager.ts b/NodeApp/src/managers/SessionManager.ts
index 18f91796c6bf0075f4d5567af3ad4d16e714d924..4f1d25d39af634dec36a96f95be22fd5591011c2 100644
--- a/NodeApp/src/managers/SessionManager.ts
+++ b/NodeApp/src/managers/SessionManager.ts
@@ -1,6 +1,5 @@
 import * as jwt                  from 'jsonwebtoken';
 import User                      from '../sharedByClients/models/User';
-import LocalConfigFile           from '../config/LocalConfigFile';
 import LocalConfigKeys           from '../types/LocalConfigKeys';
 import axios, { HttpStatusCode } from 'axios';
 import HttpManager               from './HttpManager';
@@ -20,6 +19,7 @@ import SharedGitlabManager       from '../shared/managers/SharedGitlabManager';
 import GitlabManager             from './GitlabManager';
 import GitlabToken               from '../shared/types/Gitlab/GitlabToken';
 import open                      from 'open';
+import { sessionConfigFile }     from '../config/ConfigFiles';
 
 
 class LoginServer {
@@ -75,8 +75,6 @@ class LoginServer {
 
 
 class SessionManager {
-    private configFile: LocalConfigFile = new LocalConfigFile(Config.localConfig.sessionFile);
-
     public profile: User | undefined = undefined;
 
     get isLogged(): boolean {
@@ -84,7 +82,7 @@ class SessionManager {
     }
 
     get apiToken(): string {
-        const apisToken = this.configFile.getParam(LocalConfigKeys.APIS_TOKEN) as null | { [key: string]: string };
+        const apisToken = sessionConfigFile.getParam(LocalConfigKeys.APIS_TOKEN) as null | { [key: string]: string };
 
         if ( apisToken !== null && ClientsSharedConfig.apiURL in apisToken ) {
             return apisToken[ClientsSharedConfig.apiURL];
@@ -94,12 +92,12 @@ class SessionManager {
     }
 
     set apiToken(token: string) {
-        let apisToken = this.configFile.getParam(LocalConfigKeys.APIS_TOKEN) as null | { [key: string]: string };
+        let apisToken = sessionConfigFile.getParam(LocalConfigKeys.APIS_TOKEN) as null | { [key: string]: string };
         if ( apisToken === null ) {
             apisToken = {};
         }
         apisToken[ClientsSharedConfig.apiURL] = token;
-        this.configFile.setParam(LocalConfigKeys.APIS_TOKEN, apisToken);
+        sessionConfigFile.setParam(LocalConfigKeys.APIS_TOKEN, apisToken);
 
         try {
             const payload = jwt.decode(token);
@@ -113,16 +111,14 @@ class SessionManager {
     }
 
     get gitlabCredentials(): DojoGitlabCredentials {
-        return this.configFile.getParam(LocalConfigKeys.GITLAB) as DojoGitlabCredentials;
+        return sessionConfigFile.getParam(LocalConfigKeys.GITLAB) as DojoGitlabCredentials;
     }
 
     set gitlabCredentials(credentials: DojoGitlabCredentials) {
-        this.configFile.setParam(LocalConfigKeys.GITLAB, credentials);
+        sessionConfigFile.setParam(LocalConfigKeys.GITLAB, credentials);
     }
 
-    constructor() {
-        this.configFile.loadConfig();
-    }
+    constructor() { }
 
     private async getGitlabCodeFromHeadlessEnvironment(): Promise<string> {
         const indent: string = '    ';
diff --git a/NodeApp/src/shared b/NodeApp/src/shared
index 81f39cfa68150e9e5d63fd4b66d941e7723f5c66..89f3579ca9009f793742170928d808ab4c35d931 160000
--- a/NodeApp/src/shared
+++ b/NodeApp/src/shared
@@ -1 +1 @@
-Subproject commit 81f39cfa68150e9e5d63fd4b66d941e7723f5c66
+Subproject commit 89f3579ca9009f793742170928d808ab4c35d931
diff --git a/NodeApp/src/sharedByClients b/NodeApp/src/sharedByClients
index 06f4fcdc53a384d6a9c85e68b7debf10dfbe25a8..098c6d20f6ed84240c086b979b56afd598fdfea4 160000
--- a/NodeApp/src/sharedByClients
+++ b/NodeApp/src/sharedByClients
@@ -1 +1 @@
-Subproject commit 06f4fcdc53a384d6a9c85e68b7debf10dfbe25a8
+Subproject commit 098c6d20f6ed84240c086b979b56afd598fdfea4
diff --git a/NodeApp/tsconfig.json b/NodeApp/tsconfig.json
index d7ed4058faf2c72d3e112f4a0b4378e8a0ee1235..6dd8b7ef56110c1be2c0ce6d891878d406ba1acf 100644
--- a/NodeApp/tsconfig.json
+++ b/NodeApp/tsconfig.json
@@ -1,16 +1,16 @@
 {
     "compilerOptions": {
-        "rootDir": "src",
-        "outDir": "dist",
-        "strict": true,
-        "target": "es6",
-        "module": "commonjs",
-        "sourceMap": true,
-        "esModuleInterop": true,
+        "rootDir"         : "src",
+        "outDir"          : "dist",
+        "strict"          : true,
+        "target"          : "ES2022",
+        "module"          : "commonjs",
+        "sourceMap"       : true,
+        "esModuleInterop" : true,
         "moduleResolution": "node",
-        "noImplicitAny": true
+        "noImplicitAny"   : true
     },
-    "exclude": [
+    "exclude"        : [
         "node_modules"
     ]
 }
\ No newline at end of file
diff --git a/README.md b/README.md
index e9d7c4741c4e98c7dd5734cd33ecc1d31b4d41df..025b53d8ede4c01f2a1815af55a990e87633cf5b 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
-# DojoCLI
\ No newline at end of file
+# DojoCLI
+
+More informations about the DojoCLI can be found in the [wiki](https://gitedu.hesge.ch/dojo_project/projects/ui/dojocli/-/wikis/home).
\ No newline at end of file