diff --git a/README.md b/README.md index 39b8b8c..65bb5e2 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ These features work right now: * Add `ao` alias for `ao-cli` (under Features→ao-cli) * Optionally enchant your 'cd' command to narrate your travels through the UNIX filesystem and occasionally remind you of your top priority (under Features→ao-cli) * Easily add your existing systemctl services to the Features list so you can start and stop them from the AO Features menu -* Pedagogical codebase designed for teaching novice users. Code written to be read, with relevant contextual information mentioned in comments. Browse or download the code for ao-cli at the Repository link above. +* Pedagogical codebase designed for teaching novice developers. Code written to be read, with relevant contextual information mentioned in comments. Browse or download the code for ao-cli at the Repository link above. ## Upcoming Features diff --git a/index.js b/index.js index b790d7d..4bd3c5d 100644 --- a/index.js +++ b/index.js @@ -1,13 +1,12 @@ #!/usr/bin/env node import chalk from 'chalk' -import inquirer from 'inquirer' import { execSync } from 'child_process' // Import ao-cli core features import { exitIfRoot, detectOS, updateSoftware } from './scripts/system.js' import { checkAoEnvFile, aoEnv, setAoEnv, AO_ENV_FILE_PATH } from './scripts/settings.js' import { unicornPortal, asciiArt, clearScreen } from './scripts/console.js' -import { welcome, exclaim, farewell, yesOrNo } from './scripts/welcome.js' +import { welcome, exclaim, farewell, yesOrNo, promptMenu } from './scripts/welcome.js' import wander from './scripts/forest.js' import useAoMenu from './scripts/ao.js' import aoInstallWizard, { chooseAoVersion, checkAo } from './scripts/wizard.js' @@ -21,36 +20,19 @@ import features, { featuresMenu } from './scripts/features/index.js' import manual, { printManualPage, manualFolderAsMenu, AO_MANUAL_PATH } from './scripts/features/manual.js' import aoCli from './scripts/features/ao-cli.js' -// Enable keyboard shortcut interruption of inquirer menus -import './scripts/keyboard.js' - // Prints the AO Main Menu and executes the user's choice async function mainMenu() { - console.log(`\n${headerStyle('AO Main Menu')}\n`) + //console.log(`\n${headerStyle('AO Main Menu')}\n`) let mainMenuChoices = [ - 'AO', - 'Alchemy', - 'Configure', - 'Manual', - 'Exit' + { title: 'AO', description: 'use AO features' }, + { title: 'Alchemy', description: 'transmute system to gold' }, + { title: 'Configure', description: 'configure AO features' }, + { title: 'Manual', description: 'read the manual' }, + { title: 'Exit', description: 'ESC on any menu' } ] - let answer - try { - answer = await inquirer.prompt({ - name: 'main_menu', - type: 'list', - message: 'Please choose:', - choices: mainMenuChoices, - pageSize: mainMenuChoices.length - }) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') - answer = { main_menu: 'Exit' } - } - } + const answer = await promptMenu(mainMenuChoices, 'AO Main Menu') let previousChoice - switch(answer.main_menu) { + switch(answer) { case 'AO': while(await useAoMenu()) {} break @@ -79,7 +61,7 @@ async function mainMenu() { previousChoice = await manualFolderAsMenu(AO_MANUAL_PATH, 'AO User Manual', 'Back to Main Menu', previousChoice + 1) } while(previousChoice !== false) break - case 'Exit': + default: farewell() await sleep(310) return false @@ -90,9 +72,8 @@ async function mainMenu() { // Prints the AO Admin Menu and executes the user's choice // Maybe Alchemy menu should be installation and update, and admin menu should be more configuration & AO member admin async function adminMenu() { - console.log(`\n${headerStyle('System Alchemy')}`) const adminChoices = [ - { name: 'Check AO install', value: 'check_AO' }, + { title: 'Check AO install', value: 'check_AO' }, 'Update everything', 'Update system software', //'Update AO', @@ -109,22 +90,8 @@ async function adminMenu() { //'Update remote AOs', 'Back to Main Menu' ] - let answer - try { - answer = await inquirer.prompt({ - name: 'admin_menu', - type: 'list', - message: 'Please choose:', - choices: adminChoices, - pageSize: adminChoices.length, - }) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') - return false - } - } - switch(answer.admin_menu) { + const answer = await promptMenu(adminChoices, 'System Alchemy') + switch(answer) { case 'check_AO': await checkAo() break diff --git a/package-lock.json b/package-lock.json index 08949a2..9c35ffb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.8", "license": "AGPL-3.0-or-later", "dependencies": { + "blessed": "^0.1.81", "chalk": "^5.0.1", "chalk-animation": "^2.0.2", "crypto": "^1.0.1", @@ -16,11 +17,10 @@ "figlet": "^1.5.2", "gradient-string": "^2.0.1", "hash.js": "^1.1.7", - "inquirer": "^8.2.4", - "inquirer-interrupted-prompt": "^1.0.2", "marked": "^4.0.16", "marked-terminal": "^5.1.1", "nanospinner": "^1.1.0", + "prompts": "^2.4.2", "sha.js": "^2.4.11", "socket-io": "^1.0.0", "socket.io-client": "^4.5.1", @@ -135,14 +135,14 @@ "integrity": "sha512-Kf1w9NE5HEgGxCRyIcRXR/ZYtDv0V8FVPtYHwLxl0O+maGX0erE77pQlD0gpP+/KByMZ87mOA79SjifhSB3PjQ==" }, "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", "dependencies": { - "type-fest": "^0.21.3" + "type-fest": "^1.0.2" }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -201,56 +201,15 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "node_modules/blessed": { + "version": "0.1.81", + "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", + "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==", + "bin": { + "blessed": "bin/tput.js" + }, + "engines": { + "node": ">= 0.8.0" } }, "node_modules/call-bind": { @@ -293,17 +252,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/camelcase-keys/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cardinal": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", @@ -377,33 +325,6 @@ "node": ">=8" } }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-table3": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz", @@ -418,22 +339,6 @@ "@colors/colors": "1.5.0" } }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -532,14 +437,6 @@ "node": ">=0.10.0" } }, - "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", - "dependencies": { - "clone": "^1.0.2" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -629,19 +526,6 @@ "node": ">=4" } }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -655,20 +539,6 @@ "node": ">= 0.4.0" } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -852,36 +722,6 @@ "node": ">=10" } }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/indent-string": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", @@ -898,86 +738,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/inquirer-interrupted-prompt": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/inquirer-interrupted-prompt/-/inquirer-interrupted-prompt-1.0.2.tgz", - "integrity": "sha512-9RPmteKpUzUzwRW9nplHOWe6LAp4d5rE/5Yr3BdgpRspGN/ORXSUuIjEEJiwpYpRYMaVSC4wcsYMS5Gzic8egw==" - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -1002,14 +762,6 @@ "node": ">=8" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -1018,17 +770,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1059,6 +800,14 @@ "node": ">=0.10.0" } }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "engines": { + "node": ">=6" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -1083,55 +832,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1184,31 +884,6 @@ "marked": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0" } }, - "node_modules/marked-terminal/node_modules/ansi-escapes": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", - "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", - "dependencies": { - "type-fest": "^1.0.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/marked-terminal/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/meow": { "version": "10.1.2", "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.2.tgz", @@ -1234,17 +909,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/meow/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -1283,14 +947,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -1322,11 +978,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, "node_modules/nanospinner": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/nanospinner/-/nanospinner-1.1.0.tgz", @@ -1356,99 +1007,21 @@ "engines": { "node": ">=10" } - }, - "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "engines": { - "node": ">=0.10.0" + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" } }, "node_modules/p-limit": { @@ -1524,6 +1097,18 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/qs": { "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", @@ -1582,28 +1167,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -1640,34 +1203,6 @@ "esprima": "~4.0.0" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1687,11 +1222,6 @@ } ] }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, "node_modules/semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -1731,10 +1261,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, "node_modules/socket-io": { "version": "1.0.0", @@ -1910,11 +1440,6 @@ "node": ">=8" } }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, "node_modules/tinycolor2": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz", @@ -1932,17 +1457,6 @@ "tinycolor2": "^1.0.0" } }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/trim-newlines": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.0.2.tgz", @@ -1954,15 +1468,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - }, "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "engines": { "node": ">=10" }, @@ -1992,14 +1501,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/wrap-ansi": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", @@ -2223,11 +1724,11 @@ "integrity": "sha512-Kf1w9NE5HEgGxCRyIcRXR/ZYtDv0V8FVPtYHwLxl0O+maGX0erE77pQlD0gpP+/KByMZ87mOA79SjifhSB3PjQ==" }, "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", "requires": { - "type-fest": "^0.21.3" + "type-fest": "^1.0.2" } }, "ansi-regex": { @@ -2271,29 +1772,10 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } + "blessed": { + "version": "0.1.81", + "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", + "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==" }, "call-bind": { "version": "1.0.2", @@ -2318,13 +1800,6 @@ "map-obj": "^4.1.0", "quick-lru": "^5.1.1", "type-fest": "^1.2.1" - }, - "dependencies": { - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" - } } }, "cardinal": { @@ -2375,24 +1850,6 @@ } } }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==" - }, "cli-table3": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz", @@ -2402,16 +1859,6 @@ "string-width": "^4.2.0" } }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" - }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2482,14 +1929,6 @@ } } }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", - "requires": { - "clone": "^1.0.2" - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2554,16 +1993,6 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, "fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -2574,14 +2003,6 @@ "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.5.2.tgz", "integrity": "sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==" }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2712,19 +2133,6 @@ "lru-cache": "^6.0.0" } }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, "indent-string": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", @@ -2735,67 +2143,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "inquirer-interrupted-prompt": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/inquirer-interrupted-prompt/-/inquirer-interrupted-prompt-1.0.2.tgz", - "integrity": "sha512-9RPmteKpUzUzwRW9nplHOWe6LAp4d5rE/5Yr3BdgpRspGN/ORXSUuIjEEJiwpYpRYMaVSC4wcsYMS5Gzic8egw==" - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -2814,21 +2161,11 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==" - }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==" }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2853,6 +2190,11 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -2871,39 +2213,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2933,21 +2242,6 @@ "cli-table3": "^0.6.1", "node-emoji": "^1.11.0", "supports-hyperlinks": "^2.2.0" - }, - "dependencies": { - "ansi-escapes": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", - "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", - "requires": { - "type-fest": "^1.0.2" - } - }, - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" - } } }, "meow": { @@ -2967,13 +2261,6 @@ "trim-newlines": "^4.0.2", "type-fest": "^1.2.2", "yargs-parser": "^20.2.9" - }, - "dependencies": { - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" - } } }, "methods": { @@ -2999,11 +2286,6 @@ "mime-db": "1.52.0" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, "min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -3029,11 +2311,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, "nanospinner": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/nanospinner/-/nanospinner-1.1.0.tgz", @@ -3074,59 +2351,6 @@ "wrappy": "1" } }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3170,6 +2394,15 @@ "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", "dev": true }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, "qs": { "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", @@ -3192,13 +2425,6 @@ "normalize-package-data": "^3.0.2", "parse-json": "^5.2.0", "type-fest": "^1.0.1" - }, - "dependencies": { - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" - } } }, "read-pkg-up": { @@ -3209,13 +2435,6 @@ "find-up": "^5.0.0", "read-pkg": "^6.0.0", "type-fest": "^1.0.1" - }, - "dependencies": { - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" - } } }, "readable-stream": { @@ -3245,38 +2464,11 @@ "esprima": "~4.0.0" } }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" - }, - "rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", - "requires": { - "tslib": "^2.1.0" - } - }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, "semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -3304,10 +2496,10 @@ "object-inspect": "^1.9.0" } }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, "socket-io": { "version": "1.0.0", @@ -3451,11 +2643,6 @@ } } }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, "tinycolor2": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz", @@ -3470,28 +2657,15 @@ "tinycolor2": "^1.0.0" } }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - }, "trim-newlines": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.0.2.tgz", "integrity": "sha512-GJtWyq9InR/2HRiLZgpIKv+ufIKrVrvjQWEj7PxAXNc5dwbNJkqhAUoAGgzRmULAnoOM5EIpveYd3J2VeSAIew==" }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - }, "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" }, "util-deprecate": { "version": "1.0.2", @@ -3512,14 +2686,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "requires": { - "defaults": "^1.0.3" - } - }, "wrap-ansi": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", diff --git a/package.json b/package.json index 20edf64..b7a426a 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "debug": "node inspect ." }, "keywords": [ + "cli", "AO", "Autonomous Organization", "autonomous", @@ -27,6 +28,7 @@ "author": "Coalition of Invisible Colleges", "license": "AGPL-3.0-or-later", "dependencies": { + "blessed": "^0.1.81", "chalk": "^5.0.1", "chalk-animation": "^2.0.2", "crypto": "^1.0.1", @@ -34,11 +36,10 @@ "figlet": "^1.5.2", "gradient-string": "^2.0.1", "hash.js": "^1.1.7", - "inquirer": "^8.2.4", - "inquirer-interrupted-prompt": "^1.0.2", "marked": "^4.0.16", "marked-terminal": "^5.1.1", "nanospinner": "^1.1.0", + "prompts": "^2.4.2", "sha.js": "^2.4.11", "socket-io": "^1.0.0", "socket.io-client": "^4.5.1", diff --git a/scripts/ao.js b/scripts/ao.js index c02d286..c19f633 100644 --- a/scripts/ao.js +++ b/scripts/ao.js @@ -2,15 +2,15 @@ // The ao-cli client has no store so it makes frequent (hopefully very quick) calls to get exactly the information it needs. // This requires us to make sure the AO API server's REST API is concise and efficient. // The only places ao-cli should call out to an AO server (i.e., use the API in api.js) are in this Use AO menu and in the Tests menu. -import inquirer from 'inquirer' import { aoEnv } from './settings.js' import { isLoggedIn, loginPrompt, logout } from './session.js' import { getTopPriorityText } from './priority.js' -import { startPublicBootstrap } from './bootstrap.js' import { AO_DEFAULT_HOSTNAME } from './api.js' +import { headerStyle } from './styles.js' import { cardMenu } from './cards.js' import { connectMenu } from './connect.js' -import { headerStyle } from './styles.js' +import chatMenu from './shadowchat.js' +import { promptMenu } from './welcome.js' // Prints the Use AO Menu and executes the user's choice. Using the AO as a client occurs only under this menu item (except Tests menu). export default async function useAoMenu() { @@ -37,22 +37,8 @@ export default async function useAoMenu() { loggedIn ? 'Log Out' : 'Log In', 'Back to Main Menu' ) - let answer - try { - answer = await inquirer.prompt({ - name: 'ao_menu', - type: 'list', - message: 'Please choose:', - choices: aoMenuChoices, - pageSize: aoMenuChoices.length - }) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') - return false - } - } - switch(answer.ao_menu) { + const answer = await promptMenu(aoMenuChoices, 'Use AO features', 'connect to an AO server to use AO features') + switch(answer) { case 'Deck': //await todoList('My Todo List', ['Add full AO install process to ao-cli in convenient format', 'Add AO server unit tests to ao-cli', 'Get groceries', 'Play music every day']) while(await cardMenu()) {} @@ -71,62 +57,7 @@ export default async function useAoMenu() { await logout() //await spinnerWait('Logging out...') break - case 'Back to Main Menu': - return false - } - return true -} - -// Prints a menu that allows you to join the global AO chatrooms -async function chatMenu() { - let answers = {} - const PUBLIC_BOOTSTRAP_ENABLED = aoEnv('PUBLIC_BOOTSTRAP_ENABLED') - if(PUBLIC_BOOTSTRAP_ENABLED) { - // They previously enabled public bootstrapping, so check to make sure it is working and then hide the option - // todo: start and then verify functioning of p2p boostrap method here - // if it's already started don't start it again - //if(!publicBootstrapStarted) { - console.log("\nBootstrapping public AO swarm...") - startPublicBootstrap() - console.log("Bootstrapped (just kidding)") - //} - //answers['chat_menu'] = 'Enable p2p bootstrap' - } - const chatChoices = [ - { name: 'Join public chatroom', value: 'browse_chatrooms', short: 'public chatrooms' }, - { name: 'Join chatroom', value: 'join_chat', short: 'join chat' }, - 'Address Book', - 'Back to AO Menu', - ] - console.log(`\n${headerStyle('AO Public Chatrooms')}\n`) - let answer - try { - answer = await inquirer.prompt({ - name: 'chat_menu', - type: 'list', - message: 'Please choose:', - choices: chatChoices - }) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') - return false - } - } - switch(answer.chat_menu) { - case 'browse_chatrooms': - console.log('Not yet implemented') - break - case 'join_chat': - console.log('Not yet implemented') - break - case 'Address Book': - console.log('The point of this address book is to make it possible to type short, one-word names and have them resolve to tor addresses.') - console.log('Name a piece of data by saying name=data. For example, doge=5uc41o1...onion. Then \'doge\' will return the .onion address.') - console.log('Querying with any synonym in a chain will return the final meanings they all point to.') - console.log('Keys can have multiple values.') - break - case 'Back to AO Menu': + default: return false } return true diff --git a/scripts/api.js b/scripts/api.js index 83707b2..4b875e6 100644 --- a/scripts/api.js +++ b/scripts/api.js @@ -11,12 +11,14 @@ const HOSTNAME = aoEnv('AO_CLI_TARGET_HOSTNAME') || AO_DEFAULT_HOSTNAME const [HOST, PORT] = HOSTNAME.split(':') // The AO API server websocket endpoint this ao-cli client will attempt to connect to -const AO_SOCKET_URL = - process.env.NODE_ENV === 'development' ? 'http://' + AO_DEFAULT_HOSTNAME : '/' +const AO_SOCKET_URL = 'http://' + AO_DEFAULT_HOSTNAME // was process.env.NODE_ENV === 'development' ? 'http://' + AO_DEFAULT_HOSTNAME : '/' export const socket = io(AO_SOCKET_URL, { autoConnect: false }) +// The current connected state of the websocket, can be 'attemptingAuthentication' | 'authenticationSuccess' | 'authenticationFailed' +export let socketStatus + // Load the current session cookies from the AO .env file let currentMemberId = aoEnv('AO_CLI_SESSION_MEMBERID') let currentSessionId = aoEnv('AO_CLI_SESSION_ID') @@ -81,7 +83,7 @@ export async function createSession(user, pass) { .on('error', () => false) currentMemberId = result.body.memberId currentSessionToken = token - currentSessionId = session // Not used in this api.js yet + currentSessionId = session return { session, token, memberId: currentMemberId } } @@ -97,6 +99,43 @@ export async function nameAo(newName) { }) } +// When you call startSocketListeners, it will either attempt to connect and authenticate on a web socket, or fail and return. +// onAuthenticated is a function that will be called when the client authenticates on the web socket (logs in/connects). +// In your onAuthenticated function, you should trigger fetchState or other initial fetching of state from server. +// eventCallback is a function (ev) => {} that will be called whenever an event is received on the socket. +// In this way, initial state can be fetched and then updates received after that can be used to update the local state model. +export async function startSocketListeners(onAuthenticated, onEvent, verbose = true) { + if(typeof onAuthenticated !== 'function' || typeof onEvent !== 'function') { + console.log('startSocketListeners requires two callback functions as arguments.') + return + } + socket.connect() + socket.on('connect', () => { + if(verbose) console.log('websocket connected') + socketStatus = 'attemptingAuthentication' + if(!currentSessionId || !currentSessionToken) { + if(verbose) console.log('No current session, must log in to authenticate and use socket.') + return + } + + socket.emit('authentication', { + session: currentSessionId, + token: currentSessionToken, + }) + }) + socket.on('authenticated', () => { + if(verbose) console.log('websocket authenticated') + socketStatus = 'authenticationSuccess' + socket.on('eventstream', onEvent) + onAuthenticated() + }) + socket.on('disconnect', reason => { + if(verbose) console.log('websocket disconnected') + socketStatus = 'authenticationFailed' + socket.connect() + }) +} + // Requests the public bootstrap list by making a public (not logged in) GET request to this or the specified server export async function getAoBootstrapList(serverOnion = null) { const result = await getRequest('/bootstrap', undefined, serverOnion, false) @@ -125,6 +164,15 @@ export async function bootstrap(serverOnion = null) { return onionList } +export async function shadowchat(room, message, username) { + return await postEvent({ + type: 'shadowchat', + room: room, + name: username, + message: message, + }) +} + export async function connectToAo(address, secret) { return await postEvent({ type: 'ao-outbound-connected', @@ -865,60 +913,6 @@ export async function setQuorum(quorum) { }) } -/*startSocketListeners() { - this.socket.connect() - this.socket.on('connect', () => { - console.log('connected', { 'aoStore.state': aoStore.state }) - - runInAction(() => { - aoStore.state.socketState = 'attemptingAuthentication' - const loadedSession = window.localStorage.getItem('session') - if(loadedSession) { - aoStore.state.session = loadedSession - } - const loadedToken = window.localStorage.getItem('token') - if(loadedToken) { - currentSessionToken = loadedToken - } - }) - - console.log( - 'emit auth: session: ' + - window.localStorage.getItem('session') + - ', token: ' + - window.localStorage.getItem('token') - ) - this.socket.emit('authentication', { - session: window.localStorage.getItem('session'), - token: window.localStorage.getItem('token'), - }) - }) - this.socket.on('authenticated', () => { - console.log('authenticated') - - this.fetchState().then(() => { - runInAction(() => { - aoStore.state.socketState = 'authenticationSuccess' - }) - }) - - this.socket.on('eventstream', ev => { - console.log('AO: client/api.ts: socketListener: event:', ev) - - aoStore.applyEvent(ev) - }) - }) - this.socket.on('disconnect', reason => { - console.log('disconnected') - - runInAction(() => { - aoStore.state.socketState = 'authenticationFailed' - }) - - this.socket.connect() - }) -}*/ - /*reaction( () => { //return aoStore.state.socketState diff --git a/scripts/cards.js b/scripts/cards.js index b05a7f3..0aff672 100644 --- a/scripts/cards.js +++ b/scripts/cards.js @@ -1,45 +1,31 @@ // Cards module - everything related to cards should go here (database install is automatic for AO server so no feature module) -import inquirer from 'inquirer' import { getCardByName, createCard, prioritizeCard } from './api.js' import { headerStyle } from './styles.js' import { prioritiesMenu } from './priority.js' import { aoEnv } from './settings.js' +import { promptMenu, askQuestionText } from './welcome.js' // The card menu is complex and so has been split into this separate file export async function cardMenu() { - console.log(`\n${headerStyle('My Deck')}`) const cardChoices = [ - { name: 'Top priorities', value: 'priorities', short: 'priorities' }, // hand? (7) (add #s in parens) - { name: 'Cards in hand', value: 'subcards', short: 'hand' }, // (current) deck? (60) - { name: 'Browse full deck', value: 'browse', short: 'browse' }, // archive? (10,000) + { title: 'Top priorities', value: 'priorities', short: 'priorities' }, // hand? (7) (add #s in parens) + { title: 'Cards in hand', value: 'subcards', short: 'hand' }, // (current) deck? (60) + { title: 'Browse full deck', value: 'browse', short: 'browse' }, // archive? (10,000) 'Back to AO Menu' ] - try { - const answer = await inquirer.prompt({ - name: 'card_menu', - type: 'list', - message: 'Please choose:', - choices: cardChoices, - pageSize: cardChoices.length, - }) - switch(answer.card_menu) { - case 'priorities': - while(await prioritiesMenu()) {} - break - case 'subcards': - while(await subcardsMenu()) {} - break - case 'browse': - while(await browseMenu()) {} - break - default: - return false - } - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') + const answer = await promptMenu(cardChoices, 'My Deck') + switch(answer.card_menu) { + case 'priorities': + while(await prioritiesMenu()) {} + break + case 'subcards': + while(await subcardsMenu()) {} + break + case 'browse': + while(await browseMenu()) {} + break + default: return false - } } return true } @@ -54,17 +40,12 @@ async function browseMenu() { // Ask the user to create a card, checks if it already exists, and then creates it if it doesn't export async function createCardInteractive(prioritized = true) { - console.log('createcardinteractive') const memberId = aoEnv('AO_CLI_SESSION_MEMBERID') if(!memberId) { console.log('Not logged in.') return false } - const answer = await inquirer.prompt({ - name: 'new_card_text', - type: 'input', - message: 'New card or Enter to end:', - }) + const answer = await askQuestionText('New card or Enter to end:') if(answer.new_card_text.trim().length <= 0) { return false } diff --git a/scripts/connect.js b/scripts/connect.js index 1d49efb..6d66532 100644 --- a/scripts/connect.js +++ b/scripts/connect.js @@ -1,42 +1,26 @@ // Each AO API server can connect peer-to-peer over Tor. Tor addresses are unique and data is end-to-end encrypted. -import inquirer from 'inquirer' import { headerStyle } from './styles.js' import { aoEnv, setAoEnv } from './settings.js' import { isLoggedIn } from './session.js' import { isInstalled } from './features/tor.js' import { connectToAo, getAoBootstrapList, bootstrap } from './api.js' -import { roger } from './welcome.js' +import { roger, promptMenu } from './welcome.js' // Prints a menu to connect your AO to other AOs and manage connections export async function connectMenu() { - console.log(`\n${headerStyle('AO P2P')}`) const PUBLIC_BOOTSTRAP_ENABLED = aoEnv('PUBLIC_BOOTSTRAP_ENABLED') - let publicBootstrapMenuItem = { name: 'Enable p2p bootstrap', value: 'enable_bootstrap' } + let publicBootstrapMenuItem = { title: 'Enable p2p bootstrap', value: 'enable_bootstrap' } if(PUBLIC_BOOTSTRAP_ENABLED) { - publicBootstrapMenuItem = { name: 'Disable p2p bootstrap', value: 'disable_bootstrap' } + publicBootstrapMenuItem = { title: 'Disable p2p bootstrap', value: 'disable_bootstrap' } } const connectChoices = [ - { name: 'Connect to AO', value: 'connect', short: 'p2p connect'}, - { name: 'View connections', value: 'connections' }, - { name: 'Bootstrap now', value: 'bootstrap' }, + { title: 'Connect to AO', value: 'connect', short: 'p2p connect'}, + { title: 'View connections', value: 'connections' }, + { title: 'Bootstrap now', value: 'bootstrap' }, publicBootstrapMenuItem, - { name: 'Back to AO Menu', value: false } + { title: 'Back to AO Menu', value: false } ] - let answer - try { - answer = await inquirer.prompt({ - name: 'connect_menu', - type: 'list', - message: 'Please choose:', - choices: connectChoices, - pageSize: connectChoices.length, - }) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') - return false - } - } + const answer = await promptMenu(connectChoices, 'AO P2P') switch(answer.connect_menu) { case 'connect': await connectInteractive() @@ -110,24 +94,11 @@ async function connectInteractive() { } return true } - let answer - try { - answer = await inquirer.prompt({ - name: 'connection_string', - type: 'input', - message: 'Enter connection string of other AO:', - validate: validateConnectionString - }) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('ESC') - return false - } - } + const answer = await askQuestionText('Enter connection string of other AO:') //todo: validate: validateConnectionString const [onion, secret] = answer.connection_string.split(':') console.log('onion is', onion, 'and secret is', secret) console.log('Attempting connect...') const result = await connectToAo(onion, secret) console.log('result is', result.body) return true -} \ No newline at end of file +} diff --git a/scripts/features/index.js b/scripts/features/index.js index 5cca8c7..a4e0368 100644 --- a/scripts/features/index.js +++ b/scripts/features/index.js @@ -1,7 +1,6 @@ // Re-export the features modules in this folder, which each add, remove, and admininster one AO feature (imported in project index.js) // Also contains the Features menus to control these features in this directory import chalk from 'chalk' -import inquirer from 'inquirer' import fs from 'fs' import { lsFolder } from '../files.js' import { fileURLToPath } from 'url' @@ -9,6 +8,7 @@ import path from 'path' import { headerStyle, greenChalk, styledStatus } from '../styles.js' import { spinner } from '../console.js' import SystemServiceManager, { getCustomServicesList, addCustomServiceInteractive, removeCustomService } from '../services.js' +import { promptMenu } from '../welcome.js' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) @@ -33,7 +33,6 @@ export default features // Prints the Configure AO Features menu and executes the user's choice let featuresChoices export async function featuresMenu(previousMenuChoice = 0) { - console.log(`\n${headerStyle('Configure AO Features')}`) const stopSpinner = spinner('Loading status...') let loadedFeatures = 0 if(!featuresChoices) { @@ -49,10 +48,10 @@ export async function featuresMenu(previousMenuChoice = 0) { } const statusColumn = styledStatus(status, 25) const descriptionColumn = feature.description || '' - const choice = { name: nameColumn + statusColumn + descriptionColumn, value: featureKey, short: featureKey } + const choice = { title: nameColumn + statusColumn + descriptionColumn, value: featureKey, short: featureKey } return choice }) - featuresChoices.push(new inquirer.Separator()) + featuresChoices.push({ title: '---', disabled: true }) const customServices = getCustomServicesList() customServices.forEach(serviceName => { const nameColumn = serviceName.padEnd(17) @@ -60,11 +59,11 @@ export async function featuresMenu(previousMenuChoice = 0) { const status = service.status() || 'Unknown' const statusColumn = styledStatus(status, 25) const descriptionColumn = service.description || '' - const choice = { name: nameColumn + statusColumn + descriptionColumn, value: 'service_' + serviceName, short: serviceName } + const choice = { title: nameColumn + statusColumn + descriptionColumn, value: 'service_' + serviceName, short: serviceName } featuresChoices.push(choice) }) featuresChoices.push( - { name: 'Add custom service', value: 'add_service' }, + { title: 'Add custom service', value: 'add_service' }, 'Back to Main Menu' ) } else { @@ -73,23 +72,8 @@ export async function featuresMenu(previousMenuChoice = 0) { }).length } stopSpinner('Loaded status for ' + loadedFeatures + '/' + (featuresChoices.length - 1) + ' features.') - let answer - try { - answer = await inquirer.prompt({ - name: 'features_menu', - type: 'list', - message: 'Please choose:', - choices: featuresChoices, - default: previousMenuChoice, - pageSize: featuresChoices.length - }) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') - return false - } - } - if(answer.features_menu.includes('service_')) { + const answer = await promptMenu(featuresChoices, 'Configure AO Features', undefined, previousMenuChoice) + if(answer.includes('service_')) { const serviceName = answer.features_menu.replace(/^service_/, '') const service = new SystemServiceManager(serviceName) const nameWithoutDot = serviceName.split('.')[0] + ' service' @@ -97,7 +81,7 @@ export async function featuresMenu(previousMenuChoice = 0) { await oneFeatureMenu(nameWithoutDot, service, true) return answer.features_menu } - switch(answer.features_menu) { + switch(answer) { case 'add_service': console.log('Many Linux distributions run system services in the background. You can add an existing systemctl service to the AO Features menu to make it easier to start and stop your services. You must know the name of the service and it must already exist.') while(await addCustomServiceInteractive()) {} @@ -106,10 +90,10 @@ export async function featuresMenu(previousMenuChoice = 0) { case 'Back to Main Menu': return false } - const chosenFeature = features[answer.features_menu] - const chosenFeatureName = (chosenFeature.hasOwnProperty('name') && chosenFeature.name.length >= 1) ? chosenFeature.name : answer.features_menu + const chosenFeature = features[answer] + const chosenFeatureName = (chosenFeature.hasOwnProperty('name') && chosenFeature.name.length >= 1) ? chosenFeature.name : answer while(await oneFeatureMenu(chosenFeatureName, chosenFeature)) {} - return answer.features_menu + return answer } // Prints the menu options for a specific feature (including subfeatures) @@ -126,26 +110,26 @@ function oneFeatureMenuChoices(name, feature, status, isCustom = false) { const installed = typeof feature.isInstall === 'function' ? feature.isInstalled : feature.hasOwnProperty('isInstalled') ? feature.isInstalled : status !== 'off' const running = installed && typeof feature.isRunning === 'function' ? feature.isRunning() : feature.hasOwnProperty('isRunning') ? feature.isRunning : false if(running && typeof feature.stop === 'function') { - featureChoices.push({ name: 'Stop ' + name, value: () => feature.stop() }) + featureChoices.push({ title: 'Stop ' + name, value: feature.stop }) } else if(installed && !running && typeof feature.start === 'function') { - featureChoices.push({ name: 'Start ' + name, value: () => feature.start() }) + featureChoices.push({ title: 'Start ' + name, value: feature.start }) } if(status === 'off') { if(typeof feature.install === 'function') { - featureChoices.push({ name: 'Install ' + name, value: () => feature.install() }) + featureChoices.push({ title: 'Install ' + name, value: feature.install }) } } else { if(typeof feature.update === 'function') { - featureChoices.push({ name: 'Update ' + name, value: () => feature.update() }) + featureChoices.push({ title: 'Update ' + name, value: feature.update }) } if(typeof feature.uninstall === 'function') { - featureChoices.push({ name: 'Uninstall ' + name, value: () => feature.uninstall() }) + featureChoices.push({ title: 'Uninstall ' + name, value: feature.uninstall }) } } if(isCustom) { - featureChoices.push({ name: 'Remove from list', value: () => { + featureChoices.push({ title: 'Remove from list', value: () => { const nameWithoutLabel = name.replace(/ service/, '') removeCustomService(nameWithoutLabel) featuresChoices = null @@ -157,7 +141,7 @@ function oneFeatureMenuChoices(name, feature, status, isCustom = false) { feature.menu.forEach(menuItem => { const menuItemName = typeof menuItem.name === 'function' ? menuItem.name() : menuItem.name if(menuItemName) { - featureChoices.push({ name: menuItemName, value: menuItem.Value }) + featureChoices.push({ title: menuItemName, value: menuItem.Value }) } // todo: uninstall option will go here also }) @@ -197,30 +181,15 @@ export async function oneFeatureMenu(name, feature, isCustom = false) { featureChoices.push( 'Back to Features' ) - let answer - try { - answer = await inquirer.prompt({ - name: 'feature_menu', - type: 'list', - message: 'Please choose:', - choices: featureChoices - }) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') - return false - } else { - console.log('Unknown error displaying Features menu:', error) - } - } - if(answer.feature_menu === 'Back to Features') { + const answer = await promptMenu(featureChoices, name) + if(answer === 'Back to Features') { return false } - if(typeof answer.feature_menu === 'function') { - answer.feature_menu() + if(typeof answer === 'function') { + await answer() return true - } else if(Object.keys(feature).includes(answer.feature_menu)) { - await feature[answer.feature_menu]() + } else if(Object.keys(feature).includes(answer)) { + await feature[answer]() return true } console.log('Not yet implemented') diff --git a/scripts/features/manual.js b/scripts/features/manual.js index 7a8468c..aa08bc2 100644 --- a/scripts/features/manual.js +++ b/scripts/features/manual.js @@ -1,9 +1,9 @@ // Functions for downloading, updating, and displaying the AO Manual, a hierarchy of markdown files import chalk from 'chalk' -import inquirer from 'inquirer' import { execSync, exec } from 'child_process' import { loadYamlMarkdownFile, lsFolder, isFolder } from '../files.js' import { repeatString, centerLines } from '../strings.js' +import { promptMenu } from '../welcome.js' import { headerStyle, manualTitleStyle } from '../styles.js' import { basename } from 'path' import { marked } from 'marked' @@ -158,25 +158,17 @@ export async function manualFolderAsMenu(path, menuTitle, backOption, previousMe if(previousMenuChoice >= menuChoices.length) { previousMenuChoice = 0 } - console.log(`\n${headerStyle(menuTitle)}`) - const answer = await inquirer.prompt({ - name: 'manual_menu', - type: 'rawlist', - message: 'Choose a topic:', - choices: menuChoices, - pageSize: menuChoices.length, - default: previousMenuChoice - }) - const chosenMenuIndex = menuChoices.indexOf(answer.manual_menu) - if(answer.manual_menu === backOption) { + const answer = await promptMenu(menuChoices, menuTitle, 'choose a topic', previousMenuChoice, undefined, true) + const chosenMenuIndex = menuChoices.indexOf(answer) + if(answer === backOption) { return false } - const chosenPath = path + Object.values(menuItems.find(menuItem => Object.keys(menuItem)[0] === answer.manual_menu))[0] - await printManualPage(chosenPath, answer.manual_menu) + const chosenPath = path + Object.values(menuItems.find(menuItem => Object.keys(menuItem)[0] === answer))[0] + await printManualPage(chosenPath, answer) const newBackOption = backOption === 'Back to Main Menu' ? 'Back to Table of Contents' : 'Back to ' + menuTitle let previousChoice = 0 do { - previousChoice = await manualFolderAsMenu(chosenPath, answer.manual_menu, newBackOption, previousChoice + 1) + previousChoice = await manualFolderAsMenu(chosenPath, answer, newBackOption, previousChoice + 1) } while(previousChoice !== false) return chosenMenuIndex @@ -189,4 +181,4 @@ export default { install: downloadManual, isInstalled: () => manualStatus() === 'installed', update: updateManual -} \ No newline at end of file +} diff --git a/scripts/keyboard.js b/scripts/keyboard.js index 5f8cb93..6e5a5aa 100644 --- a/scripts/keyboard.js +++ b/scripts/keyboard.js @@ -1,10 +1,10 @@ // Hook to add keyboard shortcuts to all inquirer prompts -import inquirer from 'inquirer' -import InterruptedPrompt from 'inquirer-interrupted-prompt' +//import inquirer from 'inquirer' +//import InterruptedPrompt from 'inquirer-interrupted-prompt' -InterruptedPrompt.replaceAllDefaults(inquirer) +//InterruptedPrompt.replaceAllDefaults(inquirer) // Note that the above method can only detect one key per menu, apparently, and it can't tell you which one it detected in the callback. // Might be worth looking into a more flexible UI library that integrates keyboard shortcuts with its menu library. // Right now we are stuck with just using one key, the Escape key to go up a level in menus. Every menu must handle it or it can crash. -// The inquirer-interrupted-prompt documentation shows using a prompt with an array of prompt objects as args, but it didn't work for me. \ No newline at end of file +// The inquirer-interrupted-prompt documentation shows using a prompt with an array of prompt objects as args, but it didn't work for me. diff --git a/scripts/priority.js b/scripts/priority.js index db1c038..b87ea3f 100644 --- a/scripts/priority.js +++ b/scripts/priority.js @@ -1,9 +1,9 @@ // Prioritize cards within other cards. Each card has a .priorities array of other taskIds. -import inquirer from 'inquirer' import { headerStyle } from './styles.js' import { aoEnv } from './settings.js' import { getCard, prioritizeCard, completeCard, uncheckCard, refocusCard } from './api.js' import { createCardInteractive } from './cards.js' +import { promptMenu } from './welcome.js' // Prints the text (.name) of the first card prioritized in the logged-in users member card export async function getTopPriorityText() { @@ -65,7 +65,7 @@ export async function prioritiesMenu(taskId = null) { return 'Missing card, repair your database' } return { - name: priorityCard.name, + title: priorityCard.name, value: { index: i, card: priorityCard }, short: priorityCard.name.substring(0, 70) + priorityCard.name.length >= 70 ? '...' : '' } @@ -80,29 +80,15 @@ export async function prioritiesMenu(taskId = null) { } }) if(!isNaN(firstIndexEchelonDecreases)) { - prioritiesChoices.splice(firstIndexEchelonDecreases, 0, new inquirer.Separator('───')) + prioritiesChoices.splice(firstIndexEchelonDecreases, 0, { title: '───', disabled: true }) } } prioritiesChoices.push( - { name: 'Create priority', value: 'create_here', short: 'new priority' }, - { name: 'Back to Deck', value: false, short: 'back' } + { title: 'Create priority', value: 'create_here', short: 'new priority' }, + { title: 'Back to Deck', value: false, short: 'back' } ) - let answer - try { - answer = await inquirer.prompt({ - name: 'priorities_menu', - type: 'rawlist', - message: 'Please choose:', - choices: prioritiesChoices, - loop: false - }) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') - return false - } - } - switch(answer.priorities_menu) { + const answer = await promptMenu(prioritiesChoices, 'My Priorities', undefined, undefined, 'Upboated tasks will be inserted above this line') + switch(answer) { case false: return false case 'create_here': @@ -119,7 +105,7 @@ export async function prioritiesMenu(taskId = null) { const chosenTaskId = chosenTask.taskId let previousAnswer do { - previousAnswer = await priorityCardMenu(chosenTask, answer.priorities_menu.index, priorityCards) + previousAnswer = await priorityCardMenu(chosenTask, answer.index, priorityCards) if(previousAnswer) { const fetchedCards = await getCard(chosenTaskId, false) if(!fetchedCards || fetchedCards.length < 1) { @@ -147,33 +133,18 @@ async function priorityCardMenu(card, index, allPriorities) { return false } const isChecked = card.claimed.includes(memberId) - console.log(`\n${headerStyle('Priority: ' + card.name)}`) let priorityChoices = [] if(index != 0) { - priorityChoices.push({ name: 'Upboat', value: 'upboat', short: 'upboat' }) + priorityChoices.push({ title: 'Upboat', value: 'upboat', short: 'upboat' }) } priorityChoices.push( - { name: isChecked ? 'Uncheck' : 'Check off', value: 'check', short: 'check!' }, - { name: 'Downboat', value: 'downboat', short: 'downboat' }, - //{ name: 'Browse within', value: 'browse', short: 'browse' } - { name: 'Back to Priorities', value: false, short: 'back' } + { title: isChecked ? 'Uncheck' : 'Check off', value: 'check', short: 'check!' }, + { title: 'Downboat', value: 'downboat', short: 'downboat' }, + //{ title: 'Browse within', value: 'browse', short: 'browse' } + { title: 'Back to Priorities', value: false, short: 'back' } ) - let answer - try { - answer = await inquirer.prompt({ - name: 'priority_card_menu', - type: 'list', - message: 'Please choose:', - choices: priorityChoices, - pageSize: priorityChoices.length, - }) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') - return false - } - } - switch(answer.priority_card_menu) { + const answer = await promptMenu(priorityChoices, 'Priority: ' + card.name) + switch(answer) { case 'check': if(isChecked) { await uncheckCard(taskId) diff --git a/scripts/shadowchat.js b/scripts/shadowchat.js new file mode 100644 index 0000000..6ab6f86 --- /dev/null +++ b/scripts/shadowchat.js @@ -0,0 +1,232 @@ +// AO shadowchat feature menu including bootstrap network server list browser, chatroom list on each server, and chatroom interface +// Called shadowchat because no record is kept of the chat messages, and all connections happen E2E over tor +// As this feature gets build, sensible standards must be developed around when tor addresses change hands, when users authenticate, etc +import { aoEnv } from './settings.js' +import { isLoggedIn } from './session.js' +import { startPublicBootstrap } from './bootstrap.js' +import { headerStyle } from './styles.js' +import { sleep } from './util.js' +import { askQuestionText, promptMenu } from './welcome.js' +import { AO_DEFAULT_HOSTNAME, startSocketListeners, socketStatus, socket, shadowchat } from './api.js' + +import { fileURLToPath } from 'url' +import path from 'path' +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + +// Prints a menu that allows you to join the global AO chatrooms +export default async function chatMenu() { + let answers = {} + const PUBLIC_BOOTSTRAP_ENABLED = aoEnv('PUBLIC_BOOTSTRAP_ENABLED') + if(PUBLIC_BOOTSTRAP_ENABLED) { + // They previously enabled public bootstrapping, so check to make sure it is working and then hide the option + // todo: start and then verify functioning of p2p boostrap method here + // if it's already started don't start it again + //if(!publicBootstrapStarted) { + console.log("\nBootstrapping public AO swarm...") + startPublicBootstrap() + console.log("Bootstrapped (just kidding)") + //} + //answers['chat_menu'] = 'Enable p2p bootstrap' + } + const chatChoices = [ + { title: 'Join public chatroom', value: 'browse_chatrooms', short: 'public chatrooms' }, + { title: 'Join chatroom', value: 'join_chat', short: 'join chat' }, + 'Address Book', + 'Back to AO Menu', + ] + const answer = await promptMenu(chatChoices, 'AO Public Chatrooms') + switch(answer) { + case 'browse_chatrooms': + const loggedIn = isLoggedIn() + if(!isLoggedIn) { + console.log('Please start AO server and log in first.') + return true + } + console.log('Logged in as:', aoEnv('AO_CLI_SESSION_USERNAME')) + + const onAuthenticated = () => { + //console.log('Websocket connected and authenticated.') + } + const onEvent = (event) => { + console.log('\nEvent received on websocket:', event) // todo: still receiving excess task-resets every 5 minutes + } + if(socketStatus !== 'authenticationSuccess') { + await startSocketListeners(onAuthenticated, onEvent) + await sleep(400) + } + console.log('socketStatus is', socketStatus) + if(socketStatus !== 'authenticationSuccess') { + console.log('Failed to connect to websocket. Make sure your AO server is running, and if you have a custom config set your target server in ao-cli.') + return true + } + console.log('Websocket successfully connected to listen for events.') + while(await browseChatrooms()) {} + break + case 'join_chat': + console.log('Not yet implemented') + break + case 'Address Book': + console.log('The point of this address book is to make it possible to type short, one-word names and have them resolve to tor addresses.') + console.log('Name a piece of data by saying name=data. For example, doge=5uc41o1...onion. Then \'doge\' will return the .onion address.') + console.log('Querying with any synonym in a chain will return the final meanings they all point to.') + console.log('Keys can have multiple values.') + break + default: + return false + } + return true +} + +async function getLocalRooms() { + return new Promise((resolve, reject) => { + socket.on('rooms_list', event => { + socket.off('rooms_list') + resolve(event) + }) + socket.emit('get_rooms') + }) +} + +async function browseChatrooms() { + if(socketStatus !== 'authenticationSuccess') { + console.log('Websocket is not connected, going back.') + return false + } + console.log('Getting list of chatrooms...') + const chatrooms = await getLocalRooms() + // Get list of all servers and display + // todo: check if websocket can work without authentication + + // For each server, request its list of channels + console.log('Chatrooms on this server:', chatrooms) + + //await chatInRoom('HUB') + return await chatroomScreen('HUB') + + // Display the list as a menu with a heading for each server +} + +async function chatInRoom(room) { + console.log('Joining room...') + socket.emit('join_room', room) + return new Promise(async (resolve, reject) => { + const leaveChatroom = () => { + console.log('Leaving room...') + socket.emit('leave_room', room) + socket.off('chat') + resolve() + } + + socket.on('chat', event => { + const chatLine = event.name ? event.name + ': ' + event.message : 'Anon:' + event.message + console.log(/*new Date().toLocaleTimeString(), */'\n' + chatLine) + }) + + const username = aoEnv('AO_CLI_SESSION_USERNAME') + let message + do { + message = await askQuestionText('> ') + switch(message.toLowerCase()) { + case 'exit': + case 'leave': + case 'back': + case 'quit': + case false: + resolve(false) + default: + //socket.emit('chat', room, message, username || undefined) + shadowchat(room, message, username || undefined) + } + } while(message !== 'exit') + }) +} + +/*async function chatroomScreen(room) { + var ui = new inquirer.ui.BottomBar() + ui.render() + console.log(ui) + // pipe a Stream to the log zone + //outputStream.pipe(ui.log) + + // Or simply write output + ui.log.write('something just happened.'); + ui.log.write('Almost over, standby!'); + + // During processing, update the bottom bar content to display a loader + // or output a progress bar, etc + ui.updateBottomBar('new bottom bar content'); +}*/ + +import blessed from 'blessed' +export async function chatroomScreen(room) { + const screen = blessed.screen({ + smartCSR: true, + ignoreLocked: ['C-c'], + dockBorders: true, + title: room + }) + + const chatlog = blessed.log({ + top: 0, + left: 0, + bottom: 2, + width: '100%', + content: '{bold}' + room + '{/bold}', + tags: true, + scrollable: true, + border: { + type: 'line' + }, + style: { + fg: 'white', + bg: 'black', + border: { + fg: '#f0f0f0' + }, + } + }) + screen.append(chatlog); + + const input = blessed.textbox({ + bottom: 0, + left: 0, + right: 0, + height: 3, + content: 'Type message here', + inputOnFocus: true, + border: { + type: 'line' + }, + style: { + fg: 'white', + bg: 'blue', + border: { + fg: '#f0f0f0' + }, + } + }) + input.on('submit', (event) => { + chatlog.log('new submission:', event) + input.clearValue() + input.focus() + }) + input.on('keypress', (event) => { + chatlog.add('got keypress:', event) + }) + screen.append(input) + //screen.focusPop() + chatlog.add('screen.focus:', screen.focus) + input.focus() + + screen.render() + + return new Promise(async (resolve, reject) => { + //screen.key(['C-c'], () => process.exit(0)) + screen.key(['escape', 'C-c'], function(ch, key) { + chatlog.log('Exiting chatroom...') + screen.destroy() + resolve(false) + }) + }) +} diff --git a/scripts/tests.js b/scripts/tests.js index bd67396..8235bcf 100644 --- a/scripts/tests.js +++ b/scripts/tests.js @@ -2,8 +2,8 @@ // The tests actually happen so your database will be modified (future: allow switching databases or automatically switch) // The tests use an AO API file saved in the same directory; this file must be kept up-to-date // Maybe in the future a precompiled api.js created from api.ts can be hosted so that ao-cli does not have to compile any TypeScript -import inquirer from 'inquirer' import { createSession, logout } from './api.js' +import { promptMenu } from './welcome.js' async function testLoginAndOut() { const username = 'ao' @@ -56,24 +56,11 @@ export default async function testsMenu() { return menuTitle }) testChoices.push('Back to Main Menu') - let answer - try { - answer = await inquirer.prompt({ - name: 'tests_menu', - type: 'list', - message: 'Please choose:', - choices: testChoices - }) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') - return false - } - } - if(answer.tests_menu === 'Back to Main Menu') { + const answer = await promptMenu(testChoices, 'AO Unit Tests') + if(answer === 'Back to Main Menu') { return false } const testFunction = tests[answer.tests_menu] if(testFunction) await testFunction() return true -} \ No newline at end of file +} diff --git a/scripts/welcome.js b/scripts/welcome.js index e716a4b..43a2010 100644 --- a/scripts/welcome.js +++ b/scripts/welcome.js @@ -1,7 +1,8 @@ import chalk from 'chalk' -import inquirer from 'inquirer' +//import inquirer from 'inquirer' +import prompts from 'prompts' import { selectRandom } from './util.js' -import { greenChalk, theAO, theMenu } from './styles.js' +import { greenChalk, theAO, theMenu, headerStyle } from './styles.js' // Different sets of messages that can be randomly selected from const welcomeMessages = [ @@ -100,33 +101,63 @@ export function farewell() { // Asks the given yes or no answer returns true or false for their response export async function yesOrNo(prompt = 'Yes or no?', defaultAnswer = true) { - const answer = await inquirer.prompt({ - name: 'yes_or_no', + const answer = await prompts({ type: 'confirm', + name: 'value', message: prompt, - default: defaultAnswer + initial: defaultAnswer }) - return answer.yes_or_no + return answer.value || 'ESC' } // Ask the user the given question and returns their textual response export async function askQuestionText(prompt = 'Please enter a string:', promptOptions = {}) { let options = { - name: 'text', - type: 'input', + type: 'text', + name: 'value', message: prompt } Object.assign(options, promptOptions) - let answer - try { - answer = await inquirer.prompt(options) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') - return '' - } else { - console.log('Unknown error while displaying askQuestionText prompt:', error) + const answer = await prompts(options) + return answer.value || 'ESC' +} + +export async function promptMenu(choices, prompt = 'Please choose:', hint = '(Use arrow keys)', defaultValue = null, warningMessage = null, numbered = false) { + if(prompt !== 'Please choose:') { + prompt = `\n${headerStyle(prompt)}` + } + if(numbered) { + const prependNumber = (num, str) => num + '. ' + str + for(let i = 0; i < choices.length; i++) { + if(typeof choices[i] === 'string') { + choices[i] = prependNumber(i + 1, choices[i]) + } else if(typeof choices[i].title === 'string') { + if(!choices[i].value) { + choices[i].value = choices[i].title + } + choices[i].title = prependNumber(i + 1, choices[i].title) + } } } - return answer.text + console.log() + const answer = await prompts({ + type: 'select', + name: 'value', + message: prompt, + choices: choices, + hint: hint, + initial: defaultValue, + warn: warningMessage + }) + if(answer && Object.keys(answer).length === 0 && Object.getPrototypeOf(answer) === Object.prototype) { + return false + } + if(typeof answer.value === 'string') { + return answer.value + } + const chosenOption = choices[answer.value].value || choices[answer.value].title + if(!chosenOption) { + return choices[answer.value] + } + return chosenOption } diff --git a/scripts/wizard.js b/scripts/wizard.js index cd3c614..17d50cd 100644 --- a/scripts/wizard.js +++ b/scripts/wizard.js @@ -1,5 +1,4 @@ // Functions related to intelligently installing the AO as a whole. Specific additional feature modules are each a file under ./features. -import inquirer from 'inquirer' import path from 'path' import { execSync } from 'child_process' import { aoEnv, setAoEnv } from './settings.js' @@ -9,7 +8,7 @@ import { aoIsInstalled } from './features/ao-server.js' import { asciiArt } from './console.js' import { headerStyle, heading2, styledStatus } from './styles.js' import features from './features/index.js' -import { yesOrNo } from './welcome.js' +import { yesOrNo, promptMenu } from './welcome.js' import { isNpmPackageInstalled } from './system.js' const AO_PATH = path.join(process.env.HOME, '.ao') @@ -63,58 +62,27 @@ export default async function aoInstallWizard() { // Asks if the user wants to do a minimal, standard, or full install and returns their answer async function chooseInstallLevel() { - let answer - try { - answer = await inquirer.prompt({ - name: 'level_menu', - type: 'list', - message: 'What kind of installation?', - choices: [ - { name: 'Minimal'.padEnd(11) + 'only core AO web server (or only ao-cli)', value: 'minimal', short: 'minimal install' }, - { name: 'Standard'.padEnd(11) + 'most AO features installed (recommended)', value: 'standard', short: 'standard install' }, - { name: 'Full'.padEnd(11) + 'all AO features installed', value: 'full', short: 'full install' }, - { name: 'Cancel', value: false } - ] - }) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') - return false - } - } - return answer.level_menu + return await promptMenu([ + { title: 'Minimal'.padEnd(11) + 'only core AO web server (or only ao-cli)', value: 'minimal', short: 'minimal install' }, + { title: 'Standard'.padEnd(11) + 'most AO features installed (recommended)', value: 'standard', short: 'standard install' }, + { title: 'Full'.padEnd(11) + 'all AO features installed', value: 'full', short: 'full install' }, + { title: 'Cancel', value: false } + ], 'What kind of installation?') } // Asks whether the user wants to install ao-svelte, ao-3, or ao-cli (only) and returns their choice export async function chooseAoVersion() { - console.log(`\n${headerStyle('Choose AO Version')}`) console.log('Active version:', aoEnv('AO_VERSION')) - let answer - try { - answer = await inquirer.prompt({ - name: 'version_menu', - type: 'list', - message: 'Please choose:', - choices: [ - { name: 'ao-svelte'.padEnd(12) + 'new and mobile-first, currently in prototype phase', value: 'ao-svelte', short: 'ao-svelte' }, - { name: 'ao-3'.padEnd(12) + 'the original, created in Vue 3, polished and bug-free', value: 'ao-3', short: 'ao-3' }, - { name: 'ao-cli only'.padEnd(12), value: 'ao-cli' }, - 'Cancel' - ] - }) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') - return false - } - } - if(answer.version_menu === 'Cancel') { - return false - } + const answer = await promptMenu([ + { title: 'ao-svelte'.padEnd(12) + 'new and mobile-first, currently in prototype phase', value: 'ao-svelte', short: 'ao-svelte' }, + { title: 'ao-3'.padEnd(12) + 'the original, created in Vue 3, polished and bug-free', value: 'ao-3', short: 'ao-3' }, + { title: 'ao-cli only'.padEnd(12), value: 'ao-cli' }, + { title: 'Cancel', value: false } + ], 'Choose AO Version:') setAoEnv('AO_VERSION', answer.version_menu) // todo: If the version has changed, install it now console.log('AO version choice saved.') - return answer.version_menu + return answer } function checkAoDirectories() {