diff --git a/.example/hydra/package-lock.json b/.example/hydra/package-lock.json index 665c9af..8d70ac8 100644 --- a/.example/hydra/package-lock.json +++ b/.example/hydra/package-lock.json @@ -10,7 +10,8 @@ "license": "ISC", "dependencies": { "commander": "^12.1.0", - "leviathan-client": "file:../../spec/client" + "inquirer": "^11.1.0", + "leviathan-client": "file:../../.spec/client" }, "devDependencies": { "@types/node": "^22.7.2", @@ -18,9 +19,22 @@ "typescript": "^5.6.2" } }, + "../../.spec/client": { + "name": "leviathan-client", + "version": "1.0.0", + "license": "Unlicense", + "dependencies": { + "axios": "^1.6.1" + }, + "devDependencies": { + "@types/node": "12.11.5 - 12.20.42", + "typescript": "^4.0 || ^5.0" + } + }, "../../spec/client": { "name": "leviathan-client", "version": "1.0.0", + "extraneous": true, "license": "Unlicense", "dependencies": { "axios": "^1.6.1" @@ -46,6 +60,213 @@ "node": ">=12" } }, + "node_modules/@inquirer/checkbox": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-3.0.1.tgz", + "integrity": "sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/confirm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-4.0.1.tgz", + "integrity": "sha512-46yL28o2NJ9doViqOy0VDcoTzng7rAb6yPQKU7VDLqkmbCaH4JqK4yk4XqlzNWy9PVC5pG1ZUXPBQv+VqnYs2w==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", + "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.5.5", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/editor": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-3.0.1.tgz", + "integrity": "sha512-VA96GPFaSOVudjKFraokEEmUQg/Lub6OXvbIEZU1SDCmBzRkHGhxoFAVaF30nyiB4m5cEbDgiI2QRacXZ2hw9Q==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/expand": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-3.0.1.tgz", + "integrity": "sha512-ToG8d6RIbnVpbdPdiN7BCxZGiHOTomOX94C2FaT5KOHupV40tKEDozp12res6cMIfRKrXLJyexAZhWVHgbALSQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.6.tgz", + "integrity": "sha512-yfZzps3Cso2UbM7WlxKwZQh2Hs6plrbjs1QnzQDZhK2DgyCo6D8AaHps9olkNcUFlcYERMqU3uJSp1gmy3s/qQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-3.0.1.tgz", + "integrity": "sha512-BDuPBmpvi8eMCxqC5iacloWqv+5tQSJlUafYWUe31ow1BVXjW2a5qe3dh4X/Z25Wp22RwvcaLCc2siHobEOfzg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/number": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-2.0.1.tgz", + "integrity": "sha512-QpR8jPhRjSmlr/mD2cw3IR8HRO7lSVOnqUvQa8scv1Lsr3xoAMMworcYW3J13z3ppjBFBD2ef1Ci6AE5Qn8goQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/password": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-3.0.1.tgz", + "integrity": "sha512-haoeEPUisD1NeE2IanLOiFr4wcTXGWrBOyAyPZi1FfLJuXOzNmxCJPgUrGYKVh+Y8hfGJenIfz5Wb/DkE9KkMQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/prompts": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-6.0.1.tgz", + "integrity": "sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A==", + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^3.0.1", + "@inquirer/confirm": "^4.0.1", + "@inquirer/editor": "^3.0.1", + "@inquirer/expand": "^3.0.1", + "@inquirer/input": "^3.0.1", + "@inquirer/number": "^2.0.1", + "@inquirer/password": "^3.0.1", + "@inquirer/rawlist": "^3.0.1", + "@inquirer/search": "^2.0.1", + "@inquirer/select": "^3.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/rawlist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-3.0.1.tgz", + "integrity": "sha512-VgRtFIwZInUzTiPLSfDXK5jLrnpkuSOh1ctfaoygKAdPqjcjKYmGh6sCY1pb0aGnCGsmhUxoqLDUAU0ud+lGXQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/search": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-2.0.1.tgz", + "integrity": "sha512-r5hBKZk3g5MkIzLVoSgE4evypGqtOannnB3PKTG9NRZxyFRKcfzrdxXXPcoJQsxJPzvdSU2Rn7pB7lw0GCmGAg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/select": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-3.0.1.tgz", + "integrity": "sha512-lUDGUxPhdWMkN/fHy1Lk7pF3nK1fh/gqeyWXmctefhxLYxlDsc7vsPBEpxrfVGDsVdyYJsiJoD4bJ1b623cV1Q==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", + "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -102,16 +323,30 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "22.7.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.2.tgz", "integrity": "sha512-866lXSrpGpgyHBZUa2m9YNWqHDjjM0aBTJlNtYaGEw4rqY/dcD7deRVTbBBAJelfA7oaGDbNftXF/TL/A6RgoA==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.19.2" } }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "license": "MIT" + }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -138,6 +373,45 @@ "node": ">=0.4.0" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -145,6 +419,39 @@ "dev": true, "license": "MIT" }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "license": "MIT" + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", @@ -171,8 +478,68 @@ "node": ">=0.3.1" } }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inquirer": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-11.1.0.tgz", + "integrity": "sha512-CmLAZT65GG/v30c+D2Fk8+ceP6pxD6RL+hIUOWAltCmeyEqWYwqu9v76q03OvjyZ3AB0C1Ala2stn1z/rMqGEw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/prompts": "^6.0.1", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "ansi-escapes": "^4.3.2", + "mute-stream": "^1.0.0", + "run-async": "^3.0.0", + "rxjs": "^7.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/leviathan-client": { - "resolved": "../../spec/client", + "resolved": "../../.spec/client", "link": true }, "node_modules/make-error": { @@ -182,6 +549,98 @@ "dev": true, "license": "ISC" }, + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/run-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -226,6 +685,24 @@ } } }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", @@ -244,7 +721,6 @@ "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, "license": "MIT" }, "node_modules/v8-compile-cache-lib": { @@ -254,6 +730,20 @@ "dev": true, "license": "MIT" }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -263,6 +753,18 @@ "engines": { "node": ">=6" } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/.example/hydra/package.json b/.example/hydra/package.json index 8d5c620..a03d31d 100644 --- a/.example/hydra/package.json +++ b/.example/hydra/package.json @@ -13,7 +13,8 @@ "license": "ISC", "dependencies": { "commander": "^12.1.0", - "leviathan-client": "file:../../spec/client" + "inquirer": "^11.1.0", + "leviathan-client": "file:../../.spec/client" }, "devDependencies": { "@types/node": "^22.7.2", diff --git a/.example/hydra/src/index.ts b/.example/hydra/src/index.ts index 9503c7e..54cbfa0 100644 --- a/.example/hydra/src/index.ts +++ b/.example/hydra/src/index.ts @@ -1,24 +1,87 @@ #!/usr/bin/env node import {Command} from 'commander'; -import {CoursesApi, DockerApi} from 'leviathan-client' +import {DockerApi} from 'leviathan-client'; +import inquirer from 'inquirer'; const program = new Command(); program .version('1.0.0') - .description('A simple TypeScript CLI application') - .option('-n, --name ', 'Your name') - .option('-g, --greeting ', 'Custom greeting', 'Hello') - .action((options) => { - const name = options.name || 'World'; - console.log(`${options.greeting}, ${name}!`); - }); + .description('A CLI to interact with the Leviathan API'); -program.parse(process.argv); +const baseUrl = "http://localhost:9221" -const courses = new DockerApi(undefined ,"http://localhost:9221"); +// const coursesApi = new CoursesApi(undefined, "http://localhost:9221"); +const dockerApi = new DockerApi(undefined, baseUrl); +const dockerEndpoints = { + "Get Container info": async () => { + const {containerId} = await inquirer.prompt([ + {type: 'input', name: 'containerId', message: 'Enter the container ID:'} + ]); -courses.dockerContainerIdDelete(94882).then(value => { - console.log(value) -}) \ No newline at end of file + const result = await dockerApi.dockerContainerIdGet(containerId as string); + console.log(`Status: ${result.status}, message: ${result.statusText}`); + }, + 'Delete Container': async () => { + const {containerId} = await inquirer.prompt([ + {type: 'input', name: 'containerId', message: 'Enter the container ID:'} + ]); + + const result = await dockerApi.dockerContainerIdDelete(parseInt(containerId)); + console.log(`Status: ${result.status}, message: ${result.statusText}`); + }, + 'Start Docker Container': async () => { + const {containerId} = await inquirer.prompt([ + {type: 'input', name: 'containerId', message: 'Enter the container ID:'} + ]); + const result = await dockerApi.dockerContainerIdStartGet(parseInt(containerId)); + console.log(`Status: ${result.status}, message: ${result.statusText}`); + }, + 'Stop Docker Container': async () => { + const {containerId} = await inquirer.prompt([ + {type: 'input', name: 'containerId', message: 'Enter the container ID:'} + ]); + const result = await dockerApi.dockerContainerIdStopGet(parseInt(containerId)); + console.log(`Status: ${result.status}, message: ${result.statusText}`); + }, + // todo + // 'Create Docker image': async () => { + // const file = fs.readFileSync('../ex-Dockerfile', 'utf8'); + // // const result = await dockerApi.dockerImagesCreatePost(file ,"test:latest"); + // // console.log(JSON.stringify(result, null, 2)); + // }, +}; + +async function main() { + while (true) { + const {action} = await inquirer.prompt([ + { + type: 'list', + name: 'action', + message: 'Choose an endpoint to call:', + choices: [...Object.keys(dockerEndpoints), 'Exit'] + } + ]); + + if (action === 'Exit') { + console.log('Goodbye!'); + break; + } + + try { + const act = action as string + // @ts-ignore + await dockerEndpoints[act](); + } catch (error) { + // @ts-ignore + console.error('An error occurred:', error.message); + } + + console.log('\n'); + } +} + +program.action(main); + +program.parse(process.argv); \ No newline at end of file diff --git a/.spec/readme.md b/.spec/readme.md index e21cbc8..fc41e51 100644 --- a/.spec/readme.md +++ b/.spec/readme.md @@ -1,12 +1,26 @@ # OpenApi Spec -This is the directory where the spec is defined and the code is generated. +This is where the spec is defined and the code is generated. # What is this We implement the api using the [spec](./leviathan.yaml), this contains the all paths and types defined in the [open API spec](https://swagger.io/specification/v3/) format. -This allows us to autogenerate the client and server code, in a typesafe manner. +This allows us to autogenerate the client and server code, in a (relatively)typesafe manner, the spec only defines the +types it is up to us to follow it. + +## Generated code usage + +The server files are automatically moved to [internal/generated-server](../internal/generated-server), can be directly +used. + +To use the client TS code install it via: + +``` +npm install 'https://gitpkg.vercel.app/makeopensource/leviathan/.spec/client?master' +``` + +This install the generated code on the ```master``` branch. ## Directory walkthrough diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/cmd/api/docker_impl.go b/cmd/api/docker_impl.go index 2638d39..5e672ac 100644 --- a/cmd/api/docker_impl.go +++ b/cmd/api/docker_impl.go @@ -10,27 +10,38 @@ type DockerAPI struct { // DockerContainerIdDelete Delete /docker/:containerId func (dk DockerAPI) DockerContainerIdDelete(c *gin.Context) { - + log.Debug().Msgf("Recived Delete container request with id: %s", c.Param("containerId")) + c.Status(200) } // DockerContainerIdGet Get /docker/:containerId func (dk DockerAPI) DockerContainerIdGet(c *gin.Context) { - + log.Debug().Msgf("Recived container info request with id: %s", c.Param("containerId")) + c.Status(200) } // DockerContainerIdStartGet Get /docker/:containerId/start func (dk DockerAPI) DockerContainerIdStartGet(c *gin.Context) { - + log.Debug().Msgf("Recived start container request with id: %s", c.Param("containerId")) + c.Status(200) } // DockerContainerIdStopGet Get /docker/:containerId/stop func (dk DockerAPI) DockerContainerIdStopGet(c *gin.Context) { - + log.Debug().Msgf("Recived stop container request with id: %s", c.Param("containerId")) + c.Status(200) } // DockerImagesCreatePost Post /docker/images/create func (dk DockerAPI) DockerImagesCreatePost(c *gin.Context) { - log.Debug().Msgf("Recived create image request") + form, err := c.MultipartForm() + if err != nil { + log.Error().Err(err).Msgf("Could not parse multipart form") + return + } + tagName := form.Value["tagName"] + + log.Debug().Any("Tagname", tagName).Msgf("Recived create image request") c.Status(200) } diff --git a/cmd/leviathan-agent/main.go b/cmd/leviathan-agent/main.go index 6761497..bfc097a 100644 --- a/cmd/leviathan-agent/main.go +++ b/cmd/leviathan-agent/main.go @@ -9,7 +9,7 @@ import ( "github.com/google/uuid" api "github.com/makeopensource/leviathan/cmd/api" "github.com/makeopensource/leviathan/internal/dockerclient" - store "github.com/makeopensource/leviathan/internal/messagestore" + store "github.com/makeopensource/leviathan/internal/message-store" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "net/http" @@ -20,82 +20,6 @@ import ( const jobQueueTopicName = "jobqueue.topic" const totalJobs = 5 -// test functions -//func main() { -// log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) -// -// client, err := dockerclient.NewLocalClient() -// if err != nil { -// log.Fatal().Msg("Failed to setup local docker client") -// } -// -// //client, err := dockerclient.NewSSHClient("r334@192.168.50.123") -// //if err != nil { -// // log.Fatal().Msg("Failed to setup docker client") -// //} -// -// log.Info().Msg("Connected to remote client") -// -// err = dockerclient.BuildImageFromDockerfile(client, ".example/ex-Dockerfile", "testimage:latest") -// if err != nil { -// log.Error().Err(err).Msg("Failed to build image") -// return -// } -// -// images, err := dockerclient.ListImages(client) -// if err != nil { -// log.Error().Msg("Failed to build image") -// return -// } -// -// for _, image := range images { -// log.Info().Msgf("Container names: %v", image.RepoTags) -// } -// -// newContainerId, err := dockerclient.CreateNewContainer( -// client, -// "92912992939", -// "testimage:latest", -// []string{"py", "/home/autolab/student.py"}, -// container.Resources{ -// Memory: 512 * 1000000, -// NanoCPUs: 2 * 1000000000, -// }, -// ) -// if err != nil { -// log.Error().Err(err).Msg("Failed to create container") -// return -// } -// -// err = dockerclient.CopyToContainer(client, newContainerId, ".example/student/test.py") -// if err != nil { -// log.Error().Err(err).Msg("Failed to copy to container") -// } -// -// err = dockerclient.StartContainer(client, newContainerId) -// if err != nil { -// log.Error().Err(err).Msg("Failed to start container") -// return -// } -// -// err = dockerclient.TailContainerLogs(context.Background(), client, newContainerId) -// if err != nil { -// log.Error().Err(err).Msg("Failed to tail logs") -// return -// } -// -// data, err := dockerclient.ListContainers(client) -// if err != nil { -// log.Error().Msg("Failed to build image") -// return -// } -// -// for _, info := range data { -// log.Info().Msgf("Container names: %v", info.Names) -// } -// -//} - func main() { log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) @@ -174,88 +98,3 @@ func handleJobStatus(w http.ResponseWriter, r *http.Request, publisher message.P return } } - -//func cleanup(c *client.Client, containerID string) { -// dockerclient.StopContainer(c, containerID) -// err := dockerclient.RemoveContainer(c, containerID, false, false) -// if err != nil { -// os.Exit(-1) -// } -// os.Exit(0) -//} - -//func main() { -// -// log.SetLevel(log.DebugLevel) -// if log.GetLevel() == log.TraceLevel { -// log.SetReportCaller(true) -// } -// // TODO: For prod ensure logs are json formatted for ingest -// // log.SetFormatter(&log.JSONFormatter{}) -// -// cli, err := client.NewEnvClient() -// // cli, err := dockerclient.NewSSHClient("yeager") -// if err != nil { -// log.Fatal("Failed to setup docker client") -// } -// -// // err = dockerclient.PullImage(cli, "ubautograding/autograding_image_2004") -// -// id, err := dockerclient.CreateNewContainer(cli, "ubautograding/autograding_image_2004") -// if err != nil { -// log.Fatal("Failed to create image") -// } -// -// err = dockerclient.CopyToContainer(cli, id, fmt.Sprintf("%s/tmp/sanitycheck/tmp/", util.UserHomeDir())) -// if err != nil { -// cleanup(cli, id) -// } -// -// err = dockerclient.StartContainer(cli, id) -// if err != nil { -// cleanup(cli, id) -// } -// -// ctx, cancel := context.WithCancel(context.Background()) -// go func() { -// err := dockerclient.TailContainerLogs(ctx, cli, id) -// if err != nil { -// log.Fatal("") -// } -// }() -// -// time.Sleep(10 * time.Second) -// cancel() -// -// cleanup(cli, id) -// dockerclient.ListContainer(cli) -//} -//list, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true}) -//if err != nil { -// return -//} -//go func() { -// logs, err := cli.ContainerLogs(context.Background(), "fe2c5534dba7d7a18cd802d2d5133cd5763f166c0464a828c6d929512744a338", types.ContainerLogsOptions{ShowStdout: true, -// ShowStderr: true, -// Follow: true, -// }) -// if err != nil { -// fmt.Println(err) -// return -// } -// defer func(logs io.ReadCloser) { -// err := logs.Close() -// if err != nil { -// panic(err) -// } -// }(logs) -// -// // Copy the log stream to stdout -// _, err = io.Copy(os.Stdout, logs) -// if err != nil && err != io.EOF { -// panic(err) -// } -//}() -//for _, container := range list { -// fmt.Print(container.Names) -//} diff --git a/internal/dockerclient/dockerclient.go b/internal/dockerclient/dockerclient.go index 7a8b884..960bb1c 100644 --- a/internal/dockerclient/dockerclient.go +++ b/internal/dockerclient/dockerclient.go @@ -161,7 +161,9 @@ func ListContainers(client *client.Client) ([]ContainerInfo, error) { func CreateNewContainer(client *client.Client, jobUuid string, image string, entryPointCmd []string, machineLimits container.Resources) (string, error) { config := &container.Config{ Image: image, - + Labels: map[string]string{ + "con": jobUuid, + }, Cmd: entryPointCmd, } hostConfig := &container.HostConfig{ diff --git a/internal/messagestore/message_store.go b/internal/message-store/message_store.go similarity index 96% rename from internal/messagestore/message_store.go rename to internal/message-store/message_store.go index 15fc578..65c22af 100644 --- a/internal/messagestore/message_store.go +++ b/internal/message-store/message_store.go @@ -1,4 +1,4 @@ -package messagestore +package message_store import ( "github.com/ThreeDotsLabs/watermill/message" diff --git a/tests/docker_test.go b/tests/docker_test.go new file mode 100644 index 0000000..5958582 --- /dev/null +++ b/tests/docker_test.go @@ -0,0 +1,95 @@ +package tests + +import ( + "context" + "github.com/docker/docker/api/types/container" + "github.com/makeopensource/leviathan/internal/dockerclient" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "os" + "testing" +) + +// TestHelloName calls greetings.Hello with a name, checking +// for a valid return value. +func TestHelloName(t *testing.T) { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) + + client, err := dockerclient.NewLocalClient() + if err != nil { + log.Fatal().Msg("Failed to setup local docker client") + } + + //client, err := dockerclient.NewSSHClient("r334@192.168.50.123") + //if err != nil { + // log.Fatal().Msg("Failed to setup docker client") + //} + + log.Info().Msg("Connected to remote client") + + err = dockerclient.BuildImageFromDockerfile(client, "../.example/ex-Dockerfile", "testimage:latest") + if err != nil { + log.Error().Err(err).Msg("Failed to build image") + return + } + + images, err := dockerclient.ListImages(client) + if err != nil { + log.Error().Msg("Failed to build image") + return + } + + for _, image := range images { + log.Info().Msgf("Container names: %v", image.RepoTags) + } + + newContainerId, err := dockerclient.CreateNewContainer( + client, + "92912992939", + "testimage:latest", + []string{"py", "/home/autolab/student.py"}, + container.Resources{ + Memory: 512 * 1000000, + NanoCPUs: 2 * 1000000000, + }, + ) + if err != nil { + log.Error().Err(err).Msg("Failed to create container") + return + } + + err = dockerclient.CopyToContainer(client, newContainerId, "../.example/student/test.py") + if err != nil { + log.Error().Err(err).Msg("Failed to copy to container") + } + + err = dockerclient.StartContainer(client, newContainerId) + if err != nil { + log.Error().Err(err).Msg("Failed to start container") + return + } + + err = dockerclient.TailContainerLogs(context.Background(), client, newContainerId) + if err != nil { + log.Error().Err(err).Msg("Failed to tail logs") + return + } + + data, err := dockerclient.ListContainers(client) + if err != nil { + log.Error().Msg("Failed to build image") + return + } + + for _, info := range data { + log.Info().Msgf("Container names: %v", info.Names) + } + + //t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want) + +} + +// TestHelloEmpty calls greetings.Hello with an empty string, +// checking for an error. +func TestHelloEmpty(t *testing.T) { +}