mirror of
https://github.com/codex-storage/cli.git
synced 2025-02-28 16:40:43 +00:00
Merge pull request #5 from codex-storage/feature/tool-config
Feature/tool config
This commit is contained in:
commit
dfec8f7c08
@ -1,11 +1,15 @@
|
||||
import { createSpinner } from 'nanospinner';
|
||||
import { runCommand } from '../utils/command.js';
|
||||
import { showErrorMessage, showInfoMessage, showSuccessMessage } from '../utils/messages.js';
|
||||
import { checkDependencies, isCodexInstalled } from '../services/nodeService.js';
|
||||
import path from 'path';
|
||||
import inquirer from 'inquirer';
|
||||
import boxen from 'boxen';
|
||||
import chalk from 'chalk';
|
||||
import os from 'os';
|
||||
import fs from 'fs';
|
||||
import { createSpinner } from 'nanospinner';
|
||||
import { runCommand } from '../utils/command.js';
|
||||
import { showErrorMessage, showInfoMessage, showSuccessMessage } from '../utils/messages.js';
|
||||
import { checkDependencies } from '../services/nodeService.js';
|
||||
import { saveConfig } from '../services/config.js';
|
||||
import { getCodexRootPath, getCodexBinPath, getCodexDataDirDefaultPath, getCodexLogsPath } from '../utils/appdata.js';
|
||||
|
||||
const platform = os.platform();
|
||||
|
||||
@ -52,19 +56,54 @@ These information will be used for calculating various metrics that can eventual
|
||||
return agreement.toLowerCase() === 'y';
|
||||
}
|
||||
|
||||
export async function checkCodexInstallation(showNavigationMenu) {
|
||||
export async function getCodexVersion(config) {
|
||||
if (config.codexExe.length < 1) return "";
|
||||
|
||||
try {
|
||||
const version = await runCommand('codex --version');
|
||||
console.log(chalk.green('Codex is already installed. Version:'));
|
||||
console.log(chalk.green(version));
|
||||
await showNavigationMenu();
|
||||
const version = await runCommand(`"${config.codexExe}" --version`);
|
||||
if (version.length < 1) throw new Error("Version info not found.");
|
||||
return version;
|
||||
} catch (error) {
|
||||
console.log(chalk.cyanBright('Codex is not installed, proceeding with installation...'));
|
||||
await installCodex(showNavigationMenu);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export async function installCodex(showNavigationMenu) {
|
||||
export async function checkCodexInstallation(config, showNavigationMenu) {
|
||||
const version = await getCodexVersion(config);
|
||||
|
||||
if (version.length > 0) {
|
||||
console.log(chalk.green('Codex is already installed. Version:'));
|
||||
console.log(chalk.green(version));
|
||||
await showNavigationMenu();
|
||||
} else {
|
||||
console.log(chalk.cyanBright('Codex is not installed, proceeding with installation...'));
|
||||
await installCodex(config, showNavigationMenu);
|
||||
}
|
||||
}
|
||||
|
||||
async function saveCodexExePathToConfig(config, codexExePath) {
|
||||
config.codexExe = codexExePath;
|
||||
config.dataDir = getCodexDataDirDefaultPath();
|
||||
config.logsDir = getCodexLogsPath();
|
||||
if (!fs.existsSync(config.codexExe)) {
|
||||
console.log(showErrorMessage(`Codex executable not found in expected path: ${config.codexExe}`));
|
||||
throw new Error("Exe not found");
|
||||
}
|
||||
if (await getCodexVersion(config).length < 1) {
|
||||
console.log(showInfoMessage("no"));
|
||||
throw new Error(`Codex not found at path after install. Path: '${config.codexExe}'`);
|
||||
}
|
||||
saveConfig(config);
|
||||
}
|
||||
|
||||
async function clearCodexExePathFromConfig(config) {
|
||||
config.codexExe = "";
|
||||
config.dataDir = "";
|
||||
config.logsDir = "";
|
||||
saveConfig(config);
|
||||
}
|
||||
|
||||
export async function installCodex(config, showNavigationMenu) {
|
||||
const agreed = await showPrivacyDisclaimer();
|
||||
if (!agreed) {
|
||||
console.log(showInfoMessage('You can find manual setup instructions at docs.codex.storage'));
|
||||
@ -72,40 +111,25 @@ export async function installCodex(showNavigationMenu) {
|
||||
return;
|
||||
}
|
||||
|
||||
const installPath = getCodexBinPath();
|
||||
console.log(showInfoMessage("Install location: " + installPath));
|
||||
|
||||
const spinner = createSpinner('Installing Codex...').start();
|
||||
|
||||
try {
|
||||
const spinner = createSpinner('Downloading Codex binaries...').start();
|
||||
|
||||
if (platform === 'win32') {
|
||||
try {
|
||||
try {
|
||||
await runCommand('curl --version');
|
||||
} catch (error) {
|
||||
spinner.error();
|
||||
throw new Error('curl is not available. Please install curl or update your Windows version.');
|
||||
}
|
||||
|
||||
await runCommand('curl -LO --ssl-no-revoke https://get.codex.storage/install.cmd');
|
||||
await runCommand(`set "INSTALL_DIR=${installPath}" && "${process.cwd()}\\install.cmd"`);
|
||||
|
||||
const currentDir = process.cwd();
|
||||
await runCommand(`"${currentDir}\\install.cmd"`);
|
||||
|
||||
await runCommand('set "PATH=%PATH%;%LOCALAPPDATA%\\Codex"');
|
||||
|
||||
try {
|
||||
await runCommand('setx PATH "%PATH%;%LOCALAPPDATA%\\Codex"');
|
||||
spinner.success();
|
||||
console.log(showSuccessMessage('Codex has been installed and PATH has been updated automatically!\n' +
|
||||
`You may need to restart your terminal.`
|
||||
));
|
||||
} catch (error) {
|
||||
spinner.success();
|
||||
console.log(showInfoMessage(
|
||||
'To complete installation:\n\n' +
|
||||
'1. Open Control Panel → System → Advanced System settings → Environment Variables\n' +
|
||||
'2. Or type "environment variables" in Windows Search\n' +
|
||||
'3. Add "%LOCALAPPDATA%\\Codex" to your Path variable'
|
||||
));
|
||||
}
|
||||
await saveCodexExePathToConfig(config, path.join(installPath, "codex.exe"));
|
||||
|
||||
try {
|
||||
await runCommand('del /f install.cmd');
|
||||
@ -113,23 +137,20 @@ export async function installCodex(showNavigationMenu) {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
} catch (error) {
|
||||
spinner.error();
|
||||
if (error.message.includes('Access is denied')) {
|
||||
throw new Error('Installation failed. Please run the command prompt as Administrator and try again.');
|
||||
} else if (error.message.includes('curl')) {
|
||||
throw new Error(error.message);
|
||||
} else {
|
||||
throw new Error('Installation failed. Please check your internet connection and try again.');
|
||||
throw new Error(`Installation failed: "${error.message}"`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const dependenciesInstalled = await checkDependencies();
|
||||
if (!dependenciesInstalled) {
|
||||
spinner.error();
|
||||
console.log(showInfoMessage('Please install the required dependencies and try again.'));
|
||||
await showNavigationMenu();
|
||||
return;
|
||||
throw new Error("Missing dependencies.");
|
||||
}
|
||||
|
||||
const downloadCommand = 'curl -# --connect-timeout 10 --max-time 60 -L https://get.codex.storage/install.sh -o install.sh && chmod +x install.sh';
|
||||
@ -140,19 +161,19 @@ export async function installCodex(showNavigationMenu) {
|
||||
eval {
|
||||
local $SIG{ALRM} = sub { die "timeout\\n" };
|
||||
alarm(120);
|
||||
system("bash install.sh");
|
||||
system("INSTALL_DIR=\\"${installPath}\\" bash install.sh");
|
||||
alarm(0);
|
||||
};
|
||||
die if $@;
|
||||
'`;
|
||||
await runCommand(timeoutCommand);
|
||||
} else {
|
||||
await runCommand('timeout 120 bash install.sh');
|
||||
await runCommand(`INSTALL_DIR="${installPath}" timeout 120 bash install.sh`);
|
||||
}
|
||||
|
||||
spinner.success();
|
||||
await saveCodexExePathToConfig(config, path.join(installPath, "codex"));
|
||||
|
||||
} catch (error) {
|
||||
spinner.error();
|
||||
if (error.message.includes('ECONNREFUSED') || error.message.includes('ETIMEDOUT')) {
|
||||
throw new Error('Installation failed. Please check your internet connection and try again.');
|
||||
} else if (error.message.includes('Permission denied')) {
|
||||
@ -168,28 +189,38 @@ export async function installCodex(showNavigationMenu) {
|
||||
}
|
||||
|
||||
try {
|
||||
const version = await runCommand('codex --version');
|
||||
const version = await getCodexVersion(config);
|
||||
console.log(showSuccessMessage(
|
||||
'Codex is successfully installed!\n\n' +
|
||||
'Codex is successfully installed!\n' +
|
||||
`Install path: "${config.codexExe}"\n\n` +
|
||||
`Version: ${version}`
|
||||
));
|
||||
} catch (error) {
|
||||
throw new Error('Installation completed but Codex command is not available. Please restart your terminal and try again.');
|
||||
}
|
||||
|
||||
spinner.success();
|
||||
await showNavigationMenu();
|
||||
} catch (error) {
|
||||
spinner.error();
|
||||
console.log(showErrorMessage(`Failed to install Codex: ${error.message}`));
|
||||
await showNavigationMenu();
|
||||
}
|
||||
}
|
||||
|
||||
export async function uninstallCodex(showNavigationMenu) {
|
||||
function removeDir(dir) {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
export async function uninstallCodex(config, showNavigationMenu) {
|
||||
const { confirm } = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: chalk.yellow('⚠️ Are you sure you want to uninstall Codex? This action cannot be undone.'),
|
||||
message: chalk.yellow(
|
||||
'⚠️ Are you sure you want to uninstall Codex? This action cannot be undone. \n' +
|
||||
'All data stored in the local Codex node will be deleted as well.'
|
||||
),
|
||||
default: false
|
||||
}
|
||||
]);
|
||||
@ -201,25 +232,8 @@ export async function uninstallCodex(showNavigationMenu) {
|
||||
}
|
||||
|
||||
try {
|
||||
if (platform === 'win32') {
|
||||
console.log(showInfoMessage('Removing Codex from Windows...'));
|
||||
|
||||
await runCommand('netsh advfirewall firewall delete rule name="Allow Codex (TCP-In)"');
|
||||
await runCommand('netsh advfirewall firewall delete rule name="Allow Codex (UDP-In)"');
|
||||
|
||||
await runCommand('rd /s /q "%LOCALAPPDATA%\\Codex"');
|
||||
|
||||
console.log(showInfoMessage(
|
||||
'To complete uninstallation:\n\n' +
|
||||
'1. Open Control Panel → System → Advanced System settings → Environment Variables\n' +
|
||||
'2. Or type "environment variables" in Windows Search\n' +
|
||||
'3. Remove "%LOCALAPPDATA%\\Codex" from your Path variable'
|
||||
));
|
||||
} else {
|
||||
const binaryPath = '/usr/local/bin/codex';
|
||||
console.log(showInfoMessage(`Attempting to remove Codex binary from ${binaryPath}...`));
|
||||
await runCommand(`sudo rm ${binaryPath}`);
|
||||
}
|
||||
removeDir(getCodexRootPath());
|
||||
clearCodexExePathFromConfig(config);
|
||||
|
||||
console.log(showSuccessMessage('Codex has been successfully uninstalled.'));
|
||||
await showNavigationMenu();
|
||||
|
@ -1,7 +1,8 @@
|
||||
import path from 'path';
|
||||
import { createSpinner } from 'nanospinner';
|
||||
import { runCommand } from '../utils/command.js';
|
||||
import { showErrorMessage, showInfoMessage, showSuccessMessage } from '../utils/messages.js';
|
||||
import { isNodeRunning, isCodexInstalled, logToSupabase, startPeriodicLogging, getWalletAddress, setWalletAddress } from '../services/nodeService.js';
|
||||
import { isNodeRunning, isCodexInstalled, startPeriodicLogging, getWalletAddress, setWalletAddress } from '../services/nodeService.js';
|
||||
import inquirer from 'inquirer';
|
||||
import boxen from 'boxen';
|
||||
import chalk from 'chalk';
|
||||
@ -27,8 +28,15 @@ async function promptForWalletAddress() {
|
||||
return wallet || null;
|
||||
}
|
||||
|
||||
export async function runCodex(showNavigationMenu) {
|
||||
const isInstalled = await isCodexInstalled();
|
||||
function getCurrentLogFile(config) {
|
||||
const timestamp = new Date().toISOString()
|
||||
.replaceAll(":", "-")
|
||||
.replaceAll(".", "-");
|
||||
return path.join(config.logsDir, `codex_${timestamp}.log`);
|
||||
}
|
||||
|
||||
export async function runCodex(config, showNavigationMenu) {
|
||||
const isInstalled = await isCodexInstalled(config);
|
||||
if (!isInstalled) {
|
||||
console.log(showErrorMessage('Codex is not installed. Please install Codex first using option 1 from the main menu.'));
|
||||
await showNavigationMenu();
|
||||
@ -65,9 +73,19 @@ export async function runCodex(showNavigationMenu) {
|
||||
nat = await runCommand('curl -s https://ip.codex.storage');
|
||||
}
|
||||
|
||||
const executable = `codex`;
|
||||
if (config.dataDir.length < 1) throw new Error("Missing config: dataDir");
|
||||
if (config.logsDir.length < 1) throw new Error("Missing config: logsDir");
|
||||
const logFilePath = getCurrentLogFile(config);
|
||||
|
||||
console.log(showInfoMessage(
|
||||
`Data location: ${config.dataDir}\n` +
|
||||
`Logs: ${logFilePath}`
|
||||
));
|
||||
|
||||
const executable = config.codexExe;
|
||||
const args = [
|
||||
`--data-dir=datadir`,
|
||||
`--data-dir="${config.dataDir}"`,
|
||||
`--log-file="${logFilePath}"`,
|
||||
`--disc-port=${discPort}`,
|
||||
`--listen-addrs=/ip4/0.0.0.0/tcp/${listenPort}`,
|
||||
`--nat=${nat}`,
|
||||
@ -76,10 +94,11 @@ export async function runCodex(showNavigationMenu) {
|
||||
];
|
||||
|
||||
const command =
|
||||
`${executable} ${args.join(" ")}`
|
||||
`"${executable}" ${args.join(" ")}`
|
||||
|
||||
console.log(showInfoMessage(
|
||||
'🚀 Codex node is running...\n\n' +
|
||||
'If your firewall ask, be sure to allow Codex to receive connections. \n' +
|
||||
'Please keep this terminal open. Start a new terminal to interact with the node.\n\n' +
|
||||
'Press CTRL+C to stop the node'
|
||||
));
|
||||
|
@ -9,6 +9,7 @@ import { uploadFile, downloadFile, showLocalFiles } from './handlers/fileHandler
|
||||
import { checkCodexInstallation, installCodex, uninstallCodex } from './handlers/installationHandlers.js';
|
||||
import { runCodex, checkNodeStatus } from './handlers/nodeHandlers.js';
|
||||
import { showInfoMessage } from './utils/messages.js';
|
||||
import { loadConfig } from './services/config.js';
|
||||
|
||||
async function showNavigationMenu() {
|
||||
console.log('\n')
|
||||
@ -67,9 +68,9 @@ export async function main() {
|
||||
process.on('SIGQUIT', handleExit);
|
||||
|
||||
try {
|
||||
const config = loadConfig();
|
||||
while (true) {
|
||||
console.log('\n' + chalk.cyanBright(ASCII_ART));
|
||||
|
||||
const { choice } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
@ -101,10 +102,10 @@ export async function main() {
|
||||
|
||||
switch (choice.split('.')[0]) {
|
||||
case '1':
|
||||
await checkCodexInstallation(showNavigationMenu);
|
||||
await checkCodexInstallation(config, showNavigationMenu);
|
||||
break;
|
||||
case '2':
|
||||
await runCodex(showNavigationMenu);
|
||||
await runCodex(config, showNavigationMenu);
|
||||
return;
|
||||
case '3':
|
||||
await checkNodeStatus(showNavigationMenu);
|
||||
@ -119,7 +120,7 @@ export async function main() {
|
||||
await showLocalFiles(showNavigationMenu);
|
||||
break;
|
||||
case '7':
|
||||
await uninstallCodex(showNavigationMenu);
|
||||
await uninstallCodex(config, showNavigationMenu);
|
||||
break;
|
||||
case '8':
|
||||
const { exec } = await import('child_process');
|
||||
|
46
src/services/config.js
Normal file
46
src/services/config.js
Normal file
@ -0,0 +1,46 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { getAppDataDir } from '../utils/appdata.js';
|
||||
|
||||
const defaultConfig = {
|
||||
codexExe: "",
|
||||
|
||||
// TODO:
|
||||
// Save user-selected config options. Use these when starting Codex.
|
||||
dataDir: "",
|
||||
logsDir: ""
|
||||
// storageQuota: 0,
|
||||
// ports: {
|
||||
// discPort: 8090,
|
||||
// listenPort: 8070,
|
||||
// apiPort: 8080
|
||||
// }
|
||||
};
|
||||
|
||||
function getConfigFilename() {
|
||||
return path.join(getAppDataDir(), "config.json");
|
||||
}
|
||||
|
||||
export function saveConfig(config) {
|
||||
const filePath = getConfigFilename();
|
||||
try {
|
||||
fs.writeFileSync(filePath, JSON.stringify(config));
|
||||
} catch (error) {
|
||||
console.error(`Failed to save config file to '${filePath}' error: '${error}'.`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function loadConfig() {
|
||||
const filePath = getConfigFilename();
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
saveConfig(defaultConfig);
|
||||
return defaultConfig;
|
||||
}
|
||||
return JSON.parse(fs.readFileSync(filePath));
|
||||
} catch (error) {
|
||||
console.error(`Failed to load config file from '${filePath}' error: '${error}'.`);
|
||||
throw error;
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ import axios from 'axios';
|
||||
import { runCommand } from '../utils/command.js';
|
||||
import { showErrorMessage, showInfoMessage, showSuccessMessage } from '../utils/messages.js';
|
||||
import os from 'os';
|
||||
import { getCodexVersion } from '../handlers/installationHandlers.js';
|
||||
|
||||
const platform = os.platform();
|
||||
|
||||
@ -29,10 +30,10 @@ export async function isNodeRunning() {
|
||||
}
|
||||
}
|
||||
|
||||
export async function isCodexInstalled() {
|
||||
export async function isCodexInstalled(config) {
|
||||
try {
|
||||
await runCommand('codex --version');
|
||||
return true;
|
||||
const version = await getCodexVersion(config);
|
||||
return version.length > 0;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
54
src/utils/appdata.js
Normal file
54
src/utils/appdata.js
Normal file
@ -0,0 +1,54 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
export function getAppDataDir() {
|
||||
return ensureExists(appData("codex-cli"));
|
||||
}
|
||||
|
||||
export function getCodexRootPath() {
|
||||
return ensureExists(appData("codex"));
|
||||
}
|
||||
|
||||
export function getCodexBinPath() {
|
||||
return ensureExists(path.join(appData("codex"), "bin"));
|
||||
}
|
||||
|
||||
export function getCodexDataDirDefaultPath() {
|
||||
// This path does not exist on first startup. That's good: Codex will
|
||||
// create it with the required access permissions.
|
||||
return path.join(appData("codex"), "datadir");
|
||||
}
|
||||
|
||||
export function getCodexLogsPath() {
|
||||
return ensureExists(path.join(appData("codex"), "logs"));
|
||||
}
|
||||
|
||||
function ensureExists(dir) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
function appData(...app) {
|
||||
let appData;
|
||||
if (process.platform === 'win32') {
|
||||
appData = path.join(process.env.APPDATA, ...app);
|
||||
} else if (process.platform === 'darwin') {
|
||||
appData = path.join(process.env.HOME, 'Library', 'Application Support', ...app);
|
||||
} else {
|
||||
appData = path.join(process.env.HOME, ...prependDot(...app));
|
||||
}
|
||||
return appData;
|
||||
}
|
||||
|
||||
function prependDot(...app) {
|
||||
return app.map((item, i) => {
|
||||
if (i === 0) {
|
||||
return `.${item}`;
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
169
src/utils/pathSelector.js
Normal file
169
src/utils/pathSelector.js
Normal file
@ -0,0 +1,169 @@
|
||||
import path from 'path';
|
||||
import inquirer from 'inquirer';
|
||||
import boxen from 'boxen';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs';
|
||||
|
||||
function showMsg(msg) {
|
||||
console.log(boxen(chalk.white(msg), {
|
||||
padding: 1,
|
||||
margin: 1,
|
||||
borderStyle: 'round',
|
||||
borderColor: 'white',
|
||||
titleAlignment: 'center'
|
||||
}));
|
||||
}
|
||||
|
||||
function splitPath(str) {
|
||||
return str.replaceAll("\\", "/").split("/");
|
||||
}
|
||||
|
||||
function showCurrent(currentPath) {
|
||||
const len = currentPath.length;
|
||||
showMsg(`Current path: [${len}]\n` + path.join(...currentPath));
|
||||
}
|
||||
|
||||
async function showMain(currentPath) {
|
||||
showCurrent(currentPath);
|
||||
const { choice } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'choice',
|
||||
message: 'Select an option:',
|
||||
choices: [
|
||||
'1. Enter path',
|
||||
'2. Go up one',
|
||||
'3. Go down one',
|
||||
'4. Check path exists',
|
||||
'5. Create new folder here',
|
||||
'6. Select this path',
|
||||
'7. Cancel'
|
||||
],
|
||||
pageSize: 6,
|
||||
loop: true
|
||||
}
|
||||
]).catch(() => {
|
||||
handleExit();
|
||||
return { choice: '6' };
|
||||
});
|
||||
|
||||
return choice;
|
||||
}
|
||||
|
||||
export async function selectPath() {
|
||||
var currentPath = splitPath(process.cwd());
|
||||
|
||||
while (true) {
|
||||
const choice = await showMain(currentPath);
|
||||
|
||||
switch (choice.split('.')[0]) {
|
||||
case '1':
|
||||
currentPath = await enterPath();
|
||||
break;
|
||||
case '2':
|
||||
currentPath = upOne(currentPath);
|
||||
break;
|
||||
case '3':
|
||||
currentPath = await downOne(currentPath);
|
||||
break;
|
||||
case '4':
|
||||
await checkExists(currentPath);
|
||||
break;
|
||||
case '5':
|
||||
currentPath = await createSubDir(currentPath);
|
||||
break;
|
||||
case '6':
|
||||
if (!isDir(currentPath)) {
|
||||
console.log("Current path does not exist.");
|
||||
} else {
|
||||
return currentPath;
|
||||
}
|
||||
case '7':
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function enterPath() {
|
||||
const response = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'path',
|
||||
message: 'Enter Path:'
|
||||
}]);
|
||||
|
||||
return splitPath(response.path);
|
||||
}
|
||||
|
||||
function upOne(currentPath) {
|
||||
return currentPath.slice(0, currentPath.length - 1);
|
||||
}
|
||||
|
||||
function isDir(dir) {
|
||||
return fs.lstatSync(dir).isDirectory();
|
||||
}
|
||||
|
||||
function isSubDir(currentPath, entry) {
|
||||
const newPath = path.join(...currentPath, entry);
|
||||
return isDir(newPath);
|
||||
}
|
||||
|
||||
function getSubDirOptions(currentPath) {
|
||||
const entries = fs.readdirSync(path.join(...currentPath));
|
||||
var result = [];
|
||||
var counter = 1;
|
||||
entries.forEach(function(entry) {
|
||||
if (isSubDir(currentPath, entry)) {
|
||||
result.push(counter + ". " + entry);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
async function downOne(currentPath) {
|
||||
const options = getSubDirOptions(currentPath);
|
||||
if (options.length == 0) {
|
||||
console.log("There are no subdirectories here.");
|
||||
return currentPath;
|
||||
}
|
||||
|
||||
const { choice } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'choice',
|
||||
message: 'Select an subdir:',
|
||||
choices: options,
|
||||
pageSize: options.length,
|
||||
loop: true
|
||||
}
|
||||
]).catch(() => {
|
||||
return currentPath;
|
||||
});
|
||||
|
||||
const subDir = choice.slice(3);
|
||||
return [...currentPath, subDir];
|
||||
}
|
||||
|
||||
async function checkExists(currentPath) {
|
||||
if (!isDir(path.join(...currentPath))) {
|
||||
console.log("Current path does not exist.");
|
||||
} else{
|
||||
console.log("Current path exists.");
|
||||
}
|
||||
}
|
||||
|
||||
async function createSubDir(currentPath) {
|
||||
const response = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'name',
|
||||
message: 'Enter name:'
|
||||
}]);
|
||||
|
||||
const name = response.name;
|
||||
if (name.length < 1) return;
|
||||
|
||||
const fullDir = path.join(...currentPath, name);
|
||||
fs.mkdirSync(fullDir);
|
||||
return [...currentPath, name];
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user