Browse Source

stuck on POST bug (server not receiving payload)

main
deicidus 3 years ago
parent
commit
90b22aa611
  1. 15
      .prettierignore
  2. 13
      .prettierrc
  3. 44
      README.md
  4. 142
      index.js
  5. 24
      package-lock.json
  6. 6
      package.json
  7. 1711
      scripts/api.js
  8. 1
      scripts/bootstrap.js
  9. 12
      scripts/features.js
  10. 2
      scripts/manual.js
  11. 23
      scripts/priority.js
  12. 62
      scripts/session.js
  13. 2
      scripts/settings.js
  14. 0
      scripts/styles.js
  15. 6
      scripts/tests.js
  16. 2
      scripts/util.js
  17. 20
      scripts/welcome.js

15
.prettierignore

@ -0,0 +1,15 @@
.DS_Store
node_modules
/build
/dist
/production
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

13
.prettierrc

@ -0,0 +1,13 @@
{
printWidth: 80,
useTabs: false,
tabWidth: 2,
semi: false,
singleQuote: true,
quoteProps: 'as-needed',
trailingComma: 'none',
bracketSpacing: true,
arrowParens: 'avoid',
htmlWhitespaceSensitivity: strict,
vueIndentScriptAndStyle: true,
}

44
README.md

@ -1,6 +1,6 @@
# ao-cli # ao-cli
`ao-cli` (alias `ao`) is a command-line interface (CLI) that helps you install, use, and configure the Autonomous Organization (AO). `ao-cli` is a Node/JavaScript CLI tool that wraps the functionality of Alchemy, AO administration, plus key AO features into one convenient interface. Command-line social networking. `ao-cli` (alias `ao`) is a command-line interface (CLI) that helps you install, use, and configure the Autonomous Organization (AO). Command-line social networking for hackers.
To run immediately: To run immediately:
@ -10,10 +10,46 @@ To install:
`npm i -g @autonomousorganization/ao-cli` `npm i -g @autonomousorganization/ao-cli`
Then you can run with `ao-cli`. (Inside the menu you will find an option to add 'ao' as a shortcut.) Then you can run with `ao-cli`. (In the menus you will find an option to add `ao` as an alias.)
### Version History ## Features
These features work right now:
* Browse the [AO User Manual](https://git.coalitionofinvisiblecolleges.org/autonomousorganization/ao-manual) and automatically download and keep it updated
* Manages your AO configuration file for you
* Wraps the functionality of (some of) Zen's Alchemy suite of scripts (system configuration, AO installation)
* `ao-cli` can self-update to the newest version
* Run AO unit tests to verify the up-to-spec functioning of the system's running AO API server
## Upcoming Features
These features are planned and many are mocked up in the menus:
* Join the AO .onion bootstrapping network and find public AO chatrooms p2p over tor
* Operate essential AO client features (like creating and sending cards p2p via tor)
* Easily install and configure your AO server installation
* Easily use hardware-owner-only god-mode features for your AO server including resetting any password or deleting any member
* Easily monitor your AO server status and start/stop the service
* Easily switch between serving different AO frontends: `ao-svelte`, `ao-3` (Vue), or `ao-react`
* Easily install/uninstall and turn on/off option AO features
* Easily update all your remote AOs at once
* Easily install your preferred flavor of Unix on any unsecured Windows computer given its IP address (j/k)
* Full interactive wizard to walk you through setting up and connecting new AO hardware resources to your AO server
## Important Locations
* `~/.ao/` Your AO saved data folder
* `~/.ao/database.sqlite3` Location of your AO database (copy to back up)
* `~/ao-cli/` Typical location for `ao-cli`
* `~/ao-svelte/` Typical location for `ao-svelte`
* `~/ao-3/` Typical location for `ao-3`
* `~/.ao/ao-manual/` Typical location of the AO manual (Markdown files)
* `~/Alchemy/` Typical location of Zen's Alchemy
## Version History
* 0.0.8 Added self-update feature and --version/-v arg
* 0.0.6 User manual downloads and updates automatically from [official ao-manual repo](https://git.coalitionofinvisiblecolleges.org/autonomousorganization/ao-manual) * 0.0.6 User manual downloads and updates automatically from [official ao-manual repo](https://git.coalitionofinvisiblecolleges.org/autonomousorganization/ao-manual)
* 0.0.5 Added browsable manual (must download ao-svelte) * 0.0.5 Added browsable manual
* 0.0.1 Menus prototyped * 0.0.1 Menus prototyped

142
index.js

@ -10,10 +10,13 @@ import { printManualPage, manualFolderAsMenu, AO_MANUAL_PATH } from './scripts/m
import { isFolder, loadJsonFile } from './scripts/files.js' import { isFolder, loadJsonFile } from './scripts/files.js'
import { sleep } from './scripts/util.js' import { sleep } from './scripts/util.js'
import { tests } from './scripts/tests.js' import { tests } from './scripts/tests.js'
import { headerStyle } from './scripts/chalkStyles.js' import { headerStyle, greenChalk } from './scripts/styles.js'
import './scripts/strings.js' import './scripts/strings.js'
import { installAoAlias, getAoCliVersion, selfUpdate, downloadManual, updateManual } from './scripts/features.js' import { installAoAlias, getAoCliVersion, selfUpdate, downloadManual, updateManual } from './scripts/features.js'
import { startPublicBootstrap } from './scripts/bootstrap.js' import { startPublicBootstrap } from './scripts/bootstrap.js'
import { isLoggedIn, loginPrompt, logout } from './scripts/session.js'
import { AO_DEFAULT_HOSTNAME } from './scripts/api.js'
import { getTopPriorityText } from './scripts/priority.js'
// These should become .env variables that are loaded intelligently // These should become .env variables that are loaded intelligently
let distro let distro
@ -32,15 +35,13 @@ function exitIfRoot() {
// Prints the AO Main Menu and executes the user's choice // Prints the AO Main Menu and executes the user's choice
async function mainMenu() { async function mainMenu() {
console.log(`\n${headerStyle('AO Main Menu')}\n`) console.log(`\n${headerStyle('AO Main Menu')}\n`)
const mainMenuChoices = [ let mainMenuChoices = [
'Chat', 'AO',
'Alchemy', 'Features',
'Deck',
'Admin', 'Admin',
'Tests', 'Alchemy',
'Manual', 'Manual',
'Log Out', 'Exit'
'Exit',
] ]
const answer = await inquirer.prompt({ const answer = await inquirer.prompt({
name: 'main_menu', name: 'main_menu',
@ -50,20 +51,17 @@ async function mainMenu() {
pageSize: mainMenuChoices.length pageSize: mainMenuChoices.length
}) })
switch(answer.main_menu) { switch(answer.main_menu) {
case 'Chat': case 'AO':
while(await chatMenu()) {} while(await useAoMenu()) {}
break break
case 'Alchemy': case 'Features':
while(await alchemyMenu()) {} while(await featuresMenu()) {}
break
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'])
break break
case 'Admin': case 'Admin':
while(await adminMenu()) {} while(await adminMenu()) {}
break break
case 'Tests': case 'Alchemy':
while(await testsMenu()) {} while(await alchemyMenu()) {}
break break
case 'Manual': case 'Manual':
if(!isFolder(AO_MANUAL_PATH)) { if(!isFolder(AO_MANUAL_PATH)) {
@ -78,14 +76,11 @@ async function mainMenu() {
updateManual() updateManual()
} }
await printManualPage(AO_MANUAL_PATH) // Fencepost case - print overview page await printManualPage(AO_MANUAL_PATH) // Fencepost case - print overview page
let previousChoice = 0 let previousChoice
do { do {
previousChoice = await manualFolderAsMenu(AO_MANUAL_PATH, 'AO User Manual', 'Back to Main Menu', previousChoice + 1) previousChoice = await manualFolderAsMenu(AO_MANUAL_PATH, 'AO User Manual', 'Back to Main Menu', previousChoice + 1)
} while(previousChoice !== false) } while(previousChoice !== false)
break break
case 'Log Out':
await spinnerWait('Logging out... (just kidding)')
break
case 'Exit': case 'Exit':
farewell() farewell()
await sleep(310) await sleep(310)
@ -94,6 +89,50 @@ async function mainMenu() {
return true return true
} }
// Prints the Use AO Menu and executes the user's choice
async function useAoMenu() {
const loggedIn = isLoggedIn()
console.log(`\n${headerStyle('AO')}\n`)
console.log('Top priority:', await getTopPriorityText())
let aoMenuChoices = []
if(loggedIn) {
aoMenuChoices.push(
'Chat',
'Deck',
)
}
aoMenuChoices.push(
loggedIn ? 'Log Out' : 'Log In',
'Back to Main Menu'
)
const answer = await inquirer.prompt({
name: 'ao_menu',
type: 'list',
message: 'Please choose:',
choices: aoMenuChoices,
pageSize: aoMenuChoices.length
})
switch(answer.ao_menu) {
case 'Chat':
while(await chatMenu()) {}
break
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'])
break
case 'Log In':
console.log('\nao-cli will use the AO API to log into the AO server at', (aoEnv('AO_CLI_TARGET_HOSTNAME') || AO_DEFAULT_HOSTNAME) + '.')
await loginPrompt()
break
case 'Log Out':
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 // Prints a menu that allows you to join the global AO chatrooms
let publicBootstrapStarted = false let publicBootstrapStarted = false
async function chatMenu() { async function chatMenu() {
@ -159,6 +198,7 @@ async function chatMenu() {
} }
// Prints the AO Admin Menu and executes the user's choice // 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() { async function adminMenu() {
console.log(`\n${headerStyle('AO Admin Menu')}`) console.log(`\n${headerStyle('AO Admin Menu')}`)
const adminChoices = [ const adminChoices = [
@ -166,12 +206,13 @@ async function adminMenu() {
'Update ao-cli', 'Update ao-cli',
'Check AO install', 'Check AO install',
'Update AO', 'Update AO',
'Configure AO features', 'Switch AO target server',
'Switch AO database', 'Switch AO database',
'Import/Export state/decks', 'Import/Export state/decks',
'Check AO service', 'Check AO service',
'Watch logs now', 'Watch logs now',
'Start/Stop AO service', 'Start/Stop AO service',
'Tests',
'Back to Main Menu' 'Back to Main Menu'
] ]
const answer = await inquirer.prompt({ const answer = await inquirer.prompt({
@ -190,9 +231,7 @@ async function adminMenu() {
break break
case 'Check AO install': case 'Check AO install':
case 'Update AO': case 'Update AO':
case 'Configure AO features': case 'Switch AO target server':
while(await featuresMenu()) {}
break
case 'Switch AO database': case 'Switch AO database':
case 'Import/Export state/decks': case 'Import/Export state/decks':
case 'Check AO service': case 'Check AO service':
@ -200,6 +239,9 @@ async function adminMenu() {
case 'Start/Stop AO service': case 'Start/Stop AO service':
console.log("Not yet implemented.") console.log("Not yet implemented.")
break break
case 'Tests':
while(await testsMenu()) {}
break
default: default:
return false return false
} }
@ -262,21 +304,29 @@ async function alchemyMenu() {
// Prints the Configure AO Features menu and executes the user's choice // Prints the Configure AO Features menu and executes the user's choice
async function featuresMenu() { async function featuresMenu() {
console.log(`\n${headerStyle('Configure AO Features')}`) console.log(`\n${headerStyle('Configure AO Features')}`)
const status = {
off: ' ' + chalk.grey('Off') + ' ',
ins: chalk.yellow('Installed') + ' ',
ena: ' ' + greenChalk('Enabled') + ' ',
run: ' ' + greenChalk('Running') + ' ',
err: ' ' + chalk.red('Error') + ' '
}
let widest = 9
const features = [ const features = [
'nginx host AO publicly over the world wide web', `Tor ${status.run} connect AOs p2p`,
'SSL/Certbot HTTPS for public web AO', `Bitcoin ${status.run} payments`,
'Tor connect AOs p2p', `Lightning ${status.ins} payments`,
'Bitcoin payments', `nginx ${status.ins} host AO publicly over the world wide web`,
'Lightning payments', `SSL/Certbot ${status.ins} HTTPS for public web AO`,
'Jitsi secure video chat', `Jitsi ${status.err} secure video chat`,
'Signal notifications', `Signal ${status.off} notifications`,
'File hosting file attachments on cards', `File hosting ${status.err} file attachments on cards`,
'youtube-dl cache web videos', `youtube-dl ${status.ins} cache web videos`,
'Borg backup', `Borg ${status.ins} backup`,
'Encryption serverside secret messages', `Encryption ${status.err} serverside secret messages`,
'Themes custom themes', `Themes ${status.err} custom themes`,
'Glossary custom glossary', `Glossary ${status.err} custom glossary`,
'Jubilee monthly points creation event', `Jubilee ${status.ena} monthly points creation event`,
'Back to Main Menu' 'Back to Main Menu'
] ]
const answer = await inquirer.prompt({ const answer = await inquirer.prompt({
@ -296,16 +346,6 @@ async function featuresMenu() {
return true return true
} }
// Ask the user for their name and returns it
async function askName() {
const answer = await inquirer.prompt({
name: 'member_name',
type: 'input',
message: 'What username would you like?'
})
return answer.member_name
}
// Prints the given todoItems (array of strings) and allows items to be checked and unchecked // Prints the given todoItems (array of strings) and allows items to be checked and unchecked
async function todoList(title, todoItems) { async function todoList(title, todoItems) {
console.log(`\n${headerStyle(title)}`) console.log(`\n${headerStyle(title)}`)
@ -349,7 +389,11 @@ async function main() {
exitIfRoot() exitIfRoot()
// Loading screen, display some quick info during the fun animation // Loading screen, display some quick info during the fun animation
distro = aoEnv('DISTRO')
if(!distro) {
distro = detectOS() distro = detectOS()
setAoEnv('DISTRO', distro)
}
if(checkAoEnvFile()) { if(checkAoEnvFile()) {
console.log('AO .env file exists at', AO_ENV_FILE_PATH) console.log('AO .env file exists at', AO_ENV_FILE_PATH)
} else { } else {

24
package-lock.json generated

@ -30,6 +30,9 @@
}, },
"bin": { "bin": {
"ao-cli": "index.js" "ao-cli": "index.js"
},
"devDependencies": {
"prettier": "^2.6.2"
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
@ -1500,6 +1503,21 @@
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
}, },
"node_modules/prettier": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz",
"integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/qs": { "node_modules/qs": {
"version": "6.10.3", "version": "6.10.3",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
@ -3135,6 +3153,12 @@
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
}, },
"prettier": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz",
"integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==",
"dev": true
},
"qs": { "qs": {
"version": "6.10.3", "version": "6.10.3",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",

6
package.json

@ -7,7 +7,8 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "node .", "start": "node .",
"version": "node . -v" "version": "node . -v",
"prettier": "npx prettier"
}, },
"keywords": [ "keywords": [
"AO", "AO",
@ -41,5 +42,8 @@
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
},
"devDependencies": {
"prettier": "^2.6.2"
} }
} }

1711
scripts/api.js

File diff suppressed because it is too large Load Diff

1
scripts/bootstrap.js vendored

@ -1,6 +1,7 @@
// The bootstrapping module uses the glossary in peers.json (later will use members from DB?) // The bootstrapping module uses the glossary in peers.json (later will use members from DB?)
// to look up tor addresses for the give shortname or SSH public key. // to look up tor addresses for the give shortname or SSH public key.
// We could just do all this in the AO, but the bootstrapper is for public / loose ties and the AO's explicit p2p is for close / private ties. // We could just do all this in the AO, but the bootstrapper is for public / loose ties and the AO's explicit p2p is for close / private ties.
// The other main difference is that the AO stores data, and the chat server does not (ao-cli only uses database for Use AO Features).
// The bootstrapper occasionally queries all of the tor addresses in your address book. // The bootstrapper occasionally queries all of the tor addresses in your address book.
// If they are an AO with bootstrapping turned on, the AO server will respond with its public directory information. // If they are an AO with bootstrapping turned on, the AO server will respond with its public directory information.
// Since you have connected to them via their .onion address, it is assumed they are a known trusted party, // Since you have connected to them via their .onion address, it is assumed they are a known trusted party,

12
scripts/features.js

@ -8,6 +8,16 @@ import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url) const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename) const __dirname = path.dirname(__filename)
// It is possible to run ao-cli with npx @autonomousorganization/ao-cli. In this case, it can help you install it permanently.
export function installAoCli() {
try {
execSync('npm i -g @autonomousorganization/ao-cli 2>&1')
console.log('Installed ao-cli.')
} catch(err) {
console.log('Error installing ao-cli:', err)
}
}
// Adds a line to .bashrc to make 'ao' an alias for 'ao-cli', to simplify using the AO from the command line // Adds a line to .bashrc to make 'ao' an alias for 'ao-cli', to simplify using the AO from the command line
export function installAoAlias() { export function installAoAlias() {
try { try {
@ -31,7 +41,7 @@ export async function selfUpdate() {
const result = execSync('npm update -g @autonomousorganization/ao-cli 2>&1') const result = execSync('npm update -g @autonomousorganization/ao-cli 2>&1')
const afterVersionNumber = await getAoCliVersion() const afterVersionNumber = await getAoCliVersion()
if(beforeVersionNumber === afterVersionNumber) { if(beforeVersionNumber === afterVersionNumber) {
console.log("AO version is already current.") console.log("ao-cli version is already current.")
} else { } else {
console.log('\nao-cli self-updated automatically from version', beforeVersionNumber, 'to version', afterVersionNumber, 'from the official npm repository.') console.log('\nao-cli self-updated automatically from version', beforeVersionNumber, 'to version', afterVersionNumber, 'from the official npm repository.')
} }

2
scripts/manual.js

@ -3,7 +3,7 @@ import chalk from 'chalk'
import inquirer from 'inquirer' import inquirer from 'inquirer'
import { loadYamlMarkdownFile, lsFolder, isFolder } from './files.js' import { loadYamlMarkdownFile, lsFolder, isFolder } from './files.js'
import { repeatString, centerLines } from './strings.js' import { repeatString, centerLines } from './strings.js'
import { headerStyle, manualTitleStyle } from './chalkStyles.js' import { headerStyle, manualTitleStyle } from './styles.js'
import { basename } from 'path' import { basename } from 'path'
import { marked } from 'marked' import { marked } from 'marked'
import TerminalRenderer from 'marked-terminal' import TerminalRenderer from 'marked-terminal'

23
scripts/priority.js

@ -0,0 +1,23 @@
import { aoEnv } from './settings.js'
import { getCard } from './api.js'
// Prints the text (.name) of the first card prioritized in the logged-in users member card
export async function getTopPriorityText() {
// Get the first priority of my member card
const memberId = aoEnv('AO_CLI_SESSION_MEMBERID')
if(!memberId) {
return 'Not logged in'
}
const fetchedCards = await getCard(memberId, 'priority')
console.log('fetch result:', fetchedCards)
if(!fetchedCards || fetchedCards.length < 2) {
return 'None'
}
const firstPriorityCard = fetchedCards[1]
return firstPriorityCard.name
}
// Makes an API request to get the first prioritized card in the member card of the logged-in user
async function getFirstPriorityCard() {
}

62
scripts/session.js

@ -0,0 +1,62 @@
import { createSession, logout as apiLogout } from './api.js'
import { aoEnv, setAoEnv } from './settings.js'
import { askQuestionText } from './welcome.js'
// Returns true if there is a session cookie for ao-cli saved in the AO .env file (=ready to make session requests)
export function isLoggedIn() {
const username = aoEnv('AO_CLI_SESSION_USERNAME')
const sessionId = aoEnv('AO_CLI_SESSION_ID')
const sessionToken = aoEnv('AO_CLI_SESSION_TOKEN')
return username && sessionId && sessionToken
}
// Interactive prompt to log in. Performs the login request.
export async function loginPrompt() {
const username = await askQuestionText('Username:')
const password = await askQuestionText('Password:', { type: 'password' })
await login(username, password)
}
export async function login(username, password) {
try {
console.log('Attempting login as', username, 'with password', '*'.repeat(password.length))
const response = await createSession(username, password)
if(response) {
setAoEnv('AO_CLI_SESSION_USERNAME', username)
setAoEnv('AO_CLI_SESSION_MEMBERID', response.memberId) // might not need to save this actually
setAoEnv('AO_CLI_SESSION_ID', response.session)
setAoEnv('AO_CLI_SESSION_TOKEN', response.token)
console.log('Logged in as', username + '.', 'memberId:', response.memberId)
return true
} else {
console.log('Login failed. Response:', response)
return false
}
} catch(err) {
if(err.status === 401) {
console.log("No account on the AO server matched the username and password you entered. (401 Unauthorized)")
} else {
console.log(err)
}
return false
}
}
export async function logout() {
try {
console.log('Logging out...')
const response = await apiLogout()
if(response.statusCode === 200) {
setAoEnv('AO_CLI_SESSION_USERNAME', null)
setAoEnv('AO_CLI_SESSION_ID', null)
setAoEnv('AO_CLI_SESSION_TOKEN', null)
console.log('Logged out')
} else {
console.log('Logout failed. Response:', response)
return false
}
} catch(err) {
console.log(err)
}
return true
}

2
scripts/settings.js

@ -99,7 +99,7 @@ export function setAoEnv(variable, value) {
} }
if(value === null) { if(value === null) {
delete parsedFile.variable delete parsedFile[variable]
} else { } else {
parsedFile[variable] = value parsedFile[variable] = value
} }

0
scripts/chalkStyles.js → scripts/styles.js

6
scripts/tests.js

@ -2,14 +2,14 @@
// The tests actually happen so your database will be modified (future: allow switching databases or automatically switch) // 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 // 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 // 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 api from './api.js' import { createSession, logout } from './api.js'
async function testLoginAndOut() { async function testLoginAndOut() {
const username = 'ao' const username = 'ao'
const password = 'ao' const password = 'ao'
try { try {
console.log('Attempting login as', username, 'with password', '*'.repeat(password.length)) console.log('Attempting login as', username, 'with password', '*'.repeat(password.length))
const response = await api.createSession(username, password) const response = await createSession(username, password)
if(response === true) { if(response === true) {
console.log('Logged in as', username) console.log('Logged in as', username)
} else { } else {
@ -26,7 +26,7 @@ async function testLoginAndOut() {
try { try {
console.log('Logging out...') console.log('Logging out...')
const response = await api.logout() const response = await logout()
if(response.statusCode === 200) { if(response.statusCode === 200) {
console.log('Logged out') console.log('Logged out')
} else { } else {

2
scripts/util.js

@ -16,3 +16,5 @@ export function selectRandom(arrayToChooseFrom) {
export function sleep(ms = 550) { export function sleep(ms = 550) {
return new Promise((r) => setTimeout(r, ms)) return new Promise((r) => setTimeout(r, ms))
} }
export const isObject = (obj) => Object.prototype.toString.call(obj) === '[object Object]'

20
scripts/welcome.js

@ -1,6 +1,7 @@
import chalk from 'chalk' import chalk from 'chalk'
import inquirer from 'inquirer'
import { selectRandom } from './util.js' import { selectRandom } from './util.js'
import { greenChalk, theAO, theMenu } from './chalkStyles.js' import { greenChalk, theAO, theMenu } from './styles.js'
// Different sets of messages that can be randomly selected from // Different sets of messages that can be randomly selected from
const welcomeMessages = [ const welcomeMessages = [
@ -22,7 +23,9 @@ const welcomeMessages = [
`You are offered a choice between two pills. However, you have secretly built up an immunity to both pills, and trick your opponent into taking one. Inconceviably, you are in ${theAO}.`, `You are offered a choice between two pills. However, you have secretly built up an immunity to both pills, and trick your opponent into taking one. Inconceviably, you are in ${theAO}.`,
`A young man with spiky hair and golden skin appears before you in a halo of light. He guides you to ${theAO}.`, `A young man with spiky hair and golden skin appears before you in a halo of light. He guides you to ${theAO}.`,
`You enter a gap between two hedges and, after struggling through the brush, emerge into a sunny estate garden. You've found the AO.`, `You enter a gap between two hedges and, after struggling through the brush, emerge into a sunny estate garden. You've found the AO.`,
`You find a small animal burrow dug near the riverside. Crawling in, you find a network of caves that lead to the AO.` `You find a small animal burrow dug-out near the riverside. Crawling in, you find a network of caves that lead to ${theAO}.`,
`You receive a handwritten letter in the mail, which reads, in fine calligraphy:, "Dear —, You are in ${theAO}."`,
`An unexpected calm settles over you. The ${greenChalk.bold('AO')} is here: Here is ${theAO}.`
] ]
const menuMessages = [ const menuMessages = [
`You see ${theMenu}:`, `You see ${theMenu}:`,
@ -89,6 +92,19 @@ export function roger() {
return selectRandom(rogerMessages) return selectRandom(rogerMessages)
} }
// Returns a random farewell message
export function farewell() { export function farewell() {
console.log(chalk.yellow.bold(selectRandom(farewellMessages))) console.log(chalk.yellow.bold(selectRandom(farewellMessages)))
} }
// 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',
message: prompt
}
Object.assign(options, promptOptions)
const answer = await inquirer.prompt(options)
return answer.text
}
Loading…
Cancel
Save