@ -12,7 +12,9 @@ import { sleep } from './scripts/util.js'
import { tests } from './scripts/tests.js'
import { headerStyle , greenChalk } from './scripts/styles.js'
import './scripts/strings.js'
import { installAoAlias , getAoCliVersion , selfUpdate , downloadManual , updateManual } from './scripts/features.js'
// Import AO modular features
import * as features from './scripts/features/index.js'
import { startPublicBootstrap } from './scripts/bootstrap.js'
import { isLoggedIn , loginPrompt , logout } from './scripts/session.js'
import { AO _DEFAULT _HOSTNAME } from './scripts/api.js'
@ -38,7 +40,6 @@ async function mainMenu() {
let mainMenuChoices = [
'AO' ,
'Features' ,
'Admin' ,
'Alchemy' ,
'Manual' ,
'Exit'
@ -50,33 +51,32 @@ async function mainMenu() {
choices : mainMenuChoices ,
pageSize : mainMenuChoices . length
} )
let previousChoice
switch ( answer . main _menu ) {
case 'AO' :
while ( await useAoMenu ( ) ) { }
break
case 'Features' :
while ( await featuresMenu ( ) ) { }
break
case 'Admin' :
while ( await adminMenu ( ) ) { }
do {
previousChoice = await featuresMenu ( previousChoice )
} while ( previousChoice !== false )
break
case 'Alchemy' :
while ( await alchemy Menu ( ) ) { }
while ( await admin Menu ( ) ) { }
break
case 'Manual' :
if ( ! isFolder ( AO _MANUAL _PATH ) ) {
console . log ( "Downloading the AO manual..." )
if ( downloadManua l( ) ) {
if ( features . manual . instal l( ) ) {
console . log ( "Downloaded the AO Manual from the official git repo via http and saved to" , AO _MANUAL _PATH + '.' )
} else {
console . log ( 'Failed to download the AO manual, sorry.' )
return false
}
} else {
updateManual ( )
features . manual . update ( )
}
await printManualPage ( AO _MANUAL _PATH ) // Fencepost case - print overview page
let previousChoice
do {
previousChoice = await manualFolderAsMenu ( AO _MANUAL _PATH , 'AO User Manual' , 'Back to Main Menu' , previousChoice + 1 )
} while ( previousChoice !== false )
@ -93,7 +93,10 @@ async function mainMenu() {
async function useAoMenu ( ) {
const loggedIn = isLoggedIn ( )
console . log ( ` \n ${ headerStyle ( 'AO' ) } \n ` )
if ( loggedIn ) {
console . log ( 'Logged in as:' , aoEnv ( 'AO_CLI_SESSION_USERNAME' ) )
console . log ( 'Top priority:' , await getTopPriorityText ( ) )
}
let aoMenuChoices = [ ]
if ( loggedIn ) {
aoMenuChoices . push (
@ -200,19 +203,14 @@ async function chatMenu() {
// 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 ( 'AO Admin Menu ' ) } ` )
console . log ( ` \n ${ headerStyle ( 'System Alchemy ' ) } ` )
const adminChoices = [
'Install \'ao\' alias for \'ao-cli\'' ,
'Update ao-cli' ,
'Check AO install' ,
'Update AO' ,
'Update system software' ,
'Switch AO target server' ,
'Switch AO database' ,
'Import/Export state/decks' ,
'Check AO service' ,
'Watch logs now' ,
'Start/Stop AO service' ,
'Tests' ,
'Update remote AOs' ,
'Back to Main Menu'
]
const answer = await inquirer . prompt ( {
@ -223,20 +221,13 @@ async function adminMenu() {
pageSize : adminChoices . length ,
} )
switch ( answer . admin _menu ) {
case 'Install \'ao\' alias for \'ao-cli\'' :
installAoAlias ( )
break
case 'Update ao-cli' :
await selfUpdate ( )
case 'Update system software' :
updateSoftware ( )
break
case 'Check AO install' :
case 'Update AO' :
case 'Switch AO target server' :
case 'Switch AO database' :
case 'Import/Export state/decks' :
case 'Check AO service' :
case 'Watch logs now' :
case 'Start/Stop AO service ' :
case 'Update remote AOs ' :
console . log ( "Not yet implemented." )
break
case 'Tests' :
@ -269,79 +260,148 @@ async function testsMenu() {
return true
}
// Prints the AO Admin Menu and executes the user's choice
async function alchemyMenu ( ) {
console . log ( ` \n ${ headerStyle ( 'Alchemy' ) } ` )
const alchemyChoices = [
'Update software' ,
'Install AO prerequisites' ,
'Check bitcoin status' ,
// Returns a colored capitalized status word
const styledStatus = ( fullWord ) => {
const lookup = {
off : ' ' + chalk . grey ( 'Off' ) + ' ' ,
installed : chalk . blue ( 'Installed' ) + ' ' ,
enabled : ' ' + greenChalk ( 'Enabled' ) + ' ' ,
running : ' ' + greenChalk ( 'Running' ) + ' ' ,
synced : ' ' + greenChalk ( 'Synced' ) + ' ' ,
error : ' ' + chalk . red ( 'Error' ) + ' '
}
return lookup [ fullWord . toLowerCase ( ) ] + ' '
}
// Prints the Configure AO Features menu and executes the user's choice
let featuresChoices
async function featuresMenu ( previousMenuChoice = 0 ) {
console . log ( ` \n ${ headerStyle ( 'Configure AO Features' ) } ` )
if ( ! featuresChoices ) {
featuresChoices = Object . entries ( features ) . map ( ( [ featureKey , feature ] ) => {
let featureName = featureKey
if ( feature . hasOwnProperty ( 'name' ) && feature . name . length >= 1 ) {
featureName = feature . name
}
const nameColumn = featureName . padEnd ( 17 )
const statusColumn = styledStatus ( feature . status ( ) )
const descriptionColumn = feature . description || ''
const choice = { name : nameColumn + statusColumn + descriptionColumn , value : featureKey , short : featureKey }
return choice
} )
featuresChoices . push (
'Back to Main Menu'
]
)
}
const answer = await inquirer . prompt ( {
name : 'alchemy_menu' ,
name : 'features _menu' ,
type : 'list' ,
message : 'Please choose:' ,
choices : alchemyChoices
choices : featuresChoices ,
default : previousMenuChoice ,
pageSize : featuresChoices . length
} )
switch ( answer . alchemy _menu ) {
case alchemyChoices [ 0 ] :
updateSoftware ( )
break
case alchemyChoices [ 1 ] :
installRequired ( )
break
case alchemyChoices [ 2 ] :
let stdout = execSync ( 'source ~/Alchemy/ingredients/lead && source ~/Alchemy/ingredients/gold && bitcoin_is_synced' )
console . log ( ` ${ stdout } ` )
break
default :
if ( answer . features _menu === 'Back to Main Menu' ) {
return false
}
return true
while ( await oneFeatureMenu ( answer . features _menu , features [ answer . features _menu ] ) ) { }
return answer . features _menu
}
// Prints the Configure AO Features menu and executes the user's choice
async function featuresMenu ( ) {
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' ) + ' '
// Prints the menu options for a specific feature.
// Each feature module can export functions for status, install, version, update and these will be listed in the menu based on context
// If the module also has a menu: field, these menu items will each be appended if the name is truthy when calculated
// Prints all the standard-named features plus features listed under 'menu'
async function oneFeatureMenu ( name , feature ) {
console . log ( ` \n ${ headerStyle ( name ) } ` )
const featureChoices = [ ]
const status = feature ? . status ( ) || false
if ( ! status ) {
console . log ( "This AO feature module lacks a status() function, not sure which menu items to display." )
return false
}
if ( status === 'off' ) {
if ( feature . hasOwnProperty ( 'install' ) ) {
featureChoices . push ( { name : 'Install ' + name , value : 'install' } )
menuItemCount ++
}
} else {
if ( feature . hasOwnProperty ( 'update' ) ) {
featureChoices . push ( { name : 'Update ' + name , value : 'update' } )
}
if ( feature . hasOwnProperty ( 'uninstall' ) ) {
featureChoices . push ( { name : 'Uninstall ' + name , value : 'uninstall' } )
}
}
let widest = 9
const features = [
` Tor ${ status . run } connect AOs p2p ` ,
` Bitcoin ${ status . run } payments ` ,
` Lightning ${ status . ins } payments ` ,
` nginx ${ status . ins } host AO publicly over the world wide web ` ,
` SSL/Certbot ${ status . ins } HTTPS for public web AO ` ,
` Jitsi ${ status . err } secure video chat ` ,
` Signal ${ status . off } notifications ` ,
` File hosting ${ status . err } file attachments on cards ` ,
` youtube-dl ${ status . ins } cache web videos ` ,
` Borg ${ status . ins } backup ` ,
` Encryption ${ status . err } serverside secret messages ` ,
` Themes ${ status . err } custom themes ` ,
` Glossary ${ status . err } custom glossary ` ,
` Jubilee ${ status . ena } monthly points creation event ` ,
if ( feature . hasOwnProperty ( 'menu' ) ) {
feature . menu . forEach ( menuItem => {
const menuItemName = typeof menuItem . name === 'function' ? menuItem . name ( ) : menuItem . name
if ( ! menuItemName ) {
return
}
const menuItemValue = typeof menuItem . value === 'function' ? menuItem . value ( ) : menuItem . value
// todo: uninstall option will go here also
featureChoices . push ( { name : menuItemName , value : menuItemValue } )
} )
}
if ( featureChoices . length < 1 ) {
console . log ( "Nothing to do yet on this feature, please check back soon." )
return false
}
featureChoices . push (
'Back to Features'
)
const answer = await inquirer . prompt ( {
name : 'feature_menu' ,
type : 'list' ,
message : 'Please choose:' ,
choices : featureChoices
} )
if ( answer . feature _menu === 'Back to Features' ) {
return false
}
if ( Object . keys ( feature ) . includes ( answer . feature _menu ) ) {
await feature [ answer . feature _menu ] ( )
return true
}
console . log ( 'Not yet implemented' )
return true
}
// Prints the AO Admin Menu and executes the user's choice
async function aoInstallMenu ( ) {
console . log ( ` \n ${ headerStyle ( 'Alchemy' ) } ` )
const aoServerChoices = [
{ name : 'Install AO prerequisites' , value : 'prereqs' } ,
'Check AO install' ,
'Update AO' ,
'Check AO service' ,
'Start/Stop AO service' ,
'Switch AO database' ,
'Switch AO version' ,
'Back to Main Menu'
]
const answer = await inquirer . prompt ( {
name : 'features_menu' ,
name : 'install _menu' ,
type : 'list' ,
message : 'Please choose:' ,
choices : features ,
pageSize : features . length
choices : aoServerChoic es,
pageSize : aoServerChoic es. length
} )
switch ( answer . features _menu ) {
case 'Back to Main Menu' :
return false
default :
console . log ( "Not yet implemented" )
switch ( answer . install _menu ) {
case 'prereqs' :
installRequired ( )
break
case 'Check AO install' :
case 'Update AO' :
case 'Check AO service' :
case 'Start/Stop AO service' :
case 'Switch AO database' :
case 'Switch AO version' :
console . log ( "Not yet implemented." )
return true
default :
return false
}
return true
}
@ -368,7 +428,7 @@ async function handleArgs(args) {
switch ( args [ 0 ] ) {
case '--version' :
case '-v' :
console . log ( await getAoCliV ersion( ) )
console . log ( await features [ 'ao-cli' ] . v ersion( ) )
return false
}
return true