diff --git a/.github/workflows/validate-code.yml b/.github/workflows/validate-code.yml index 4fc31240..895ae429 100644 --- a/.github/workflows/validate-code.yml +++ b/.github/workflows/validate-code.yml @@ -19,6 +19,9 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Install libsecret runtime + run: sudo apt-get update && sudo apt-get install -y libsecret-1-0 + - name: Set up Node.js uses: actions/setup-node@v4 with: diff --git a/README.md b/README.md index b0007fd9..5009736e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,25 @@ Before installing the GenLayer CLI, ensure you have Node.js installed on your sy npm install -g genlayer ``` +### Linux Dependencies + +On some Linux distributions with minimal setups (like Debian netinst or Docker images), you may need to manually install libsecret: + +```bash +# Ubuntu/Debian +sudo apt-get install libsecret-1-0 + +# CentOS/RHEL/Fedora +sudo yum install libsecret +# or for newer versions +sudo dnf install libsecret + +# Arch Linux +sudo pacman -S libsecret +``` + +The GenLayer CLI uses the `keytar` library for secure key storage, which relies on `libsecret` on Linux systems. + ## Usage Each command includes syntax, usage information, and examples to help you effectively use the CLI for interacting with the GenLayer environment. diff --git a/esbuild.config.dev.js b/esbuild.config.dev.js index cffca383..c0b98f8a 100644 --- a/esbuild.config.dev.js +++ b/esbuild.config.dev.js @@ -11,7 +11,7 @@ export default { banner: { js: `const _importMetaUrl = new URL(import.meta.url).pathname;`, }, - external: ["commander", "dockerode", "dotenv", "ethers", "inquirer", "update-check", "ssh2", "fs-extra", "esbuild"] + external: ["commander", "dockerode", "dotenv", "ethers", "inquirer", "update-check", "ssh2", "fs-extra", "esbuild", "keytar"] }, watch: true, }; diff --git a/esbuild.config.prod.js b/esbuild.config.prod.js index 9d14dbd9..20e01180 100644 --- a/esbuild.config.prod.js +++ b/esbuild.config.prod.js @@ -11,7 +11,7 @@ export default { banner: { js: `const _importMetaUrl = new URL(import.meta.url).pathname;`, }, - external: ["commander", "dockerode", "dotenv", "ethers", "inquirer", "update-check", "ssh2", "fs-extra", "esbuild"] + external: ["commander", "dockerode", "dotenv", "ethers", "inquirer", "update-check", "ssh2", "fs-extra", "esbuild", "keytar"] }, watch: false, }; diff --git a/package-lock.json b/package-lock.json index 515a4b0d..9c3c8a4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "fs-extra": "^11.3.0", "genlayer-js": "^0.11.0", "inquirer": "^12.0.0", + "keytar": "^7.9.0", "node-fetch": "^3.0.0", "open": "^10.1.0", "ora": "^8.2.0", @@ -146,9 +147,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz", - "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -341,21 +342,21 @@ } }, "node_modules/@emnapi/core": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.4.tgz", - "integrity": "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", + "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.0.3", + "@emnapi/wasi-threads": "1.0.4", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz", - "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", "dev": true, "license": "MIT", "optional": true, @@ -364,9 +365,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.3.tgz", - "integrity": "sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz", + "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==", "dev": true, "license": "MIT", "optional": true, @@ -375,9 +376,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", - "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", "cpu": [ "ppc64" ], @@ -391,9 +392,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", - "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", "cpu": [ "arm" ], @@ -407,9 +408,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", - "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", "cpu": [ "arm64" ], @@ -423,9 +424,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", - "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", "cpu": [ "x64" ], @@ -439,9 +440,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", - "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", "cpu": [ "arm64" ], @@ -455,9 +456,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", - "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", "cpu": [ "x64" ], @@ -471,9 +472,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", - "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", "cpu": [ "arm64" ], @@ -487,9 +488,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", - "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", "cpu": [ "x64" ], @@ -503,9 +504,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", - "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", "cpu": [ "arm" ], @@ -519,9 +520,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", - "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", "cpu": [ "arm64" ], @@ -535,9 +536,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", - "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", "cpu": [ "ia32" ], @@ -551,9 +552,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", - "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", "cpu": [ "loong64" ], @@ -567,9 +568,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", - "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", "cpu": [ "mips64el" ], @@ -583,9 +584,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", - "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", "cpu": [ "ppc64" ], @@ -599,9 +600,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", - "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", "cpu": [ "riscv64" ], @@ -615,9 +616,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", - "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", "cpu": [ "s390x" ], @@ -631,9 +632,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", - "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", "cpu": [ "x64" ], @@ -647,9 +648,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", - "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", "cpu": [ "arm64" ], @@ -663,9 +664,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", - "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", "cpu": [ "x64" ], @@ -679,9 +680,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", - "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", "cpu": [ "arm64" ], @@ -695,9 +696,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", - "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", "cpu": [ "x64" ], @@ -711,9 +712,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", - "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", "cpu": [ "arm64" ], @@ -727,9 +728,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", - "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", "cpu": [ "x64" ], @@ -743,9 +744,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", - "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", "cpu": [ "arm64" ], @@ -759,9 +760,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", - "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", "cpu": [ "ia32" ], @@ -775,9 +776,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", - "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", "cpu": [ "x64" ], @@ -863,9 +864,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -929,9 +930,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -950,9 +951,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", - "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.15.1", @@ -962,18 +963,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@grpc/grpc-js": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.4.tgz", @@ -1077,14 +1066,14 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.9.tgz", - "integrity": "sha512-DBJBkzI5Wx4jFaYm221LHvAhpKYkhVS0k9plqHwaHhofGNxvYB7J3Bz8w+bFJ05zaMb0sZNHo4KdmENQFlNTuQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.0.tgz", + "integrity": "sha512-fdSw07FLJEU5vbpOPzXo5c6xmMGDzbZE2+niuDHX5N6mc6V0Ebso/q3xiHra4D73+PMsC8MJmcaZKuAAoaQsSA==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -1101,13 +1090,13 @@ } }, "node_modules/@inquirer/confirm": { - "version": "5.1.13", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.13.tgz", - "integrity": "sha512-EkCtvp67ICIVVzjsquUiVSd+V5HRGOGQfsqA4E4vMWhYnB7InUL0pa0TIWt1i+OfP16Gkds8CdIu6yGZwOM1Yw==", + "version": "5.1.14", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.14.tgz", + "integrity": "sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/type": "^3.0.7" + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" }, "engines": { "node": ">=18" @@ -1122,13 +1111,13 @@ } }, "node_modules/@inquirer/core": { - "version": "10.1.14", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.14.tgz", - "integrity": "sha512-Ma+ZpOJPewtIYl6HZHZckeX1STvDnHTCB2GVINNUlSEn2Am6LddWwfPkIGY0IUFVjUUrr/93XlBwTK6mfLjf0A==", + "version": "10.1.15", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.15.tgz", + "integrity": "sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA==", "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", @@ -1149,13 +1138,13 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.14", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.14.tgz", - "integrity": "sha512-yd2qtLl4QIIax9DTMZ1ZN2pFrrj+yL3kgIWxm34SS6uwCr0sIhsNyudUjAo5q3TqI03xx4SEBkUJqZuAInp9uA==", + "version": "4.2.15", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.15.tgz", + "integrity": "sha512-wst31XT8DnGOSS4nNJDIklGKnf+8shuauVrWzgKegWUe28zfCftcWZ2vktGdzJgcylWSS2SrDnYUb6alZcwnCQ==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", "external-editor": "^3.1.0" }, "engines": { @@ -1171,13 +1160,13 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.16.tgz", - "integrity": "sha512-oiDqafWzMtofeJyyGkb1CTPaxUkjIcSxePHHQCfif8t3HV9pHcw1Kgdw3/uGpDvaFfeTluwQtWiqzPVjAqS3zA==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.17.tgz", + "integrity": "sha512-PSqy9VmJx/VbE3CT453yOfNa+PykpKg/0SYP7odez1/NWBGuDXgPhp4AeGYYKjhLn5lUUavVS/JbeYMPdH50Mw==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1193,22 +1182,22 @@ } }, "node_modules/@inquirer/figures": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz", - "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", + "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@inquirer/input": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.0.tgz", - "integrity": "sha512-opqpHPB1NjAmDISi3uvZOTrjEEU5CWVu/HBkDby8t93+6UxYX0Z7Ps0Ltjm5sZiEbWenjubwUkivAEYQmy9xHw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.1.tgz", + "integrity": "sha512-tVC+O1rBl0lJpoUZv4xY+WGWY8V5b0zxU1XDsMsIHYregdh7bN5X5QnIONNBAl0K765FYlAfNHS2Bhn7SSOVow==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/type": "^3.0.7" + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" }, "engines": { "node": ">=18" @@ -1223,13 +1212,13 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.16.tgz", - "integrity": "sha512-kMrXAaKGavBEoBYUCgualbwA9jWUx2TjMA46ek+pEKy38+LFpL9QHlTd8PO2kWPUgI/KB+qi02o4y2rwXbzr3Q==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.17.tgz", + "integrity": "sha512-GcvGHkyIgfZgVnnimURdOueMk0CztycfC8NZTiIY9arIAkeOgt6zG57G+7vC59Jns3UX27LMkPKnKWAOF5xEYg==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/type": "^3.0.7" + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" }, "engines": { "node": ">=18" @@ -1244,13 +1233,13 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.16.tgz", - "integrity": "sha512-g8BVNBj5Zeb5/Y3cSN+hDUL7CsIFDIuVxb9EPty3lkxBaYpjL5BNRKSYOF9yOLe+JOcKFd+TSVeADQ4iSY7rbg==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.17.tgz", + "integrity": "sha512-DJolTnNeZ00E1+1TW+8614F7rOJJCM4y4BAGQ3Gq6kQIG+OJ4zr3GLjIjVVJCbKsk2jmkmv6v2kQuN/vriHdZA==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2" }, "engines": { @@ -1266,21 +1255,21 @@ } }, "node_modules/@inquirer/prompts": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.6.0.tgz", - "integrity": "sha512-jAhL7tyMxB3Gfwn4HIJ0yuJ5pvcB5maYUcouGcgd/ub79f9MqZ+aVnBtuFf+VC2GTkCBF+R+eo7Vi63w5VZlzw==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.7.1.tgz", + "integrity": "sha512-XDxPrEWeWUBy8scAXzXuFY45r/q49R0g72bUzgQXZ1DY/xEFX+ESDMkTQolcb5jRBzaNJX2W8XQl6krMNDTjaA==", "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^4.1.9", - "@inquirer/confirm": "^5.1.13", - "@inquirer/editor": "^4.2.14", - "@inquirer/expand": "^4.0.16", - "@inquirer/input": "^4.2.0", - "@inquirer/number": "^3.0.16", - "@inquirer/password": "^4.0.16", - "@inquirer/rawlist": "^4.1.4", - "@inquirer/search": "^3.0.16", - "@inquirer/select": "^4.2.4" + "@inquirer/checkbox": "^4.2.0", + "@inquirer/confirm": "^5.1.14", + "@inquirer/editor": "^4.2.15", + "@inquirer/expand": "^4.0.17", + "@inquirer/input": "^4.2.1", + "@inquirer/number": "^3.0.17", + "@inquirer/password": "^4.0.17", + "@inquirer/rawlist": "^4.1.5", + "@inquirer/search": "^3.0.17", + "@inquirer/select": "^4.3.1" }, "engines": { "node": ">=18" @@ -1295,13 +1284,13 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.4.tgz", - "integrity": "sha512-5GGvxVpXXMmfZNtvWw4IsHpR7RzqAR624xtkPd1NxxlV5M+pShMqzL4oRddRkg8rVEOK9fKdJp1jjVML2Lr7TQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.5.tgz", + "integrity": "sha512-R5qMyGJqtDdi4Ht521iAkNqyB6p2UPuZUbMifakg1sWtu24gc2Z8CJuw8rP081OckNDMgtDCuLe42Q2Kr3BolA==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1317,14 +1306,14 @@ } }, "node_modules/@inquirer/search": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.16.tgz", - "integrity": "sha512-POCmXo+j97kTGU6aeRjsPyuCpQQfKcMXdeTMw708ZMtWrj5aykZvlUxH4Qgz3+Y1L/cAVZsSpA+UgZCu2GMOMg==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.17.tgz", + "integrity": "sha512-CuBU4BAGFqRYors4TNCYzy9X3DpKtgIW4Boi0WNkm4Ei1hvY9acxKdBdyqzqBCEe4YxSdaQQsasJlFlUJNgojw==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1340,14 +1329,14 @@ } }, "node_modules/@inquirer/select": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.2.4.tgz", - "integrity": "sha512-unTppUcTjmnbl/q+h8XeQDhAqIOmwWYWNyiiP2e3orXrg6tOaa5DHXja9PChCSbChOsktyKgOieRZFnajzxoBg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.1.tgz", + "integrity": "sha512-Gfl/5sqOF5vS/LIrSndFgOh7jgoe0UXEizDqahFRkq5aJBLegZ6WjuMh/hVEJwlFQjyLq1z9fRtvUMkb7jM1LA==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -1364,9 +1353,9 @@ } }, "node_modules/@inquirer/type": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz", - "integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", + "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", "license": "MIT", "engines": { "node": ">=18" @@ -1620,9 +1609,9 @@ } }, "node_modules/@octokit/core": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.5.tgz", - "integrity": "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg==", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.6.tgz", + "integrity": "sha512-kIU8SLQkYWGp3pVKiYzA5OSaNF5EE03P/R8zEmmrG6XwOg5oBjXyQVVIauQ0dgau4zYhpZEhJrvIYt6oM+zZZA==", "dev": true, "license": "MIT", "dependencies": { @@ -1809,110 +1798,6 @@ "@octokit/openapi-types": "^25.1.0" } }, - "node_modules/@oxlint/darwin-arm64": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.6.0.tgz", - "integrity": "sha512-m3wyqBh1TOHjpr/dXeIZY7OoX+MQazb+bMHQdDtwUvefrafUx+5YHRvulYh1sZSQ449nQ3nk3qj5qj535vZRjg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@oxlint/darwin-x64": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-1.6.0.tgz", - "integrity": "sha512-75fJfF/9xNypr7cnOYoZBhfmG1yP7ex3pUOeYGakmtZRffO9z1i1quLYhjZsmaDXsAIZ3drMhenYHMmFKS3SRg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@oxlint/linux-arm64-gnu": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.6.0.tgz", - "integrity": "sha512-YhXGf0FXa72bEt4F7eTVKx5X3zWpbAOPnaA/dZ6/g8tGhw1m9IFjrabVHFjzcx3dQny4MgA59EhyElkDvpUe8A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@oxlint/linux-arm64-musl": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.6.0.tgz", - "integrity": "sha512-T3JDhx8mjGjvh5INsPZJrlKHmZsecgDYvtvussKRdkc1Nnn7WC+jH9sh5qlmYvwzvmetlPVNezAoNvmGO9vtMg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@oxlint/linux-x64-gnu": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.6.0.tgz", - "integrity": "sha512-Dx7ghtAl8aXBdqofJpi338At6lkeCtTfoinTYQXd9/TEJx+f+zCGNlQO6nJz3ydJBX48FDuOFKkNC+lUlWrd8w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@oxlint/linux-x64-musl": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-1.6.0.tgz", - "integrity": "sha512-7KvMGdWmAZtAtg6IjoEJHKxTXdAcrHnUnqfgs0JpXst7trquV2mxBeRZusQXwxpu4HCSomKMvJfsp1qKaqSFDg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@oxlint/win32-arm64": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-1.6.0.tgz", - "integrity": "sha512-iSGC9RwX+dl7o5KFr5aH7Gq3nFbkq/3Gda6mxNPMvNkWrgXdIyiINxpyD8hJu566M+QSv1wEAu934BZotFDyoQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@oxlint/win32-x64": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-1.6.0.tgz", - "integrity": "sha512-jOj3L/gfLc0IwgOTkZMiZ5c673i/hbAmidlaylT0gE6H18hln9HxPgp5GCf4E4y6mwEJlW8QC5hQi221+9otdA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@phun-ky/typeof": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@phun-ky/typeof/-/typeof-1.2.8.tgz", @@ -2052,9 +1937,9 @@ } }, "node_modules/@scure/bip32/node_modules/@noble/curves": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.2.tgz", - "integrity": "sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.4.tgz", + "integrity": "sha512-2bKONnuM53lINoDrSmK8qP8W271ms7pygDhZt4SiLOoLwBtoHqeCFi6RG42V8zd3mLHuJFhU/Bmaqo4nX0/kBw==", "license": "MIT", "dependencies": { "@noble/hashes": "1.8.0" @@ -2238,9 +2123,9 @@ } }, "node_modules/@types/node": { - "version": "22.16.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.3.tgz", - "integrity": "sha512-sr4Xz74KOUeYadexo1r8imhRtlVXcs+j3XK3TcoiYk7B1t3YRVJgtaD3cwX73NYb71pmVuMLNRhJ9XKdoDB74g==", + "version": "22.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", + "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -2295,9 +2180,9 @@ } }, "node_modules/@types/ssh2/node_modules/@types/node": { - "version": "18.19.118", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.118.tgz", - "integrity": "sha512-hIPK0hSrrcaoAu/gJMzN3QClXE4QdCdFvaenJ0JsjIbExP1JFFVH+RHcBt25c9n8bx5dkIfqKE+uw6BmBns7ug==", + "version": "18.19.120", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.120.tgz", + "integrity": "sha512-WtCGHFXnVI8WHLxDAt5TbnCM4eSE+nI0QN2NJtwzcgMhht2eNz6V9evJrk+lwC8bCY8OWV5Ym8Jz7ZEyGnKnMA==", "dev": true, "license": "MIT", "dependencies": { @@ -2329,17 +2214,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -2353,22 +2238,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -2384,14 +2269,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -2406,14 +2291,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2424,9 +2309,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -2441,14 +2326,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -2465,9 +2351,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -2479,16 +2365,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2508,16 +2394,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2532,13 +2418,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3464,23 +3350,23 @@ } }, "node_modules/c12": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/c12/-/c12-3.0.4.tgz", - "integrity": "sha512-t5FaZTYbbCtvxuZq9xxIruYydrAGsJ+8UdP0pZzMiK2xl/gNiSOy0OxhLzHUEEb0m1QXYqfzfvyIFEmz/g9lqg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", "dev": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", - "dotenv": "^16.5.0", - "exsolve": "^1.0.5", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.4.2", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", - "pkg-types": "^2.1.0", + "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { @@ -4231,6 +4117,21 @@ "devOptional": true, "license": "MIT" }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", @@ -4358,6 +4259,15 @@ "dev": true, "license": "MIT" }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -4440,9 +4350,9 @@ } }, "node_modules/dotenv": { - "version": "17.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.0.tgz", - "integrity": "sha512-Q4sgBT60gzd0BB0lSyYD3xM4YxrXA9y4uBDof1JNYGzOXrQdQ6yX+7XIAqoFOGQFOTK1D3Hts5OllpxMDZFONQ==", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", + "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -4649,9 +4559,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", - "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -4661,32 +4571,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.6", - "@esbuild/android-arm": "0.25.6", - "@esbuild/android-arm64": "0.25.6", - "@esbuild/android-x64": "0.25.6", - "@esbuild/darwin-arm64": "0.25.6", - "@esbuild/darwin-x64": "0.25.6", - "@esbuild/freebsd-arm64": "0.25.6", - "@esbuild/freebsd-x64": "0.25.6", - "@esbuild/linux-arm": "0.25.6", - "@esbuild/linux-arm64": "0.25.6", - "@esbuild/linux-ia32": "0.25.6", - "@esbuild/linux-loong64": "0.25.6", - "@esbuild/linux-mips64el": "0.25.6", - "@esbuild/linux-ppc64": "0.25.6", - "@esbuild/linux-riscv64": "0.25.6", - "@esbuild/linux-s390x": "0.25.6", - "@esbuild/linux-x64": "0.25.6", - "@esbuild/netbsd-arm64": "0.25.6", - "@esbuild/netbsd-x64": "0.25.6", - "@esbuild/openbsd-arm64": "0.25.6", - "@esbuild/openbsd-x64": "0.25.6", - "@esbuild/openharmony-arm64": "0.25.6", - "@esbuild/sunos-x64": "0.25.6", - "@esbuild/win32-arm64": "0.25.6", - "@esbuild/win32-ia32": "0.25.6", - "@esbuild/win32-x64": "0.25.6" + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" } }, "node_modules/escalade": { @@ -4733,19 +4643,19 @@ } }, "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", + "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.32.0", + "@eslint/plugin-kit": "^0.3.4", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -4793,9 +4703,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -5270,6 +5180,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/expect-type": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", @@ -5588,9 +5507,9 @@ } }, "node_modules/genlayer-js": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/genlayer-js/-/genlayer-js-0.11.1.tgz", - "integrity": "sha512-TffFbyhdgJQaCTq7C7F7nTv2AwipLu0xBTapNmnJe913dVa6rrGZy+1DS76QJn4E4nNtjOcnECKq9CSe0CTWsw==", + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/genlayer-js/-/genlayer-js-0.11.2.tgz", + "integrity": "sha512-t6zNdHg8EaBhTtpVfbGzR/hgR37QDj2Qwt6aAhvlwbY1YczVB3ooVID7WmSJu0zXLFHyFf/s+Drb/qMj70/3eQ==", "license": "MIT", "dependencies": { "eslint-plugin-import": "^2.30.0", @@ -5797,6 +5716,12 @@ "git-up": "^8.1.0" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -6156,17 +6081,17 @@ "license": "ISC" }, "node_modules/inquirer": { - "version": "12.7.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.7.0.tgz", - "integrity": "sha512-KKFRc++IONSyE2UYw9CJ1V0IWx5yQKomwB+pp3cWomWs+v2+ZsG11G2OVfAjFS6WWCppKw+RfKmpqGfSzD5QBQ==", + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.8.2.tgz", + "integrity": "sha512-oBDL9f4+cDambZVJdfJu2M5JQfvaug9lbo6fKDlFV40i8t3FGA1Db67ov5Hp5DInG4zmXhHWTSnlXBntnJ7GMA==", "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/prompts": "^7.6.0", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/prompts": "^7.7.1", + "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "mute-stream": "^2.0.0", - "run-async": "^4.0.4", + "run-async": "^4.0.5", "rxjs": "^7.8.2" }, "engines": { @@ -6822,9 +6747,9 @@ } }, "node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", "devOptional": true, "license": "MIT", "bin": { @@ -6960,6 +6885,17 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -7024,14 +6960,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -7094,9 +7022,9 @@ "license": "Apache-2.0" }, "node_modules/loupe": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", - "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", + "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", "license": "MIT" }, "node_modules/lru-cache": { @@ -7264,6 +7192,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -7345,10 +7285,16 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, "node_modules/napi-postinstall": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.0.tgz", - "integrity": "sha512-M7NqKyhODKV1gRLdkwE7pDsZP2/SC2a2vHkOYh9MCpKMbWVfyVfUw5MaH83Fv6XMjxr5jryUp3IDDL9rlxsTeA==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.2.tgz", + "integrity": "sha512-tWVJxJHmBWLy69PvO96TZMZDrzmw5KeiZBz3RHmiM2XZ9grBJ2WgMAFVVg25nqp3ZjTFUs2Ftw1JhscL3Teliw==", "dev": true, "license": "MIT", "bin": { @@ -7413,6 +7359,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/node-abi": { + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "license": "MIT" + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -7503,9 +7467,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", - "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", + "version": "2.2.21", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.21.tgz", + "integrity": "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==", "devOptional": true, "license": "MIT" }, @@ -7652,15 +7616,15 @@ } }, "node_modules/open": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz", - "integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", "license": "MIT", "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", - "is-wsl": "^3.1.0" + "wsl-utils": "^0.1.0" }, "engines": { "node": ">=18" @@ -7789,9 +7753,9 @@ "license": "MIT" }, "node_modules/ox/node_modules/@noble/curves": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.2.tgz", - "integrity": "sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.4.tgz", + "integrity": "sha512-2bKONnuM53lINoDrSmK8qP8W271ms7pygDhZt4SiLOoLwBtoHqeCFi6RG42V8zd3mLHuJFhU/Bmaqo4nX0/kBw==", "license": "MIT", "dependencies": { "@noble/hashes": "1.8.0" @@ -7815,32 +7779,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/oxlint": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.6.0.tgz", - "integrity": "sha512-jtaD65PqzIa1udvSxxscTKBxYKuZoFXyKGLiU1Qjo1ulq3uv/fQDtoV1yey1FrQZrQjACGPi1Widsy1TucC7Jg==", - "license": "MIT", - "bin": { - "oxc_language_server": "bin/oxc_language_server", - "oxlint": "bin/oxlint" - }, - "engines": { - "node": ">=8.*" - }, - "funding": { - "url": "https://github.com/sponsors/Boshen" - }, - "optionalDependencies": { - "@oxlint/darwin-arm64": "1.6.0", - "@oxlint/darwin-x64": "1.6.0", - "@oxlint/linux-arm64-gnu": "1.6.0", - "@oxlint/linux-arm64-musl": "1.6.0", - "@oxlint/linux-x64-gnu": "1.6.0", - "@oxlint/linux-x64-musl": "1.6.0", - "@oxlint/win32-arm64": "1.6.0", - "@oxlint/win32-x64": "1.6.0" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -8123,6 +8061,32 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -8136,6 +8100,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" @@ -8447,9 +8412,9 @@ } }, "node_modules/release-it": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/release-it/-/release-it-19.0.3.tgz", - "integrity": "sha512-lEXp7w9BZZ4r51toFtE3KnR67doEsyRSUzSONW1mMvinMNjBjKKySEBQxPcSQK9nKV1cpwHI0ONhr66M/gSYIw==", + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-19.0.4.tgz", + "integrity": "sha512-W9A26FW+l1wy5fDg9BeAknZ19wV+UvHUDOC4D355yIOZF/nHBOIhjDwutKd4pikkjvL7CpKeF+4zLxVP9kmVEw==", "dev": true, "funding": [ { @@ -8467,23 +8432,22 @@ "@octokit/rest": "21.1.1", "@phun-ky/typeof": "1.2.8", "async-retry": "1.3.3", - "c12": "3.0.4", - "ci-info": "^4.2.0", + "c12": "3.1.0", + "ci-info": "^4.3.0", "eta": "3.5.0", "git-url-parse": "16.1.0", - "inquirer": "12.6.3", + "inquirer": "12.7.0", "issue-parser": "7.0.1", - "lodash.get": "4.4.2", "lodash.merge": "4.6.2", "mime-types": "3.0.1", "new-github-release-url": "2.0.0", - "open": "10.1.2", + "open": "10.2.0", "ora": "8.2.0", "os-name": "6.1.0", "proxy-agent": "6.5.0", "semver": "7.7.2", "tinyglobby": "0.2.14", - "undici": "6.21.2", + "undici": "6.21.3", "url-join": "5.0.0", "wildcard-match": "5.1.4", "yargs-parser": "21.1.1" @@ -8496,18 +8460,18 @@ } }, "node_modules/release-it/node_modules/inquirer": { - "version": "12.6.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.6.3.tgz", - "integrity": "sha512-eX9beYAjr1MqYsIjx1vAheXsRk1jbZRvHLcBu5nA9wX0rXR1IfCZLnVLp4Ym4mrhqmh7AuANwcdtgQ291fZDfQ==", + "version": "12.7.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.7.0.tgz", + "integrity": "sha512-KKFRc++IONSyE2UYw9CJ1V0IWx5yQKomwB+pp3cWomWs+v2+ZsG11G2OVfAjFS6WWCppKw+RfKmpqGfSzD5QBQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/prompts": "^7.5.3", + "@inquirer/core": "^10.1.14", + "@inquirer/prompts": "^7.6.0", "@inquirer/type": "^3.0.7", "ansi-escapes": "^4.3.2", "mute-stream": "^2.0.0", - "run-async": "^3.0.0", + "run-async": "^4.0.4", "rxjs": "^7.8.2" }, "engines": { @@ -8522,16 +8486,6 @@ } } }, - "node_modules/release-it/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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -8619,9 +8573,9 @@ }, "node_modules/rollup": { "name": "@rollup/wasm-node", - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/wasm-node/-/wasm-node-4.44.2.tgz", - "integrity": "sha512-bKl9rKDle9BbO/Tb5RFU4PDYtRcxNdllUL2cC6vixXkxV0U5XOIoi11KUNVoKyB2RLRSk3BHouMfw9fJdMsJkg==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/wasm-node/-/wasm-node-4.46.1.tgz", + "integrity": "sha512-3vOhhqH3c3YJoIvyHsAOqdnH8KOl+LhvVjyfzd/BI1RvhnfA3RMRCporxlQCriUtEoA1JzCok1UWDrMqj5nejQ==", "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -8657,14 +8611,10 @@ } }, "node_modules/run-async": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.4.tgz", - "integrity": "sha512-2cgeRHnV11lSXBEhq7sN7a5UVjTKm9JTb9x8ApIT//16D7QL96AgnNeWSGoB4gIHc0iYw/Ha0Z+waBaCYZVNhg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.5.tgz", + "integrity": "sha512-oN9GTgxUNDBumHTTDmQ8dep6VIJbgj9S3dPP+9XylVLIK4xB9XTXtKWROd5pnhdXR9k0EgO1JRcNh0T+Ny2FsA==", "license": "MIT", - "dependencies": { - "oxlint": "^1.2.0", - "prettier": "^3.5.3" - }, "engines": { "node": ">=0.12.0" } @@ -8797,7 +8747,6 @@ "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -8963,6 +8912,51 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -8975,9 +8969,9 @@ } }, "node_modules/socks": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz", - "integrity": "sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==", + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz", + "integrity": "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==", "dev": true, "license": "MIT", "dependencies": { @@ -9466,9 +9460,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" @@ -9650,6 +9644,18 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", "license": "0BSD" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -9814,9 +9820,9 @@ } }, "node_modules/undici": { - "version": "6.21.2", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.2.tgz", - "integrity": "sha512-uROZWze0R0itiAKVPsYhFov9LxrPMHLMEQFszeI2gCN6bnIIZ8twzBCJcN2LJrBBLfrP0t1FW0g+JmKVl8Vk1g==", + "version": "6.21.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", + "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", "dev": true, "license": "MIT", "engines": { @@ -9960,9 +9966,9 @@ } }, "node_modules/viem": { - "version": "2.31.7", - "resolved": "https://registry.npmjs.org/viem/-/viem-2.31.7.tgz", - "integrity": "sha512-mpB8Hp6xK77E/b/yJmpAIQcxcOfpbrwWNItjnXaIA8lxZYt4JS433Pge2gg6Hp3PwyFtaUMh01j5L8EXnLTjQQ==", + "version": "2.33.1", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.33.1.tgz", + "integrity": "sha512-++Dkj8HvSOLPMKEs+ZBNNcWbBRlUHcXNWktjIU22hgr6YmbUldV1sPTGLZa6BYRm06WViMjXj6HIsHt8rD+ZKQ==", "funding": [ { "type": "github", @@ -10148,9 +10154,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" @@ -10232,9 +10238,9 @@ } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" @@ -10626,6 +10632,21 @@ } } }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", diff --git a/package.json b/package.json index 2b1e1366..8b22735f 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "fs-extra": "^11.3.0", "genlayer-js": "^0.11.0", "inquirer": "^12.0.0", + "keytar": "^7.9.0", "node-fetch": "^3.0.0", "open": "^10.1.0", "ora": "^8.2.0", diff --git a/src/commands/keygen/index.ts b/src/commands/keygen/index.ts index 1803f55b..82a185c4 100644 --- a/src/commands/keygen/index.ts +++ b/src/commands/keygen/index.ts @@ -1,5 +1,7 @@ import { Command } from "commander"; import { CreateKeypairOptions, KeypairCreator } from "./create"; +import { UnlockAction } from "./unlock"; +import { LockAction } from "./lock"; export function initializeKeygenCommands(program: Command) { @@ -17,5 +19,21 @@ export function initializeKeygenCommands(program: Command) { await keypairCreator.createKeypairAction(options); }); + keygenCommand + .command("unlock") + .description("Unlock your wallet by storing the decrypted private key in OS keychain") + .action(async () => { + const unlockAction = new UnlockAction(); + await unlockAction.execute(); + }); + + keygenCommand + .command("lock") + .description("Lock your wallet by removing the decrypted private key from OS keychain") + .action(async () => { + const lockAction = new LockAction(); + await lockAction.execute(); + }); + return program; } diff --git a/src/commands/keygen/lock.ts b/src/commands/keygen/lock.ts new file mode 100644 index 00000000..6e5d8b77 --- /dev/null +++ b/src/commands/keygen/lock.ts @@ -0,0 +1,31 @@ +import { BaseAction } from "../../lib/actions/BaseAction"; + +export class LockAction extends BaseAction { + async execute(): Promise { + this.startSpinner("Checking keychain availability..."); + + const keychainAvailable = await this.keychainManager.isKeychainAvailable(); + if (!keychainAvailable) { + this.failSpinner("OS keychain is not available. This command requires a supported keychain (e.g. macOS Keychain, Windows Credential Manager, or GNOME Keyring)."); + return; + } + + this.setSpinnerText("Checking for cached private key..."); + + const hasCachedKey = await this.keychainManager.getPrivateKey(); + if (!hasCachedKey) { + this.succeedSpinner("Wallet is already locked (no cached key found in OS keychain)."); + return; + } + + this.setSpinnerText("Removing private key from OS keychain..."); + + try { + await this.keychainManager.removePrivateKey(); + + this.succeedSpinner("Wallet locked successfully! Your private key has been removed from the OS keychain."); + } catch (error) { + this.failSpinner("Failed to lock wallet.", error); + } + } +} \ No newline at end of file diff --git a/src/commands/keygen/unlock.ts b/src/commands/keygen/unlock.ts new file mode 100644 index 00000000..7754cb39 --- /dev/null +++ b/src/commands/keygen/unlock.ts @@ -0,0 +1,41 @@ +import { BaseAction } from "../../lib/actions/BaseAction"; +import { readFileSync, existsSync } from "fs"; +import { ethers } from "ethers"; + +export class UnlockAction extends BaseAction { + async execute(): Promise { + this.startSpinner("Checking keychain availability..."); + + const keychainAvailable = await this.keychainManager.isKeychainAvailable(); + if (!keychainAvailable) { + this.failSpinner("OS keychain is not available. This command requires a supported keychain (e.g. macOS Keychain, Windows Credential Manager, or GNOME Keyring)."); + return; + } + + this.setSpinnerText("Checking for existing keystore..."); + + const keypairPath = this.getConfigByKey("keyPairPath"); + if (!keypairPath || !existsSync(keypairPath)) { + this.failSpinner("No keystore file found. Please create a keypair first using 'genlayer keygen create'."); + return; + } + + const keystoreData = JSON.parse(readFileSync(keypairPath, "utf-8")); + if (!this.isValidKeystoreFormat(keystoreData)) { + this.failSpinner("Invalid keystore format. Expected encrypted keystore file."); + return; + } + + this.stopSpinner(); + + try { + const password = await this.promptPassword("Enter password to decrypt keystore:"); + const wallet = await ethers.Wallet.fromEncryptedJson(keystoreData.encrypted, password); + + await this.keychainManager.storePrivateKey(wallet.privateKey); + this.succeedSpinner("Wallet unlocked successfully! Your private key is now stored securely in the OS keychain."); + } catch (error) { + this.failSpinner("Failed to unlock wallet.", error); + } + } +} \ No newline at end of file diff --git a/src/lib/actions/BaseAction.ts b/src/lib/actions/BaseAction.ts index 39b5ad74..af603681 100644 --- a/src/lib/actions/BaseAction.ts +++ b/src/lib/actions/BaseAction.ts @@ -1,4 +1,5 @@ import {ConfigFileManager} from "../../lib/config/ConfigFileManager"; +import {KeychainManager} from "../../lib/config/KeychainManager"; import ora, {Ora} from "ora"; import chalk from "chalk"; import inquirer from "inquirer"; @@ -14,15 +15,15 @@ export class BaseAction extends ConfigFileManager { private static readonly DEFAULT_KEYSTORE_PATH = "./keypair.json"; private static readonly MAX_PASSWORD_ATTEMPTS = 3; private static readonly MIN_PASSWORD_LENGTH = 8; - private static readonly TEMP_KEY_FILENAME = "decrypted_private_key"; private spinner: Ora; private _genlayerClient: GenLayerClient | null = null; + protected keychainManager: KeychainManager; constructor() { super(); this.spinner = ora({text: "", spinner: "dots"}); - this.cleanupExpiredTempFiles(); + this.keychainManager = new KeychainManager(); } private async decryptKeystore(keystoreData: KeystoreData, attempt: number = 1): Promise { @@ -33,8 +34,6 @@ export class BaseAction extends ConfigFileManager { const password = await this.promptPassword(message); const wallet = await ethers.Wallet.fromEncryptedJson(keystoreData.encrypted, password); - this.storeTempFile(BaseAction.TEMP_KEY_FILENAME, wallet.privateKey); - return wallet.privateKey; } catch (error) { if (attempt >= BaseAction.MAX_PASSWORD_ATTEMPTS) { @@ -45,7 +44,7 @@ export class BaseAction extends ConfigFileManager { } } - private isValidKeystoreFormat(data: any): data is KeystoreData { + protected isValidKeystoreFormat(data: any): data is KeystoreData { return Boolean( data && data.version === 1 && @@ -101,7 +100,7 @@ export class BaseAction extends ConfigFileManager { } if (!decryptedPrivateKey) { - const cachedKey = this.getTempFile(BaseAction.TEMP_KEY_FILENAME); + const cachedKey = await this.keychainManager.getPrivateKey(); decryptedPrivateKey = cachedKey ? cachedKey : await this.decryptKeystore(keystoreData); } return createAccount(decryptedPrivateKey as Hash); @@ -146,7 +145,7 @@ export class BaseAction extends ConfigFileManager { writeFileSync(finalOutputPath, JSON.stringify(keystoreData, null, 2)); this.writeConfig('keyPairPath', finalOutputPath); - this.clearTempFile(BaseAction.TEMP_KEY_FILENAME); + await this.keychainManager.removePrivateKey(); return wallet.privateKey; } diff --git a/src/lib/config/ConfigFileManager.ts b/src/lib/config/ConfigFileManager.ts index 8ab0ca4b..7227275d 100644 --- a/src/lib/config/ConfigFileManager.ts +++ b/src/lib/config/ConfigFileManager.ts @@ -2,24 +2,15 @@ import path from "path"; import os from "os"; import fs from "fs"; -interface TempFileData { - content: string; - timestamp: number; -} - export class ConfigFileManager { private folderPath: string; private configFilePath: string; - private tempFolderPath: string; - private static readonly TEMP_FILE_EXPIRATION_MS = 5 * 60 * 1000; // 5 minutes constructor(baseFolder: string = ".genlayer/", configFileName: string = "genlayer-config.json") { this.folderPath = path.resolve(os.homedir(), baseFolder); this.configFilePath = path.resolve(this.folderPath, configFileName); - this.tempFolderPath = path.resolve(os.tmpdir(), "genlayer-temp"); this.ensureFolderExists(); this.ensureConfigFileExists(); - this.ensureTempFolderExists(); } private ensureFolderExists(): void { @@ -34,12 +25,6 @@ export class ConfigFileManager { } } - private ensureTempFolderExists(): void { - if (!fs.existsSync(this.tempFolderPath)) { - fs.mkdirSync(this.tempFolderPath, { recursive: true, mode: 0o700 }); // Owner-only access - } - } - getFolderPath(): string { return this.folderPath; } @@ -63,67 +48,4 @@ export class ConfigFileManager { config[key] = value; fs.writeFileSync(this.configFilePath, JSON.stringify(config, null, 2)); } - - storeTempFile(fileName: string, content: string): void { - this.ensureTempFolderExists(); - const filePath = path.resolve(this.tempFolderPath, fileName); - const tempData: TempFileData = { - content, - timestamp: Date.now() - }; - fs.writeFileSync(filePath, JSON.stringify(tempData), { mode: 0o600 }); // Owner-only access - } - - getTempFile(fileName: string): string | null { - const filePath = path.resolve(this.tempFolderPath, fileName); - - if (!fs.existsSync(filePath)) { - return null; - } - - const fileContent = fs.readFileSync(filePath, "utf-8"); - const tempData: TempFileData = JSON.parse(fileContent); - - if (Date.now() - tempData.timestamp > ConfigFileManager.TEMP_FILE_EXPIRATION_MS) { - this.clearTempFile(fileName); - return null; - } - - return tempData.content; - } - - hasTempFile(fileName: string): boolean { - return this.getTempFile(fileName) !== null; - } - - clearTempFile(fileName: string): void { - const filePath = path.resolve(this.tempFolderPath, fileName); - if (fs.existsSync(filePath)) { - fs.unlinkSync(filePath); - } - } - - cleanupExpiredTempFiles(): void { - if (!fs.existsSync(this.tempFolderPath)) { - return; - } - - const files = fs.readdirSync(this.tempFolderPath); - const now = Date.now(); - - for (const file of files) { - const filePath = path.resolve(this.tempFolderPath, file); - - try { - const fileContent = fs.readFileSync(filePath, "utf-8"); - const tempData: TempFileData = JSON.parse(fileContent); - - if (now - tempData.timestamp > ConfigFileManager.TEMP_FILE_EXPIRATION_MS) { - fs.unlinkSync(filePath); - } - } catch (error) { - fs.unlinkSync(filePath); - } - } - } } diff --git a/src/lib/config/KeychainManager.ts b/src/lib/config/KeychainManager.ts new file mode 100644 index 00000000..02d09bce --- /dev/null +++ b/src/lib/config/KeychainManager.ts @@ -0,0 +1,29 @@ +import {default as keytar} from 'keytar'; + +export class KeychainManager { + private static readonly SERVICE = 'genlayer-cli'; + private static readonly ACCOUNT = 'default-user'; + + constructor() {} + + async isKeychainAvailable(): Promise { + try { + await keytar.findCredentials('test-service'); + return true; + } catch { + return false; + } + } + + async storePrivateKey(privateKey: string): Promise { + return await keytar.setPassword(KeychainManager.SERVICE, KeychainManager.ACCOUNT, privateKey); + } + + async getPrivateKey(): Promise { + return await keytar.getPassword(KeychainManager.SERVICE, KeychainManager.ACCOUNT); + } + + async removePrivateKey(): Promise { + return await keytar.deletePassword(KeychainManager.SERVICE, KeychainManager.ACCOUNT); + } +} \ No newline at end of file diff --git a/tests/actions/lock.test.ts b/tests/actions/lock.test.ts new file mode 100644 index 00000000..555cd01c --- /dev/null +++ b/tests/actions/lock.test.ts @@ -0,0 +1,66 @@ +import {describe, test, vi, beforeEach, afterEach, expect} from "vitest"; +import {LockAction} from "../../src/commands/keygen/lock"; + +describe("LockAction", () => { + let lockAction: LockAction; + + beforeEach(() => { + vi.clearAllMocks(); + lockAction = new LockAction(); + + // Mock the BaseAction methods + vi.spyOn(lockAction as any, "startSpinner").mockImplementation(() => {}); + vi.spyOn(lockAction as any, "setSpinnerText").mockImplementation(() => {}); + vi.spyOn(lockAction as any, "succeedSpinner").mockImplementation(() => {}); + vi.spyOn(lockAction as any, "failSpinner").mockImplementation(() => {}); + + // Mock keychainManager + vi.spyOn(lockAction["keychainManager"], "isKeychainAvailable").mockResolvedValue(true); + vi.spyOn(lockAction["keychainManager"], "getPrivateKey").mockResolvedValue("test-private-key"); + vi.spyOn(lockAction["keychainManager"], "removePrivateKey").mockResolvedValue(true); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + test("successfully locks wallet when keychain is available and key exists", async () => { + await lockAction.execute(); + + expect(lockAction["startSpinner"]).toHaveBeenCalledWith("Checking keychain availability..."); + expect(lockAction["keychainManager"].isKeychainAvailable).toHaveBeenCalled(); + expect(lockAction["setSpinnerText"]).toHaveBeenCalledWith("Checking for cached private key..."); + expect(lockAction["keychainManager"].getPrivateKey).toHaveBeenCalled(); + expect(lockAction["setSpinnerText"]).toHaveBeenCalledWith("Removing private key from OS keychain..."); + expect(lockAction["keychainManager"].removePrivateKey).toHaveBeenCalled(); + expect(lockAction["succeedSpinner"]).toHaveBeenCalledWith("Wallet locked successfully! Your private key has been removed from the OS keychain."); + }); + + test("fails when keychain is not available", async () => { + vi.spyOn(lockAction["keychainManager"], "isKeychainAvailable").mockResolvedValue(false); + + await lockAction.execute(); + + expect(lockAction["failSpinner"]).toHaveBeenCalledWith("OS keychain is not available. This command requires a supported keychain (e.g. macOS Keychain, Windows Credential Manager, or GNOME Keyring)."); + expect(lockAction["keychainManager"].getPrivateKey).not.toHaveBeenCalled(); + expect(lockAction["keychainManager"].removePrivateKey).not.toHaveBeenCalled(); + }); + + test("succeeds when wallet is already locked (no cached key)", async () => { + vi.spyOn(lockAction["keychainManager"], "getPrivateKey").mockResolvedValue(null); + + await lockAction.execute(); + + expect(lockAction["succeedSpinner"]).toHaveBeenCalledWith("Wallet is already locked (no cached key found in OS keychain)."); + expect(lockAction["keychainManager"].removePrivateKey).not.toHaveBeenCalled(); + }); + + test("handles error during key removal", async () => { + const mockError = new Error("Keychain error"); + vi.spyOn(lockAction["keychainManager"], "removePrivateKey").mockRejectedValue(mockError); + + await lockAction.execute(); + + expect(lockAction["failSpinner"]).toHaveBeenCalledWith("Failed to lock wallet.", mockError); + }); +}); \ No newline at end of file diff --git a/tests/actions/unlock.test.ts b/tests/actions/unlock.test.ts new file mode 100644 index 00000000..d9a4aa36 --- /dev/null +++ b/tests/actions/unlock.test.ts @@ -0,0 +1,121 @@ +import {describe, test, vi, beforeEach, afterEach, expect} from "vitest"; +import {UnlockAction} from "../../src/commands/keygen/unlock"; +import {readFileSync, existsSync} from "fs"; +import {ethers} from "ethers"; +import inquirer from "inquirer"; + +vi.mock("fs"); +vi.mock("ethers"); +vi.mock("inquirer"); + +describe("UnlockAction", () => { + let unlockAction: UnlockAction; + const mockKeystoreData = { + version: 1, + encrypted: '{"address":"test","crypto":{"cipher":"aes-128-ctr"}}', + address: "0x1234567890123456789012345678901234567890" + }; + const mockWallet = { + privateKey: "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890" + }; + + beforeEach(() => { + vi.clearAllMocks(); + unlockAction = new UnlockAction(); + + // Mock the BaseAction methods + vi.spyOn(unlockAction as any, "startSpinner").mockImplementation(() => {}); + vi.spyOn(unlockAction as any, "setSpinnerText").mockImplementation(() => {}); + vi.spyOn(unlockAction as any, "stopSpinner").mockImplementation(() => {}); + vi.spyOn(unlockAction as any, "succeedSpinner").mockImplementation(() => {}); + vi.spyOn(unlockAction as any, "failSpinner").mockImplementation(() => {}); + vi.spyOn(unlockAction as any, "promptPassword").mockResolvedValue("test-password"); + vi.spyOn(unlockAction as any, "getConfigByKey").mockReturnValue("./test-keypair.json"); + vi.spyOn(unlockAction as any, "isValidKeystoreFormat").mockReturnValue(true); + + // Mock keychainManager + vi.spyOn(unlockAction["keychainManager"], "isKeychainAvailable").mockResolvedValue(true); + vi.spyOn(unlockAction["keychainManager"], "storePrivateKey").mockResolvedValue(); + + // Mock fs and ethers + vi.mocked(existsSync).mockReturnValue(true); + vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockKeystoreData)); + vi.mocked(ethers.Wallet.fromEncryptedJson).mockResolvedValue(mockWallet as any); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + test("successfully unlocks wallet when all conditions are met", async () => { + await unlockAction.execute(); + + expect(unlockAction["startSpinner"]).toHaveBeenCalledWith("Checking keychain availability..."); + expect(unlockAction["keychainManager"].isKeychainAvailable).toHaveBeenCalled(); + expect(unlockAction["setSpinnerText"]).toHaveBeenCalledWith("Checking for existing keystore..."); + expect(unlockAction["getConfigByKey"]).toHaveBeenCalledWith("keyPairPath"); + expect(existsSync).toHaveBeenCalledWith("./test-keypair.json"); + expect(unlockAction["stopSpinner"]).toHaveBeenCalled(); + expect(unlockAction["promptPassword"]).toHaveBeenCalledWith("Enter password to decrypt keystore:"); + expect(readFileSync).toHaveBeenCalledWith("./test-keypair.json", "utf-8"); + expect(ethers.Wallet.fromEncryptedJson).toHaveBeenCalledWith(mockKeystoreData.encrypted, "test-password"); + expect(unlockAction["keychainManager"].storePrivateKey).toHaveBeenCalledWith(mockWallet.privateKey); + expect(unlockAction["succeedSpinner"]).toHaveBeenCalledWith("Wallet unlocked successfully! Your private key is now stored securely in the OS keychain."); + }); + + test("fails when keychain is not available", async () => { + vi.spyOn(unlockAction["keychainManager"], "isKeychainAvailable").mockResolvedValue(false); + + await unlockAction.execute(); + + expect(unlockAction["failSpinner"]).toHaveBeenCalledWith("OS keychain is not available. This command requires a supported keychain (e.g. macOS Keychain, Windows Credential Manager, or GNOME Keyring)."); + expect(unlockAction["promptPassword"]).not.toHaveBeenCalled(); + expect(unlockAction["keychainManager"].storePrivateKey).not.toHaveBeenCalled(); + }); + + test("fails when no keystore file is found", async () => { + vi.spyOn(unlockAction as any, "getConfigByKey").mockReturnValue(null); + + await unlockAction.execute(); + + expect(unlockAction["failSpinner"]).toHaveBeenCalledWith("No keystore file found. Please create a keypair first using 'genlayer keygen create'."); + expect(unlockAction["promptPassword"]).not.toHaveBeenCalled(); + }); + + test("fails when keystore file does not exist", async () => { + vi.mocked(existsSync).mockReturnValue(false); + + await unlockAction.execute(); + + expect(unlockAction["failSpinner"]).toHaveBeenCalledWith("No keystore file found. Please create a keypair first using 'genlayer keygen create'."); + expect(unlockAction["promptPassword"]).not.toHaveBeenCalled(); + }); + + test("fails when keystore format is invalid", async () => { + vi.spyOn(unlockAction as any, "isValidKeystoreFormat").mockReturnValue(false); + + await unlockAction.execute(); + + expect(unlockAction["failSpinner"]).toHaveBeenCalledWith("Invalid keystore format. Expected encrypted keystore file."); + expect(unlockAction["promptPassword"]).not.toHaveBeenCalled(); + }); + + test("handles error during wallet decryption", async () => { + const mockError = new Error("Decryption failed"); + vi.mocked(ethers.Wallet.fromEncryptedJson).mockRejectedValue(mockError); + + await unlockAction.execute(); + + expect(unlockAction["failSpinner"]).toHaveBeenCalledWith("Failed to unlock wallet.", mockError); + expect(unlockAction["keychainManager"].storePrivateKey).not.toHaveBeenCalled(); + }); + + test("handles error during key storage", async () => { + const mockError = new Error("Storage failed"); + vi.spyOn(unlockAction["keychainManager"], "storePrivateKey").mockRejectedValue(mockError); + + await unlockAction.execute(); + + expect(unlockAction["failSpinner"]).toHaveBeenCalledWith("Failed to unlock wallet.", mockError); + }); +}); \ No newline at end of file diff --git a/tests/commands/keygen.test.ts b/tests/commands/keygen.test.ts index bde597bd..f83c8c5a 100644 --- a/tests/commands/keygen.test.ts +++ b/tests/commands/keygen.test.ts @@ -2,8 +2,12 @@ import { Command } from "commander"; import { vi, describe, beforeEach, afterEach, test, expect } from "vitest"; import { initializeKeygenCommands } from "../../src/commands/keygen"; import { KeypairCreator } from "../../src/commands/keygen/create"; +import { UnlockAction } from "../../src/commands/keygen/unlock"; +import { LockAction } from "../../src/commands/keygen/lock"; vi.mock("../../src/commands/keygen/create"); +vi.mock("../../src/commands/keygen/unlock"); +vi.mock("../../src/commands/keygen/lock"); describe("keygen create command", () => { let program: Command; @@ -59,15 +63,7 @@ describe("keygen create command", () => { expect(KeypairCreator).toHaveBeenCalledTimes(1); }); - test("throws error for unrecognized options", async () => { - const keygenCommand = program.commands.find((cmd) => cmd.name() === "keygen"); - const createCommand = keygenCommand?.commands.find((cmd) => cmd.name() === "create"); - createCommand?.exitOverride(); - expect(() => program.parse(["node", "test", "keygen", "create", "--unknown"])).toThrowError( - "error: unknown option '--unknown'" - ); - }); test("keypairCreator.createKeypairAction is called without throwing errors for default options", async () => { program.parse(["node", "test", "keygen", "create"]); @@ -75,3 +71,53 @@ describe("keygen create command", () => { expect(() => program.parse(["node", "test", "keygen", "create"])).not.toThrow(); }); }); + +describe("keygen unlock command", () => { + let program: Command; + + beforeEach(() => { + program = new Command(); + initializeKeygenCommands(program); + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + test("UnlockAction is instantiated and execute is called", async () => { + program.parse(["node", "test", "keygen", "unlock"]); + expect(UnlockAction).toHaveBeenCalledTimes(1); + expect(UnlockAction.prototype.execute).toHaveBeenCalled(); + }); + + test("UnlockAction.execute is called without throwing errors", async () => { + vi.mocked(UnlockAction.prototype.execute).mockResolvedValue(); + expect(() => program.parse(["node", "test", "keygen", "unlock"])).not.toThrow(); + }); +}); + +describe("keygen lock command", () => { + let program: Command; + + beforeEach(() => { + program = new Command(); + initializeKeygenCommands(program); + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + test("LockAction is instantiated and execute is called", async () => { + program.parse(["node", "test", "keygen", "lock"]); + expect(LockAction).toHaveBeenCalledTimes(1); + expect(LockAction.prototype.execute).toHaveBeenCalled(); + }); + + test("LockAction.execute is called without throwing errors", async () => { + vi.mocked(LockAction.prototype.execute).mockResolvedValue(); + expect(() => program.parse(["node", "test", "keygen", "lock"])).not.toThrow(); + }); +}); diff --git a/tests/libs/baseAction.test.ts b/tests/libs/baseAction.test.ts index 5b26563c..b3464ee2 100644 --- a/tests/libs/baseAction.test.ts +++ b/tests/libs/baseAction.test.ts @@ -83,11 +83,12 @@ describe("BaseAction", () => { vi.spyOn(baseAction as any, "writeConfig").mockImplementation(() => {}); vi.spyOn(baseAction as any, "getConfig").mockReturnValue({}); - // Mock temp file methods - vi.spyOn(baseAction as any, "storeTempFile").mockImplementation(() => {}); - vi.spyOn(baseAction as any, "getTempFile").mockReturnValue(null); - vi.spyOn(baseAction as any, "clearTempFile").mockImplementation(() => {}); - vi.spyOn(baseAction as any, "cleanupExpiredTempFiles").mockImplementation(() => {}); + // Mock keychainManager methods + vi.spyOn(baseAction["keychainManager"], "isKeychainAvailable").mockResolvedValue(false); + vi.spyOn(baseAction["keychainManager"], "getPrivateKey").mockResolvedValue(null); + vi.spyOn(baseAction["keychainManager"], "storePrivateKey").mockResolvedValue(); + vi.spyOn(baseAction["keychainManager"], "removePrivateKey").mockResolvedValue(true); + }); afterEach(() => { @@ -291,12 +292,13 @@ describe("BaseAction", () => { }); test("should use cached key when available", async () => { - vi.spyOn(baseAction as any, "getTempFile").mockReturnValue(mockWallet.privateKey); + vi.spyOn(baseAction["keychainManager"], "isKeychainAvailable").mockResolvedValue(true); + vi.spyOn(baseAction["keychainManager"], "getPrivateKey").mockResolvedValue(mockWallet.privateKey); const account = await baseAction["getAccount"](false); expect((account as any).privateKey).toBe(mockWallet.privateKey); - expect(baseAction["getTempFile"]).toHaveBeenCalledWith("decrypted_private_key"); + expect(baseAction["keychainManager"].getPrivateKey).toHaveBeenCalled(); expect(inquirer.prompt).not.toHaveBeenCalled(); }); @@ -368,6 +370,7 @@ describe("BaseAction", () => { expect(ethers.Wallet.createRandom).toHaveBeenCalled(); expect(mockWallet.encrypt).toHaveBeenCalledWith("test-password"); expect(writeFileSync).toHaveBeenCalled(); + expect(baseAction["keychainManager"].removePrivateKey).toHaveBeenCalled(); }); test("should fail when file exists and overwrite is false", async () => { @@ -412,6 +415,7 @@ describe("BaseAction", () => { expect(result).toBe(mockWallet.privateKey); expect(writeFileSync).toHaveBeenCalled(); + expect(baseAction["keychainManager"].removePrivateKey).toHaveBeenCalled(); }); test("should return true for valid keystore format", () => { diff --git a/tests/libs/configFileManager.test.ts b/tests/libs/configFileManager.test.ts index fd52dbac..c84beabb 100644 --- a/tests/libs/configFileManager.test.ts +++ b/tests/libs/configFileManager.test.ts @@ -11,14 +11,12 @@ vi.mock("os") describe("ConfigFileManager", () => { const mockFolderPath = "/mocked/home/.genlayer"; const mockConfigFilePath = `${mockFolderPath}/genlayer-config.json`; - const mockTempFolderPath = "/mocked/tmp/genlayer-temp"; let configFileManager: ConfigFileManager; beforeEach(() => { vi.clearAllMocks(); vi.mocked(os.homedir).mockReturnValue("/mocked/home"); - vi.mocked(os.tmpdir).mockReturnValue("/mocked/tmp"); configFileManager = new ConfigFileManager(); }); @@ -109,207 +107,4 @@ describe("ConfigFileManager", () => { const expectedConfig = { existingKey: "newValue" }; expect(fs.writeFileSync).toHaveBeenCalledWith(mockConfigFilePath, JSON.stringify(expectedConfig, null, 2)); }); - - describe("Temp File Operations", () => { - beforeEach(() => { - vi.clearAllMocks(); - vi.mocked(os.homedir).mockReturnValue("/mocked/home"); - vi.mocked(os.tmpdir).mockReturnValue("/mocked/tmp"); - configFileManager = new ConfigFileManager(); - }); - - test("storeTempFile creates temp folder and stores file with timestamp", () => { - vi.mocked(fs.existsSync).mockReturnValue(false); - const mockTimestamp = 1234567890; - vi.spyOn(Date, 'now').mockReturnValue(mockTimestamp); - - configFileManager.storeTempFile("test.json", "test content"); - - expect(fs.mkdirSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp", { recursive: true, mode: 0o700 }); - - const expectedData = { - content: "test content", - timestamp: mockTimestamp - }; - expect(fs.writeFileSync).toHaveBeenCalledWith( - "/mocked/tmp/genlayer-temp/test.json", - JSON.stringify(expectedData), - { mode: 0o600 } - ); - }); - - test("storeTempFile does not create temp folder when it already exists", () => { - vi.clearAllMocks(); - vi.mocked(fs.existsSync).mockImplementation((path) => { - if (path === "/mocked/home/.genlayer") return true; - if (path === "/mocked/tmp/genlayer-temp") return true; - return false; - }); - - const testConfigManager = new ConfigFileManager(); - - const mockTimestamp = 1234567890; - vi.spyOn(Date, 'now').mockReturnValue(mockTimestamp); - - testConfigManager.storeTempFile("test.json", "test content"); - - expect(fs.mkdirSync).not.toHaveBeenCalled(); - - const expectedData = { - content: "test content", - timestamp: mockTimestamp - }; - expect(fs.writeFileSync).toHaveBeenCalledWith( - "/mocked/tmp/genlayer-temp/test.json", - JSON.stringify(expectedData), - { mode: 0o600 } - ); - }); - - test("getTempFile returns content when file exists and is not expired", () => { - const mockTimestamp = Date.now() - 60000; // 1 minute ago - const mockFileData = { - content: "cached content", - timestamp: mockTimestamp - }; - - vi.mocked(fs.existsSync).mockReturnValue(true); - vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockFileData)); - - const result = configFileManager.getTempFile("test.json"); - - expect(result).toBe("cached content"); - expect(fs.existsSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp/test.json"); - expect(fs.readFileSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp/test.json", "utf-8"); - }); - - test("getTempFile returns null when file does not exist", () => { - vi.mocked(fs.existsSync).mockReturnValue(false); - - const result = configFileManager.getTempFile("nonexistent.json"); - - expect(result).toBeNull(); - expect(fs.existsSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp/nonexistent.json"); - }); - - test("getTempFile returns null and clears file when expired", () => { - const expiredTimestamp = Date.now() - (6 * 60 * 1000); // 6 minutes ago (expired) - const mockFileData = { - content: "expired content", - timestamp: expiredTimestamp - }; - - vi.mocked(fs.existsSync).mockReturnValue(true); - vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockFileData)); - - const result = configFileManager.getTempFile("expired.json"); - - expect(result).toBeNull(); - expect(fs.unlinkSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp/expired.json"); - }); - - test("hasTempFile returns true when valid temp file exists", () => { - const mockTimestamp = Date.now() - 60000; // 1 minute ago - const mockFileData = { - content: "test content", - timestamp: mockTimestamp - }; - - vi.mocked(fs.existsSync).mockReturnValue(true); - vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockFileData)); - - const result = configFileManager.hasTempFile("test.json"); - - expect(result).toBe(true); - }); - - test("hasTempFile returns false when temp file is expired", () => { - const expiredTimestamp = Date.now() - (6 * 60 * 1000); // 6 minutes ago - const mockFileData = { - content: "expired content", - timestamp: expiredTimestamp - }; - - vi.mocked(fs.existsSync).mockReturnValue(true); - vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockFileData)); - - const result = configFileManager.hasTempFile("expired.json"); - - expect(result).toBe(false); - }); - - test("clearTempFile removes specific temp file", () => { - vi.mocked(fs.existsSync).mockReturnValue(true); - - configFileManager.clearTempFile("test.json"); - - expect(fs.existsSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp/test.json"); - expect(fs.unlinkSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp/test.json"); - }); - - test("clearTempFile does nothing when file does not exist", () => { - vi.mocked(fs.existsSync).mockReturnValue(false); - - configFileManager.clearTempFile("nonexistent.json"); - - expect(fs.existsSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp/nonexistent.json"); - expect(fs.unlinkSync).not.toHaveBeenCalled(); - }); - - test("cleanupExpiredTempFiles removes only expired files", () => { - const now = Date.now(); - const validTimestamp = now - 60000; // 1 minute ago (valid) - const expiredTimestamp = now - (6 * 60 * 1000); // 6 minutes ago (expired) - - vi.mocked(fs.existsSync).mockReturnValue(true); - vi.mocked(fs.readdirSync).mockReturnValue(['valid.json', 'expired.json'] as any); - - vi.mocked(fs.readFileSync) - .mockReturnValueOnce(JSON.stringify({content: "valid", timestamp: validTimestamp})) - .mockReturnValueOnce(JSON.stringify({content: "expired", timestamp: expiredTimestamp})); - - vi.spyOn(Date, 'now').mockReturnValue(now); - - configFileManager.cleanupExpiredTempFiles(); - - expect(fs.readdirSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp"); - expect(fs.readFileSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp/valid.json", "utf-8"); - expect(fs.readFileSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp/expired.json", "utf-8"); - expect(fs.unlinkSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp/expired.json"); - expect(fs.unlinkSync).toHaveBeenCalledTimes(1); // Only expired file should be deleted - }); - - test("cleanupExpiredTempFiles removes corrupted files that cannot be parsed", () => { - const now = Date.now(); - const validTimestamp = now - 60000; // 1 minute ago (valid) - - vi.mocked(fs.existsSync).mockReturnValue(true); - vi.mocked(fs.readdirSync).mockReturnValue(['valid.json', 'corrupted.json'] as any); - - vi.mocked(fs.readFileSync) - .mockReturnValueOnce(JSON.stringify({content: "valid", timestamp: validTimestamp})) - .mockReturnValueOnce("invalid json content"); - - vi.spyOn(Date, 'now').mockReturnValue(now); - - configFileManager.cleanupExpiredTempFiles(); - - expect(fs.readdirSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp"); - expect(fs.readFileSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp/valid.json", "utf-8"); - expect(fs.readFileSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp/corrupted.json", "utf-8"); - - // The corrupted file should be deleted due to JSON.parse error - expect(fs.unlinkSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp/corrupted.json"); - expect(fs.unlinkSync).toHaveBeenCalledTimes(1); // Only corrupted file should be deleted - }); - - test("cleanupExpiredTempFiles does nothing when temp folder does not exist", () => { - vi.mocked(fs.existsSync).mockReturnValue(false); - - configFileManager.cleanupExpiredTempFiles(); - - expect(fs.existsSync).toHaveBeenCalledWith("/mocked/tmp/genlayer-temp"); - expect(fs.readdirSync).not.toHaveBeenCalled(); - }); - }); }); diff --git a/tests/libs/keychainManager.test.ts b/tests/libs/keychainManager.test.ts new file mode 100644 index 00000000..7593a372 --- /dev/null +++ b/tests/libs/keychainManager.test.ts @@ -0,0 +1,112 @@ +import {describe, test, vi, beforeEach, afterEach, expect} from "vitest"; +import {KeychainManager} from "../../src/lib/config/KeychainManager"; +import keytar from "keytar"; + +vi.mock("keytar"); + +describe("KeychainManager", () => { + let keychainManager: KeychainManager; + + beforeEach(() => { + vi.clearAllMocks(); + keychainManager = new KeychainManager(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe("isKeychainAvailable", () => { + test("returns true when keychain is available", async () => { + vi.mocked(keytar.findCredentials).mockResolvedValue([]); + + const result = await keychainManager.isKeychainAvailable(); + + expect(result).toBe(true); + expect(keytar.findCredentials).toHaveBeenCalledWith("test-service"); + }); + + test("returns false when keychain is not available", async () => { + vi.mocked(keytar.findCredentials).mockRejectedValue(new Error("Keychain not available")); + + const result = await keychainManager.isKeychainAvailable(); + + expect(result).toBe(false); + expect(keytar.findCredentials).toHaveBeenCalledWith("test-service"); + }); + }); + + describe("storePrivateKey", () => { + test("successfully stores private key", async () => { + const privateKey = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"; + vi.mocked(keytar.setPassword).mockResolvedValue(); + + await keychainManager.storePrivateKey(privateKey); + + expect(keytar.setPassword).toHaveBeenCalledWith("genlayer-cli", "default-user", privateKey); + }); + + test("handles storage error", async () => { + const privateKey = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"; + vi.mocked(keytar.setPassword).mockRejectedValue(new Error("Storage failed")); + + await expect(keychainManager.storePrivateKey(privateKey)).rejects.toThrow("Storage failed"); + expect(keytar.setPassword).toHaveBeenCalledWith("genlayer-cli", "default-user", privateKey); + }); + }); + + describe("getPrivateKey", () => { + test("returns private key when it exists", async () => { + const expectedKey = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"; + vi.mocked(keytar.getPassword).mockResolvedValue(expectedKey); + + const result = await keychainManager.getPrivateKey(); + + expect(result).toBe(expectedKey); + expect(keytar.getPassword).toHaveBeenCalledWith("genlayer-cli", "default-user"); + }); + + test("returns null when private key does not exist", async () => { + vi.mocked(keytar.getPassword).mockResolvedValue(null); + + const result = await keychainManager.getPrivateKey(); + + expect(result).toBeNull(); + expect(keytar.getPassword).toHaveBeenCalledWith("genlayer-cli", "default-user"); + }); + + test("handles retrieval error", async () => { + vi.mocked(keytar.getPassword).mockRejectedValue(new Error("Retrieval failed")); + + await expect(keychainManager.getPrivateKey()).rejects.toThrow("Retrieval failed"); + expect(keytar.getPassword).toHaveBeenCalledWith("genlayer-cli", "default-user"); + }); + }); + + describe("removePrivateKey", () => { + test("successfully removes private key", async () => { + vi.mocked(keytar.deletePassword).mockResolvedValue(true); + + const result = await keychainManager.removePrivateKey(); + + expect(result).toBe(true); + expect(keytar.deletePassword).toHaveBeenCalledWith("genlayer-cli", "default-user"); + }); + + test("returns false when key does not exist", async () => { + vi.mocked(keytar.deletePassword).mockResolvedValue(false); + + const result = await keychainManager.removePrivateKey(); + + expect(result).toBe(false); + expect(keytar.deletePassword).toHaveBeenCalledWith("genlayer-cli", "default-user"); + }); + + test("handles removal error", async () => { + vi.mocked(keytar.deletePassword).mockRejectedValue(new Error("Removal failed")); + + await expect(keychainManager.removePrivateKey()).rejects.toThrow("Removal failed"); + expect(keytar.deletePassword).toHaveBeenCalledWith("genlayer-cli", "default-user"); + }); + }); +}); \ No newline at end of file