mirror of
https://github.com/hexastack/hexabot
synced 2024-11-23 01:55:15 +00:00
feat: move cli to it's own repo
This commit is contained in:
parent
7584c1dc76
commit
8aa1ea91a3
2
cli/.gitignore
vendored
2
cli/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
node_modules
|
||||
dist
|
@ -1,35 +0,0 @@
|
||||
{
|
||||
"name": "hexabot-cli",
|
||||
"version": "2.0.0",
|
||||
"description": "Hexabot CLI for creating and managing chatbots built with Hexabot.",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"hexabot": "dist/index.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node dist/cli.js",
|
||||
"dev": "ts-node src/cli.ts",
|
||||
"prepare": "npm run build"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Hexastack",
|
||||
"license": "AGPL-3.0-only",
|
||||
"dependencies": {
|
||||
"chalk": "^5.3.0",
|
||||
"commander": "^12.1.0",
|
||||
"degit": "^2.8.4",
|
||||
"dotenv": "^16.4.5",
|
||||
"figlet": "^1.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chalk": "^2.2.0",
|
||||
"@types/commander": "^2.12.2",
|
||||
"@types/degit": "^2.8.6",
|
||||
"@types/figlet": "^1.5.8",
|
||||
"@types/node": "^22.7.4",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.6.2"
|
||||
}
|
||||
}
|
235
cli/src/index.ts
235
cli/src/index.ts
@ -1,235 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import chalk from 'chalk';
|
||||
import { execSync } from 'child_process';
|
||||
import { Command } from 'commander';
|
||||
import figlet from 'figlet';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
console.log(figlet.textSync('Hexabot'));
|
||||
|
||||
// Configuration
|
||||
const FOLDER = path.resolve(process.cwd(), './docker');
|
||||
|
||||
/**
|
||||
* Check if the docker folder exists, otherwise prompt the user to cd into the correct folder.
|
||||
*/
|
||||
const checkDockerFolder = (): void => {
|
||||
if (!fs.existsSync(FOLDER)) {
|
||||
console.error(
|
||||
chalk.red(
|
||||
`Error: The 'docker' folder is not found in the current directory.`,
|
||||
),
|
||||
);
|
||||
console.error(
|
||||
chalk.yellow(
|
||||
`Please make sure you're in the Hexabot project directory and try again.`,
|
||||
),
|
||||
);
|
||||
console.log(chalk.cyan(`Example: cd path/to/hexabot`));
|
||||
process.exit(1); // Exit the script if the folder is not found
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize Commander
|
||||
const program = new Command();
|
||||
|
||||
// Helper Functions
|
||||
|
||||
/**
|
||||
* Generate Docker Compose file arguments based on provided services.
|
||||
* @param services List of services
|
||||
* @param type Optional type ('dev' | 'prod')
|
||||
* @returns String of Docker Compose file arguments
|
||||
*/
|
||||
const generateComposeFiles = (
|
||||
services: string[],
|
||||
type?: 'dev' | 'prod',
|
||||
): string => {
|
||||
let files = [`-f ${path.join(FOLDER, 'docker-compose.yml')}`];
|
||||
|
||||
services.forEach((service) => {
|
||||
files.push(`-f ${path.join(FOLDER, `docker-compose.${service}.yml`)}`);
|
||||
if (type) {
|
||||
const serviceTypeFile = path.join(
|
||||
FOLDER,
|
||||
`docker-compose.${service}.${type}.yml`,
|
||||
);
|
||||
if (fs.existsSync(serviceTypeFile)) {
|
||||
files.push(`-f ${serviceTypeFile}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (type) {
|
||||
const mainTypeFile = path.join(FOLDER, `docker-compose.${type}.yml`);
|
||||
if (fs.existsSync(mainTypeFile)) {
|
||||
files.push(`-f ${mainTypeFile}`);
|
||||
}
|
||||
}
|
||||
|
||||
return files.join(' ');
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute a Docker Compose command.
|
||||
* @param args Additional arguments for the docker compose command
|
||||
*/
|
||||
const dockerCompose = (args: string): void => {
|
||||
try {
|
||||
execSync(`docker compose ${args}`, { stdio: 'inherit' });
|
||||
} catch (error) {
|
||||
console.error(chalk.red('Error executing Docker Compose command.'));
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute a Docker Exec command.
|
||||
* @param container Container for the docker exec command
|
||||
* @param options Additional options for the docker exec command
|
||||
* @param command Command to be executed within the container
|
||||
*/
|
||||
const dockerExec = (
|
||||
container: string,
|
||||
command: string,
|
||||
options?: string,
|
||||
): void => {
|
||||
try {
|
||||
execSync(`docker exec -it ${options} ${container} ${command}`, {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(chalk.red('Error executing Docker Exec command.'));
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the comma-separated service list.
|
||||
* @param serviceString Comma-separated list of services
|
||||
* @returns Array of services
|
||||
*/
|
||||
const parseServices = (serviceString: string): string[] => {
|
||||
return serviceString
|
||||
.split(',')
|
||||
.map((service) => service.trim())
|
||||
.filter((s) => s);
|
||||
};
|
||||
|
||||
// Check if the docker folder exists
|
||||
checkDockerFolder();
|
||||
|
||||
// Commands
|
||||
|
||||
program
|
||||
.name('Hexabot')
|
||||
.description('A CLI to manage your Hexabot chatbot instance')
|
||||
.version('1.0.0');
|
||||
|
||||
program
|
||||
.command('init')
|
||||
.description('Initialize the environment by copying .env.example to .env')
|
||||
.action(() => {
|
||||
const envPath = path.join(FOLDER, '.env');
|
||||
const exampleEnvPath = path.join(FOLDER, '.env.example');
|
||||
|
||||
if (fs.existsSync(envPath)) {
|
||||
console.log(chalk.yellow('.env file already exists.'));
|
||||
} else {
|
||||
fs.copyFileSync(exampleEnvPath, envPath);
|
||||
console.log(chalk.green('Copied .env.example to .env'));
|
||||
}
|
||||
});
|
||||
|
||||
program
|
||||
.command('start')
|
||||
.description('Start specified services with Docker Compose')
|
||||
.option(
|
||||
'--enable <services>',
|
||||
'Comma-separated list of services to enable',
|
||||
'',
|
||||
)
|
||||
.action((options) => {
|
||||
const services = parseServices(options.enable);
|
||||
const composeArgs = generateComposeFiles(services);
|
||||
dockerCompose(`${composeArgs} up -d`);
|
||||
});
|
||||
|
||||
program
|
||||
.command('dev')
|
||||
.description(
|
||||
'Start specified services in development mode with Docker Compose',
|
||||
)
|
||||
.option(
|
||||
'--enable <services>',
|
||||
'Comma-separated list of services to enable',
|
||||
'',
|
||||
)
|
||||
.action((options) => {
|
||||
const services = parseServices(options.enable);
|
||||
const composeArgs = generateComposeFiles(services, 'dev');
|
||||
dockerCompose(`${composeArgs} up --build -d`);
|
||||
});
|
||||
|
||||
program
|
||||
.command('migrate [args...]')
|
||||
.description('Run database migrations')
|
||||
.action((args) => {
|
||||
const migrateArgs = args.join(' ');
|
||||
dockerExec(
|
||||
'api',
|
||||
`npm run migrate ${migrateArgs}`,
|
||||
'--user $(id -u):$(id -g)',
|
||||
);
|
||||
});
|
||||
|
||||
program
|
||||
.command('start-prod')
|
||||
.description(
|
||||
'Start specified services in production mode with Docker Compose',
|
||||
)
|
||||
.option(
|
||||
'--enable <services>',
|
||||
'Comma-separated list of services to enable',
|
||||
'',
|
||||
)
|
||||
.action((options) => {
|
||||
const services = parseServices(options.enable);
|
||||
const composeArgs = generateComposeFiles(services, 'prod');
|
||||
dockerCompose(`${composeArgs} up -d`);
|
||||
});
|
||||
|
||||
program
|
||||
.command('stop')
|
||||
.description('Stop specified Docker Compose services')
|
||||
.option('--enable <services>', 'Comma-separated list of services to stop', '')
|
||||
.action((options) => {
|
||||
const services = parseServices(options.enable);
|
||||
const composeArgs = generateComposeFiles(services);
|
||||
dockerCompose(`${composeArgs} down`);
|
||||
});
|
||||
|
||||
program
|
||||
.command('destroy')
|
||||
.description('Destroy specified Docker Compose services and remove volumes')
|
||||
.option(
|
||||
'--enable <services>',
|
||||
'Comma-separated list of services to destroy',
|
||||
'',
|
||||
)
|
||||
.action((options) => {
|
||||
const services = parseServices(options.enable);
|
||||
const composeArgs = generateComposeFiles(services);
|
||||
dockerCompose(`${composeArgs} down -v`);
|
||||
});
|
||||
|
||||
// Parse arguments
|
||||
program.parse(process.argv);
|
||||
|
||||
// If no command is provided, display help
|
||||
if (!process.argv.slice(2).length) {
|
||||
program.outputHelp();
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ES2020", // Change to ES2020 or ESNext
|
||||
"moduleResolution": "node", // Ensure module resolution is node
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
Loading…
Reference in New Issue
Block a user