Merge pull request #279 from Hexastack/feat/cli-create-project

Feat/cli  Move CLI to it's own repo
This commit is contained in:
Med Marrouchi 2024-10-27 22:32:10 +01:00 committed by GitHub
commit 2bf0135ca3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 79 additions and 335 deletions

127
README.md
View File

@ -58,84 +58,73 @@
- **nlu:** The NLU Engine built with Python, enabling intent recognition and language detection through machine learning models.
- **docker:** A set of Docker Compose files for deploying the entire solution, making it easy to run Hexabot in any environment.
## Prerequisites
## Getting Started
To ensure Hexabot runs smoothly, you'll need the following:
### Prerequisites
- **Docker:** We recommend using Docker to start the app since multiple services are required (MongoDB, Redis, Prometheus, etc.). All the necessary Docker Compose files are located in the docker folder.
- **Node.js:** For development purposes, ensure you have Node.js >= v18.17.0 installed. We recommend using nvm (Node Version Manager) to easily manage and update your Node.js versions.
- Node.js >= 18.17.0
- npm (Node Package Manager)
- Docker installed
## Installation
### Installation
1. **Clone the Repository:**
Install Hexabot CLI globally to have easy access to its commands:
```bash
$ git clone https://github.com/hexastack/hexabot.git
```sh
npm install -g hexabot-cli
```
2. **Installation:**
Install node dependencies:
```bash
$ npm install
```
### Usage
3. **Environment Setup:** To configure the environment variables, use the following command at the root folder for initialization:
1. **Create a new project**:
```bash
$ npx hexabot init
```
```sh
hexabot create my-chatbot
```
This will copy the `.env.example` file to `.env` in the `./docker` directory if the file does not already exist.
This will create a new folder `my-chatbot` with all necessary files to get started.
4. **Running the Application:** Once your environment is set up, you can start the app. Use either of the following commands:
2. **Navigate to your project folder**:
```bash
$ npx hexabot start
```
```sh
cd my-chatbot
```
or for development mode:
3. **Install dependencies**:
```bash
$ npx hexabot dev
```
```sh
npm install
```
You can also enable services such as the NLU engine and Ollama (The services are declared under the `./docker` folder) :
4. **Initialize environment**:
```bash
$ npx hexabot dev --enable=ollama,nlu
```
```sh
hexabot init
```
**Note:** The first time you run the app, Docker will take some time to download all the required images.
This command copies the `.env.example` file to `.env`, which you can edit to customize your configuration.
5. **Run in development mode**:
```sh
hexabot dev --services nlu,ollama
```
This starts the required services in development mode.
## Usage
UI Admin Panel is accessible via http://localhost:8080, the default credentials are :
- **Username:** admin@admin.admin
- **Password:** adminadmin
Live Chat Widget is accessible via http://localhost:5173
## Commands
- `npx hexabot init` : Copies the .env.example file to .env in the ./docker directory if .env does not exist. This is usually used for initial setup.
- `npx hexabot dev` : Starts all configured Docker services in development mode. It first checks the .env file for completeness against .env.example and builds the docker images locally.
- `npx hexabot start` : Starts all configured Docker services by loading all images from Docker Hub. This target also checks the .env file for required variables.
- `npx hexabot stop` : Stops all running Docker services defined in the compose files.
- `npx hexabot destroy` : Stops all services and removes all volumes associated with the Docker compose setup, ensuring a clean state.
Example on how to start the stack by adding the Nginx service :
```sh
npx hexabot start --enable=nginx
```
## Documentation
For detailed information on how to get started, as well as in-depth user and developer guides, please refer to our full documentation available in the docs folder or visit the [Documentation](https://docs.hexabot.ai).
You can also find specific documentation for different components of the project in the following locations:
- [CLI Documentation](https://github.com/Hexastack/hexabot-cli/)
- [API Documentation](api/README.md)
- [UI Documentation](frontend/README.md)
- [Live Chat Widget Documentation](widget/README.md)
@ -152,6 +141,48 @@ Please refer to our contribution policy first : [How to contribute to Hexabot](.
Feel free to join us on [Discord](https://discord.gg/rNb9t2MFkG)
1. **Clone the Repository:**
```bash
$ git clone https://github.com/hexastack/hexabot.git
```
2. **Installation:**
Install node dependencies:
```bash
$ npm install
```
3. **Environment Setup:** To configure the environment variables, use the following command at the root folder for initialization:
```bash
$ hexabot init
```
This will copy the `.env.example` file to `.env` in the `./docker` directory if the file does not already exist.
4. **Running the Application:** Once your environment is set up, you can start the app. Use either of the following commands:
For development mode:
```bash
$ hexabot dev
```
Otherwise, you can choose to download docker images rather than building them:
```bash
$ hexabot start
```
You can also enable services such as the NLU engine and Ollama (The services are declared under the `./docker` folder) :
```bash
$ hexabot dev --enable=ollama,nlu
```
**Note:** The first time you run the app, Docker will take some time to download all the required images.
## License
This software is licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:

2
cli/.gitignore vendored
View File

@ -1,2 +0,0 @@
node_modules
dist

View File

@ -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"
}
}

View File

@ -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();
}

View File

@ -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"]
}