import { existsSync, writeFileSync } from 'fs'; import { execFile } from 'child_process'; import { promisify } from 'util'; import { CliTerseError } from '@alwaysai/alwayscli'; import mkdirp = require('mkdirp'); import { dirname } from 'path'; import { PRIVATE_KEY_FILE_PRETTY_PATH, PRIVATE_KEY_FILE_PATH, PUBLIC_KEY_FILE_PATH, PUBLIC_KEY_FILE_PRETTY_PATH } from '../../paths'; import { ALWAYSAI_OS_PLATFORM } from '../../environment'; import { UnableToProceedWithoutMessage, Spinner } from '../../util'; import { confirmWriteFilePromptComponent } from './confirm-write-file-prompt-component'; const WRITE_MESSAGE = `Write ${PRIVATE_KEY_FILE_PRETTY_PATH}`; export const PUBLIC_KEY_FILE_COMMENT = 'Generated by the alwaysAI CLI'; export async function findOrWritePrivateKeyFileComponent(props: { yes: boolean; }) { const { yes } = props; if (existsSync(PRIVATE_KEY_FILE_PATH)) { // Make sure that the public part of the key is in place. It should be if it // was created with ssh-keygen. It might not be if the private key was // copied to this host from elsewhere. if (!existsSync(PUBLIC_KEY_FILE_PATH)) { const confirmed = yes || (await confirmWriteFilePromptComponent({ fileName: PUBLIC_KEY_FILE_PRETTY_PATH, description: 'Public key file' })); if (!confirmed) { throw new CliTerseError( UnableToProceedWithoutMessage(PUBLIC_KEY_FILE_PATH) ); } const spinner = Spinner(`Write ${PUBLIC_KEY_FILE_PRETTY_PATH}`); try { const { stdout } = await promisify(execFile)('ssh-keygen', [ '-y', '-f', PRIVATE_KEY_FILE_PATH ]); await writeFileSync(PUBLIC_KEY_FILE_PATH, stdout, { flag: 'wx' }); spinner.succeed(); } catch (exception) { if (exception.code === 'EEXIST') { spinner.succeed(); // Unlikely scenario that the file did not exist but now does } else { spinner.fail(); throw exception; } } } } else { // !exists const confirmed = yes || (await confirmWriteFilePromptComponent({ fileName: PRIVATE_KEY_FILE_PRETTY_PATH, description: 'Private key file' })); if (!confirmed) { throw new CliTerseError( UnableToProceedWithoutMessage(PRIVATE_KEY_FILE_PRETTY_PATH) ); } // ssh-keygen does not automatically create the .ssh directory on Windows if // it does not already exist. On non-Windows let's just let ssh-keygen // create the directory so that it has proper permissions. if (ALWAYSAI_OS_PLATFORM === 'win32') { mkdirp.sync(dirname(PRIVATE_KEY_FILE_PATH)); } // ssh-keygen creates the private key file and the corresponding .pub file const spinner = Spinner(WRITE_MESSAGE); try { await promisify(execFile)('ssh-keygen', [ '-q', '-b', '2048', '-t', 'rsa', '-N', '', '-C', PUBLIC_KEY_FILE_COMMENT, '-f', PRIVATE_KEY_FILE_PATH ]); spinner.succeed(); } catch (exception) { spinner.fail(); throw exception; } } }