Merge branch 'master' into feature/upload-download

# Conflicts:
#	src/handlers/installationHandlers.js
#	src/handlers/nodeHandlers.js
This commit is contained in:
Ben 2025-07-02 09:20:22 +02:00
commit b3c7a62a3f
No known key found for this signature in database
GPG Key ID: 0F16E812E736C24B
2 changed files with 418 additions and 351 deletions

View File

@ -115,6 +115,46 @@ async function clearCodexExePathFromConfig(config) {
saveConfig(config); saveConfig(config);
} }
async function configureShellPath(installPath) {
try {
const homedir = os.homedir();
let shellConfigFile;
let exportLine = `export PATH="${installPath}:$PATH"`;
if (platform === 'win32') {
// For Windows, update the User PATH environment variable
const currentPath = process.env.PATH || '';
if (!currentPath.includes(installPath)) {
await runCommand(`setx PATH "${installPath};%PATH%"`);
}
return true;
} else {
// For Unix-like systems (macOS and Linux)
if (platform === 'darwin') {
// Check for zsh first (default on modern macOS)
if (fs.existsSync(path.join(homedir, '.zshrc'))) {
shellConfigFile = path.join(homedir, '.zshrc');
} else {
shellConfigFile = path.join(homedir, '.bash_profile');
}
} else {
// Linux
shellConfigFile = path.join(homedir, '.bashrc');
}
// Check if PATH is already configured
const fileContent = fs.existsSync(shellConfigFile) ? fs.readFileSync(shellConfigFile, 'utf8') : '';
if (!fileContent.includes(installPath)) {
fs.appendFileSync(shellConfigFile, `\n${exportLine}\n`);
}
return true;
}
} catch (error) {
console.log(showErrorMessage(`Failed to configure PATH: ${error.message}`));
return false;
}
}
async function performInstall(config) { async function performInstall(config) {
const agreed = await showPrivacyDisclaimer(); const agreed = await showPrivacyDisclaimer();
if (!agreed) { if (!agreed) {
@ -187,32 +227,112 @@ async function performInstall(config) {
'`; '`;
await runCommand(timeoutCommand); await runCommand(timeoutCommand);
} else { } else {
await runCommand( await runCommand(`INSTALL_DIR="${installPath}" timeout 120 bash install.sh`);
`INSTALL_DIR="${installPath}" timeout 120 bash install.sh`,
);
} }
await saveCodexExePath(config, path.join(installPath, "codex")); await saveCodexExePath(config, path.join(installPath, "codex"));
} catch (error) { } catch (error) {
if ( if (error.message.includes('ECONNREFUSED') || error.message.includes('ETIMEDOUT')) {
error.message.includes("ECONNREFUSED") || throw new Error('Installation failed. Please check your internet connection and try again.');
error.message.includes("ETIMEDOUT") } else if (error.message.includes('Permission denied')) {
) { throw new Error('Permission denied. Please try running the command with sudo.');
throw new Error( } else if (error.message.includes('timeout')) {
"Installation failed. Please check your internet connection and try again.", throw new Error('Installation is taking longer than expected. Please try running with sudo.');
);
} else if (error.message.includes("Permission denied")) {
throw new Error(
"Permission denied. Please try running the command with sudo.",
);
} else if (error.message.includes("timeout")) {
throw new Error(
"Installation is taking longer than expected. Please try running with sudo.",
);
} else { } else {
throw new Error( throw new Error('Installation failed. Please try running with sudo if you haven\'t already.');
"Installation failed. Please try running with sudo if you haven't already.", }
); } finally {
await runCommand('rm -f install.sh').catch(() => {});
}
}
try {
const version = await getCodexVersion(config);
console.log(chalk.green(version));
// Configure shell PATH
const pathConfigured = await configureShellPath(installPath);
spinner.success();
if (pathConfigured) {
console.log(boxen(
chalk.green('Codex is installed successfully!\n\n') +
chalk.cyan('Current configuration:\n') +
`${chalk.white('Data path')} = ${config.dataDir}\n` +
`${chalk.white('Logs path')} = ${config.logsDir}\n` +
`${chalk.white('Storage quota')} = ${config.storageQuota} Bytes\n` +
`${chalk.white('Discovery port')} = ${config.ports.discPort}\n` +
`${chalk.white('P2P Listen port')} = ${config.ports.listenPort}\n` +
`${chalk.white('API Port')} = ${config.ports.apiPort}\n\n` +
chalk.gray('You can modify the above configuration from the main menu.'),
{
padding: 1,
margin: 1,
borderStyle: 'round',
borderColor: 'green',
title: '✅ Installation Complete',
titleAlignment: 'center'
}
));
const { choice } = await inquirer.prompt([
{
type: 'list',
name: 'choice',
message: 'What would you like to do?',
choices: [
'1. Return to main menu',
'2. Exit',
],
pageSize: 2,
loop: true
}
]);
switch (choice.split('.')[0].trim()) {
case '1':
await showNavigationMenu();
break;
case '2':
process.exit(0);
}
} else {
console.log(showInfoMessage(
'Codex is installed but PATH configuration failed.\n' +
`Please manually add "${installPath}" to your PATH.\n`
));
}
} catch (error) {
throw new Error('Installation completed but Codex command is not available. Please restart your terminal and try again.');
}
console.log(showInfoMessage(
"Please review the configuration before starting Codex."
));
return true;
} catch (error) {
spinner.error();
console.log(showErrorMessage(`Failed to install Codex: ${error.message}`));
return false;
}
}
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. \n' +
'All data stored in the local Codex node will be deleted as well.'
),
default: false
} }
} finally { } finally {
await runCommand("rm -f install.sh").catch(() => {}); await runCommand("rm -f install.sh").catch(() => {});

View File

@ -1,47 +1,35 @@
import path from "path"; import path from 'path';
import { createSpinner } from "nanospinner"; import { createSpinner } from 'nanospinner';
import { runCommand } from "../utils/command.js"; import { runCommand } from '../utils/command.js';
import { import { showErrorMessage, showInfoMessage, showSuccessMessage } from '../utils/messages.js';
showErrorMessage, import { isNodeRunning, isCodexInstalled, startPeriodicLogging, getWalletAddress, setWalletAddress } from '../services/nodeService.js';
showInfoMessage, import inquirer from 'inquirer';
showSuccessMessage, import boxen from 'boxen';
} from "../utils/messages.js"; import chalk from 'chalk';
import { import os from 'os';
isNodeRunning, import { exec } from 'child_process';
isCodexInstalled, import axios from 'axios';
startPeriodicLogging,
getWalletAddress,
setWalletAddress,
} from "../services/nodeService.js";
import inquirer from "inquirer";
import boxen from "boxen";
import chalk from "chalk";
import os from "os";
import { exec } from "child_process";
import axios from "axios";
const platform = os.platform(); const platform = os.platform();
async function promptForWalletAddress() { async function promptForWalletAddress() {
const { wallet } = await inquirer.prompt([ const { wallet } = await inquirer.prompt([
{ {
type: "input", type: 'input',
name: "wallet", name: 'wallet',
message: message: 'Please enter your ERC20 wallet address (or press enter to skip):',
"Please enter your ERC20 wallet address (or press enter to skip):",
validate: (input) => { validate: (input) => {
if (!input) return true; // Allow empty input if (!input) return true; // Allow empty input
if (/^0x[a-fA-F0-9]{40}$/.test(input)) return true; if (/^0x[a-fA-F0-9]{40}$/.test(input)) return true;
return "Please enter a valid ERC20 wallet address (0x followed by 40 hexadecimal characters) or press enter to skip"; return 'Please enter a valid ERC20 wallet address (0x followed by 40 hexadecimal characters) or press enter to skip';
}, }
}, }
]); ]);
return wallet || null; return wallet || null;
} }
function getCurrentLogFile(config) { function getCurrentLogFile(config) {
const timestamp = new Date() const timestamp = new Date().toISOString()
.toISOString()
.replaceAll(":", "-") .replaceAll(":", "-")
.replaceAll(".", "-"); .replaceAll(".", "-");
return path.join(config.logsDir, `codex_${timestamp}.log`); return path.join(config.logsDir, `codex_${timestamp}.log`);
@ -50,11 +38,7 @@ function getCurrentLogFile(config) {
export async function runCodex(config, showNavigationMenu) { export async function runCodex(config, showNavigationMenu) {
const isInstalled = await isCodexInstalled(config); const isInstalled = await isCodexInstalled(config);
if (!isInstalled) { if (!isInstalled) {
console.log( console.log(showErrorMessage('Codex is not installed. Please install Codex first using option 1 from the main menu.'));
showErrorMessage(
"Codex is not installed. Please install Codex first using option 1 from the main menu.",
),
);
await showNavigationMenu(); await showNavigationMenu();
return; return;
} }
@ -62,135 +46,118 @@ export async function runCodex(config, showNavigationMenu) {
const nodeAlreadyRunning = await isNodeRunning(config); const nodeAlreadyRunning = await isNodeRunning(config);
if (nodeAlreadyRunning) { if (nodeAlreadyRunning) {
console.log(showInfoMessage("A Codex node is already running.")); console.log(showInfoMessage('A Codex node is already running.'));
await showNavigationMenu(); await showNavigationMenu();
} else { } else {
try { try {
let nat; let nat;
if (platform === "win32") { if (platform === 'win32') {
const result = await runCommand( const result = await runCommand('for /f "delims=" %a in (\'curl -s --ssl-reqd ip.codex.storage\') do @echo %a');
"for /f \"delims=\" %a in ('curl -s --ssl-reqd ip.codex.storage') do @echo %a",
);
nat = result.trim(); nat = result.trim();
} else { } else {
nat = await runCommand("curl -s https://ip.codex.storage"); nat = await runCommand('curl -s https://ip.codex.storage');
} }
if (config.dataDir.length < 1) throw new Error("Missing config: dataDir"); if (config.dataDir.length < 1) throw new Error("Missing config: dataDir");
if (config.logsDir.length < 1) throw new Error("Missing config: logsDir"); if (config.logsDir.length < 1) throw new Error("Missing config: logsDir");
const logFilePath = getCurrentLogFile(config); const logFilePath = getCurrentLogFile(config);
console.log( console.log(showInfoMessage(
showInfoMessage(
`Data location: ${config.dataDir}\n` + `Data location: ${config.dataDir}\n` +
`Logs: ${logFilePath}\n` + `Logs: ${logFilePath}\n` +
`API port: ${config.ports.apiPort}`, `API port: ${config.ports.apiPort}`
), ));
);
const executable = config.codexExe; const executable = config.codexExe;
const args = [ const args = [
`--data-dir="${config.dataDir}"`, `--data-dir="${config.dataDir}"`,
`--api-cors-origin="*"`,
`--storage-quota=11811160064`,
`--bootstrap-node=spr:CiUIAhIhAiJvIcA_ZwPZ9ugVKDbmqwhJZaig5zKyLiuaicRcCGqLEgIDARo8CicAJQgCEiECIm8hwD9nA9n26BUoNuarCEllqKDnMrIuK5qJxFwIaosQ3d6esAYaCwoJBJ_f8zKRAnU6KkYwRAIgM0MvWNJL296kJ9gWvfatfmVvT-A7O2s8Mxp8l9c8EW0CIC-h-H-jBVSgFjg3Eny2u33qF7BDnWFzo7fGfZ7_qc9P`,
`--bootstrap-node=spr:CiUIAhIhAyUvcPkKoGE7-gh84RmKIPHJPdsX5Ugm_IHVJgF-Mmu_EgIDARo8CicAJQgCEiEDJS9w-QqgYTv6CHzhGYog8ck92xflSCb8gdUmAX4ya78QoemesAYaCwoJBES39Q2RAnVOKkYwRAIgLi3rouyaZFS_Uilx8k99ySdQCP1tsmLR21tDb9p8LcgCIG30o5YnEooQ1n6tgm9fCT7s53k6XlxyeSkD_uIO9mb3`,
`--bootstrap-node=spr:CiUIAhIhA7E4DEMer8nUOIUSaNPA4z6x0n9Xaknd28Cfw9S2-cCeEgIDARo8CicAJQgCEiEDsTgMQx6vydQ4hRJo08DjPrHSf1dqSd3bwJ_D1Lb5wJ4Qt_CesAYaCwoJBEDhWZORAnVYKkYwRAIgFNzhnftocLlVHJl1onuhbSUM7MysXPV6dawHAA0DZNsCIDRVu9gnPTH5UkcRXLtt7MLHCo4-DL-RCMyTcMxYBXL0`,
`--bootstrap-node=spr:CiUIAhIhAzZn3JmJab46BNjadVnLNQKbhnN3eYxwqpteKYY32SbOEgIDARo8CicAJQgCEiEDNmfcmYlpvjoE2Np1Wcs1ApuGc3d5jHCqm14phjfZJs4QrvWesAYaCwoJBKpA-TaRAnViKkcwRQIhANuMmZDD2c25xzTbKSirEpkZYoxbq-FU_lpI0K0e4mIVAiBfQX4yR47h1LCnHznXgDs6xx5DLO5q3lUcicqUeaqGeg`,
`--bootstrap-node=spr:CiUIAhIhAgybmRwboqDdUJjeZrzh43sn5mp8jt6ENIb08tLn4x01EgIDARo8CicAJQgCEiECDJuZHBuioN1QmN5mvOHjeyfmanyO3oQ0hvTy0ufjHTUQh4ifsAYaCwoJBI_0zSiRAnVsKkcwRQIhAJCb_z0E3RsnQrEePdJzMSQrmn_ooHv6mbw1DOh5IbVNAiBbBJrWR8eBV6ftzMd6ofa5khNA2h88OBhMqHCIzSjCeA`,
`--bootstrap-node=spr:CiUIAhIhAntGLadpfuBCD9XXfiN_43-V3L5VWgFCXxg4a8uhDdnYEgIDARo8CicAJQgCEiECe0Ytp2l-4EIP1dd-I3_jf5XcvlVaAUJfGDhry6EN2dgQsIufsAYaCwoJBNEmoCiRAnV2KkYwRAIgXO3bzd5VF8jLZG8r7dcLJ_FnQBYp1BcxrOvovEa40acCIDhQ14eJRoPwJ6GKgqOkXdaFAsoszl-HIRzYcXKeb7D9`,
`--log-level=DEBUG`, `--log-level=DEBUG`,
`--log-file="${logFilePath}"`, `--log-file="${logFilePath}"`,
`--storage-quota="${config.storageQuota}"`, `--storage-quota="${config.storageQuota}"`,
`--disc-port=${config.ports.discPort}`, `--disc-port=${config.ports.discPort}`,
`--listen-addrs=/ip4/0.0.0.0/tcp/${config.ports.listenPort}`, `--listen-addrs=/ip4/0.0.0.0/tcp/${config.ports.listenPort}`,
`--api-port=${config.ports.apiPort}`, `--api-port=${config.ports.apiPort}`,
`--nat=${nat}`, `--nat=extip:${nat}`,
`--api-cors-origin="*"`, `--bootstrap-node=spr:CiUIAhIhAiJvIcA_ZwPZ9ugVKDbmqwhJZaig5zKyLiuaicRcCGqLEgIDARo8CicAJQgCEiECIm8hwD9nA9n26BUoNuarCEllqKDnMrIuK5qJxFwIaosQ3d6esAYaCwoJBJ_f8zKRAnU6KkYwRAIgM0MvWNJL296kJ9gWvfatfmVvT-A7O2s8Mxp8l9c8EW0CIC-h-H-jBVSgFjg3Eny2u33qF7BDnWFzo7fGfZ7_qc9P`
`--bootstrap-node=spr:CiUIAhIhAiJvIcA_ZwPZ9ugVKDbmqwhJZaig5zKyLiuaicRcCGqLEgIDARo8CicAJQgCEiECIm8hwD9nA9n26BUoNuarCEllqKDnMrIuK5qJxFwIaosQ3d6esAYaCwoJBJ_f8zKRAnU6KkYwRAIgM0MvWNJL296kJ9gWvfatfmVvT-A7O2s8Mxp8l9c8EW0CIC-h-H-jBVSgFjg3Eny2u33qF7BDnWFzo7fGfZ7_qc9P`,
]; ];
const command = `"${executable}" ${args.join(" ")}`; const command =
`"${executable}" ${args.join(" ")}`
console.log( console.log(showInfoMessage(
showInfoMessage( '🚀 Codex node is running...\n\n' +
"🚀 Codex node is running...\n\n" + 'If your firewall ask, be sure to allow Codex to receive connections. \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' +
"Please keep this terminal open. Start a new terminal to interact with the node.\n\n" + 'Press CTRL+C to stop the node'
"Press CTRL+C to stop the node", ));
),
);
const nodeProcess = exec(command); const nodeProcess = exec(command);
await new Promise((resolve) => setTimeout(resolve, 5000)); await new Promise(resolve => setTimeout(resolve, 5000));
try { try {
const response = await axios.get( const response = await axios.get(`http://localhost:${config.ports.apiPort}/api/codex/v1/debug/info`);
`http://localhost:${config.ports.apiPort}/api/codex/v1/debug/info`,
);
if (response.status === 200) { if (response.status === 200) {
// Check if wallet exists // Check if wallet exists
try { try {
const existingWallet = await getWalletAddress(); const existingWallet = await getWalletAddress();
if (!existingWallet) { if (!existingWallet) {
console.log( console.log(showInfoMessage('[OPTIONAL] Please provide your ERC20 wallet address.'));
showInfoMessage(
"[OPTIONAL] Please provide your ERC20 wallet address.",
),
);
const wallet = await promptForWalletAddress(); const wallet = await promptForWalletAddress();
if (wallet) { if (wallet) {
await setWalletAddress(wallet); await setWalletAddress(wallet);
console.log( console.log(showSuccessMessage('Wallet address saved successfully!'));
showSuccessMessage("Wallet address saved successfully!"),
);
} }
} }
} catch (error) { } catch (error) {
console.log( console.log(showErrorMessage('Failed to process wallet address. Continuing without wallet update.'));
showErrorMessage(
"Failed to process wallet address. Continuing without wallet update.",
),
);
} }
// Start periodic logging // Start periodic logging
const stopLogging = await startPeriodicLogging(config); const stopLogging = await startPeriodicLogging(config);
nodeProcess.on("exit", () => { nodeProcess.on('exit', () => {
stopLogging(); stopLogging();
}); });
console.log( console.log(boxen(
boxen( chalk.cyan('We are logging some of your node\'s public data for improving the Codex experience'),
chalk.cyan(
"We are logging some of your node's public data for improving the Codex experience",
),
{ {
padding: 1, padding: 1,
margin: 1, margin: 1,
borderStyle: "round", borderStyle: 'round',
borderColor: "cyan", borderColor: 'cyan',
title: "🔒 Privacy Notice", title: '🔒 Privacy Notice',
titleAlignment: "center", titleAlignment: 'center',
dimBorder: true, dimBorder: true
}, }
), ));
);
} }
} catch (error) { } catch (error) {
// Silently handle any logging errors // Silently handle any logging errors
} }
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
nodeProcess.on("exit", (code) => { nodeProcess.on('exit', (code) => {
if (code === 0) resolve(); if (code === 0) resolve();
else reject(new Error(`Node exited with code ${code}`)); else reject(new Error(`Node exited with code ${code}`));
}); });
}); });
if (platform === "win32") { if (platform === 'win32') {
console.log(showInfoMessage("Cleaning up firewall rules...")); console.log(showInfoMessage('Cleaning up firewall rules...'));
await runCommand( await runCommand('netsh advfirewall firewall delete rule name="Allow Codex (TCP-In)"');
'netsh advfirewall firewall delete rule name="Allow Codex (TCP-In)"', await runCommand('netsh advfirewall firewall delete rule name="Allow Codex (UDP-In)"');
);
await runCommand(
'netsh advfirewall firewall delete rule name="Allow Codex (UDP-In)"',
);
} }
} catch (error) { } catch (error) {
console.log(showErrorMessage(`Failed to run Codex: ${error.message}`)); console.log(showErrorMessage(`Failed to run Codex: ${error.message}`));
} }
@ -201,100 +168,90 @@ export async function runCodex(config, showNavigationMenu) {
async function showNodeDetails(data, showNavigationMenu) { async function showNodeDetails(data, showNavigationMenu) {
const { choice } = await inquirer.prompt([ const { choice } = await inquirer.prompt([
{ {
type: "list", type: 'list',
name: "choice", name: 'choice',
message: "Select information to view:", message: 'Select information to view:',
choices: [ choices: [
"1. View Connected Peers", '1. View Connected Peers',
"2. View Node Information", '2. View Node Information',
"3. Update Wallet Address", '3. Update Wallet Address',
"4. Back to Main Menu", '4. Back to Main Menu',
"5. Exit", '5. Exit'
], ],
pageSize: 5, pageSize: 5,
loop: true, loop: true
}, }
]); ]);
switch (choice.split(".")[0].trim()) { switch (choice.split('.')[0].trim()) {
case "1": case '1':
const peerCount = data.table.nodes.length; const peerCount = data.table.nodes.length;
if (peerCount > 0) { if (peerCount > 0) {
console.log(showInfoMessage("Connected Peers")); console.log(showInfoMessage('Connected Peers'));
data.table.nodes.forEach((node, index) => { data.table.nodes.forEach((node, index) => {
console.log( console.log(boxen(
boxen(
`Peer ${index + 1}:\n` + `Peer ${index + 1}:\n` +
`${chalk.cyan("Peer ID:")} ${node.peerId}\n` + `${chalk.cyan('Peer ID:')} ${node.peerId}\n` +
`${chalk.cyan("Address:")} ${node.address}\n` + `${chalk.cyan('Address:')} ${node.address}\n` +
`${chalk.cyan("Status:")} ${node.seen ? chalk.green("Active") : chalk.gray("Inactive")}`, `${chalk.cyan('Status:')} ${node.seen ? chalk.green('Active') : chalk.gray('Inactive')}`,
{ {
padding: 1, padding: 1,
margin: 1, margin: 1,
borderStyle: "round", borderStyle: 'round',
borderColor: "blue", borderColor: 'blue'
}, }
), ));
);
}); });
} else { } else {
console.log(showInfoMessage("No connected peers found.")); console.log(showInfoMessage('No connected peers found.'));
} }
return showNodeDetails(data, showNavigationMenu); return showNodeDetails(data, showNavigationMenu);
case "2": case '2':
console.log( console.log(boxen(
boxen( `${chalk.cyan('Version:')} ${data.codex.version}\n` +
`${chalk.cyan("Version:")} ${data.codex.version}\n` + `${chalk.cyan('Revision:')} ${data.codex.revision}\n\n` +
`${chalk.cyan("Revision:")} ${data.codex.revision}\n\n` + `${chalk.cyan('Node ID:')} ${data.table.localNode.nodeId}\n` +
`${chalk.cyan("Node ID:")} ${data.table.localNode.nodeId}\n` + `${chalk.cyan('Peer ID:')} ${data.table.localNode.peerId}\n` +
`${chalk.cyan("Peer ID:")} ${data.table.localNode.peerId}\n` + `${chalk.cyan('Listening Address:')} ${data.table.localNode.address}\n\n` +
`${chalk.cyan("Listening Address:")} ${data.table.localNode.address}\n\n` + `${chalk.cyan('Public IP:')} ${data.announceAddresses[0].split('/')[2]}\n` +
`${chalk.cyan("Public IP:")} ${data.announceAddresses[0].split("/")[2]}\n` + `${chalk.cyan('Port:')} ${data.announceAddresses[0].split('/')[4]}\n` +
`${chalk.cyan("Port:")} ${data.announceAddresses[0].split("/")[4]}\n` + `${chalk.cyan('Connected Peers:')} ${data.table.nodes.length}`,
`${chalk.cyan("Connected Peers:")} ${data.table.nodes.length}`,
{ {
padding: 1, padding: 1,
margin: 1, margin: 1,
borderStyle: "round", borderStyle: 'round',
borderColor: "yellow", borderColor: 'yellow',
title: "📊 Node Information", title: '📊 Node Information',
titleAlignment: "center", titleAlignment: 'center'
}, }
), ));
);
return showNodeDetails(data, showNavigationMenu); return showNodeDetails(data, showNavigationMenu);
case "3": case '3':
try { try {
const existingWallet = await getWalletAddress(); const existingWallet = await getWalletAddress();
console.log( console.log(boxen(
boxen( `${chalk.cyan('Current wallet address:')}\n${existingWallet || 'Not set'}`,
`${chalk.cyan("Current wallet address:")}\n${existingWallet || "Not set"}`,
{ {
padding: 1, padding: 1,
margin: 1, margin: 1,
borderStyle: "round", borderStyle: 'round',
borderColor: "blue", borderColor: 'blue'
}, }
), ));
);
const wallet = await promptForWalletAddress(); const wallet = await promptForWalletAddress();
if (wallet) { if (wallet) {
await setWalletAddress(wallet); await setWalletAddress(wallet);
console.log( console.log(showSuccessMessage('Wallet address updated successfully!'));
showSuccessMessage("Wallet address updated successfully!"),
);
} }
} catch (error) { } catch (error) {
console.log( console.log(showErrorMessage(`Failed to update wallet address: ${error.message}`));
showErrorMessage(`Failed to update wallet address: ${error.message}`),
);
} }
return showNodeDetails(data, showNavigationMenu); return showNodeDetails(data, showNavigationMenu);
case "4": case '4':
return showNavigationMenu(); return showNavigationMenu();
case "5": case '5':
process.exit(0); process.exit(0);
} }
} }
@ -304,10 +261,8 @@ export async function checkNodeStatus(config, showNavigationMenu) {
const nodeRunning = await isNodeRunning(config); const nodeRunning = await isNodeRunning(config);
if (nodeRunning) { if (nodeRunning) {
const spinner = createSpinner("Checking node status...").start(); const spinner = createSpinner('Checking node status...').start();
const response = await runCommand( const response = await runCommand(`curl http://localhost:${config.ports.apiPort}/api/codex/v1/debug/info`);
`curl http://localhost:${config.ports.apiPort}/api/codex/v1/debug/info`,
);
spinner.success(); spinner.success();
const data = JSON.parse(response); const data = JSON.parse(response);
@ -315,35 +270,27 @@ export async function checkNodeStatus(config, showNavigationMenu) {
const peerCount = data.table.nodes.length; const peerCount = data.table.nodes.length;
const isOnline = peerCount > 2; const isOnline = peerCount > 2;
console.log( console.log(boxen(
boxen(
isOnline isOnline
? chalk.green("Node is ONLINE & DISCOVERABLE") ? chalk.green('Node is ONLINE & DISCOVERABLE')
: chalk.yellow("Node is ONLINE but has few peers"), : chalk.yellow('Node is ONLINE but has few peers'),
{ {
padding: 1, padding: 1,
margin: 1, margin: 1,
borderStyle: "round", borderStyle: 'round',
borderColor: isOnline ? "green" : "yellow", borderColor: isOnline ? 'green' : 'yellow',
title: "🔌 Node Status", title: '🔌 Node Status',
titleAlignment: "center", titleAlignment: 'center'
}, }
), ));
);
await showNodeDetails(data, showNavigationMenu); await showNodeDetails(data, showNavigationMenu);
} else { } else {
console.log( console.log(showErrorMessage('Codex node is not running. Try again after starting the node'));
showErrorMessage(
"Codex node is not running. Try again after starting the node",
),
);
await showNavigationMenu(); await showNavigationMenu();
} }
} catch (error) { } catch (error) {
console.log( console.log(showErrorMessage(`Failed to check node status: ${error.message}`));
showErrorMessage(`Failed to check node status: ${error.message}`),
);
await showNavigationMenu(); await showNavigationMenu();
} }
} }