@hexabot-ai/cli 3.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +5 -0
- package/AGENTS.md +64 -0
- package/README.md +192 -0
- package/dist/cli.js +31 -0
- package/dist/commands/__tests__/check.test.js +97 -0
- package/dist/commands/__tests__/config.test.js +80 -0
- package/dist/commands/__tests__/dev.test.js +105 -0
- package/dist/commands/__tests__/docker.test.js +132 -0
- package/dist/commands/__tests__/env.test.js +72 -0
- package/dist/commands/__tests__/migrate.test.js +42 -0
- package/dist/commands/__tests__/start.test.js +120 -0
- package/dist/commands/check.js +73 -0
- package/dist/commands/config.js +76 -0
- package/dist/commands/create.js +131 -0
- package/dist/commands/dev.js +72 -0
- package/dist/commands/docker.js +119 -0
- package/dist/commands/env.js +44 -0
- package/dist/commands/migrate.js +22 -0
- package/dist/commands/start.js +76 -0
- package/dist/core/__tests__/config.test.js +88 -0
- package/dist/core/__tests__/docker.test.js +43 -0
- package/dist/core/__tests__/env.test.js +71 -0
- package/dist/core/__tests__/package-manager.test.js +95 -0
- package/dist/core/__tests__/project.test.js +49 -0
- package/dist/core/config.js +78 -0
- package/dist/core/docker.js +66 -0
- package/dist/core/env.js +50 -0
- package/dist/core/package-manager.js +87 -0
- package/dist/core/prerequisites.js +80 -0
- package/dist/core/project.js +58 -0
- package/dist/index.js +16 -0
- package/dist/services/templates.js +27 -0
- package/dist/ui/banner.js +14 -0
- package/dist/utils/__tests__/services.test.js +18 -0
- package/dist/utils/__tests__/validation.test.js +17 -0
- package/dist/utils/__tests__/version.test.js +27 -0
- package/dist/utils/services.js +11 -0
- package/dist/utils/validation.js +9 -0
- package/dist/utils/version.js +22 -0
- package/eslint.config-staged.cjs +10 -0
- package/eslint.config.cjs +104 -0
- package/jest.config.ts +24 -0
- package/package.json +63 -0
- package/src/cli.ts +37 -0
- package/src/commands/__tests__/check.test.ts +116 -0
- package/src/commands/__tests__/config.test.ts +97 -0
- package/src/commands/__tests__/dev.test.ts +151 -0
- package/src/commands/__tests__/docker.test.ts +168 -0
- package/src/commands/__tests__/env.test.ts +95 -0
- package/src/commands/__tests__/migrate.test.ts +64 -0
- package/src/commands/__tests__/start.test.ts +166 -0
- package/src/commands/check.ts +102 -0
- package/src/commands/config.ts +90 -0
- package/src/commands/create.ts +201 -0
- package/src/commands/dev.ts +122 -0
- package/src/commands/docker.ts +190 -0
- package/src/commands/env.ts +62 -0
- package/src/commands/migrate.ts +27 -0
- package/src/commands/start.ts +126 -0
- package/src/core/__tests__/config.test.ts +114 -0
- package/src/core/__tests__/docker.test.ts +59 -0
- package/src/core/__tests__/env.test.ts +97 -0
- package/src/core/__tests__/package-manager.test.ts +121 -0
- package/src/core/__tests__/project.test.ts +68 -0
- package/src/core/config.ts +127 -0
- package/src/core/docker.ts +91 -0
- package/src/core/env.ts +90 -0
- package/src/core/package-manager.ts +126 -0
- package/src/core/prerequisites.ts +117 -0
- package/src/core/project.ts +97 -0
- package/src/index.ts +21 -0
- package/src/services/templates.ts +33 -0
- package/src/ui/banner.ts +18 -0
- package/src/utils/__tests__/services.test.ts +21 -0
- package/src/utils/__tests__/validation.test.ts +21 -0
- package/src/utils/__tests__/version.test.ts +35 -0
- package/src/utils/services.ts +12 -0
- package/src/utils/validation.ts +11 -0
- package/src/utils/version.ts +28 -0
- package/test/__mocks__/chalk.ts +13 -0
- package/tsconfig.json +15 -0
package/.prettierrc
ADDED
package/AGENTS.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Hexabot CLI
|
|
2
|
+
|
|
3
|
+
This file is the authoritative cheat-sheet for the Hexabot CLI. It summarizes the runtime bootstrap, core helpers, and per-command responsibilities so we can confidently extend or coordinate agent work without re-reading the entire codebase.
|
|
4
|
+
|
|
5
|
+
## Runtime & Bootstrap
|
|
6
|
+
|
|
7
|
+
- **Entry point**: `src/index.ts` prints the banner (`printBanner()`) and calls `checkPrerequisites({ silent: true })` before instantiating the Commander program. That pre-flight verifies Node.js (see below) so user-facing commands can assume a supported runtime.
|
|
8
|
+
- **CLI factory**: `createCliProgram()` (`src/cli.ts`) builds the `Command` instance, configures name/description/version (`getCliVersion()`), and registers `check`, `create`, `config`, `dev`, `docker`, `env`, `start`, and `migrate` commands.
|
|
9
|
+
- **Prerequisite gate**: `checkPrerequisites()` / `checkNodeVersion()` / `checkDocker()` (`src/core/prerequisites.ts`) centralize system checks. The top-level bootstrap validates Node (>= 20.18.1) while Docker checks are performed by commands that actually need Docker.
|
|
10
|
+
- **Project guard**: `assertHexabotProject()` / `isHexabotProject()` (`src/core/project.ts`) enforce that commands run inside a workspace whose `package.json` depends on `@hexabot-ai/api`. Docker-aware commands also call `ensureDockerFolder()` which ensures `<projectRoot>/docker/` exists before touching compose files.
|
|
11
|
+
- **Service parsing**: `parseServices()` (`src/utils/services.ts`) normalizes the shared `--services` flag to a de-duped string array so Docker helpers can safely compose per-service overlays.
|
|
12
|
+
|
|
13
|
+
## Configuration & Environment Model
|
|
14
|
+
|
|
15
|
+
- **Config file**: `hexabot.config.json` lives at the project root and is managed by `src/core/config.ts`. `loadProjectConfig()` merges user overrides with defaults (`devScript`, `startScript`, compose path `docker/docker-compose.yml`, env filenames, and optional `packageManager`). `ensureProjectConfig()` writes the file when scaffolding and `updateProjectConfig()` persists dot-notation updates from the CLI.
|
|
16
|
+
- **Env helpers**: `bootstrapEnvFile()`, `listEnvStatus()`, and `resolveEnvExample()` (`src/core/env.ts`) copy `*.example` files when needed and report which env files exist. Commands never manipulate env paths directly—they rely on the config struct.
|
|
17
|
+
- **Package managers**: `normalizePackageManager()`, `detectPackageManager()`, `runPackageScript()`, and `installDependencies()` (`src/core/package-manager.ts`) keep npm/pnpm/yarn/bun handling consistent. `runPackageScript()` verifies the requested script exists in `package.json` before executing it.
|
|
18
|
+
|
|
19
|
+
## Docker Compose helpers
|
|
20
|
+
|
|
21
|
+
- **Compose resolution**: `resolveComposeFile()` ensures config paths are absolute, while `generateComposeFiles()` (`src/core/docker.ts`) builds the `-f` flag chain. It always starts with the base file, then per-service overlays (`docker-compose.<service>.yml`), optional service/mode overlays (`docker-compose.<service>.<dev|prod>.yml`), and finally the global `docker-compose.<mode>.yml` when it exists.
|
|
22
|
+
- **Execution wrappers**: `dockerCompose()` and `dockerExec()` (same module) print highlighted commands, run them via `execSync`, and exit with code `1` on Docker failures.
|
|
23
|
+
|
|
24
|
+
## Subcommand Catalog
|
|
25
|
+
|
|
26
|
+
| Command | Responsibilities | Options & IO | Implementation Notes |
|
|
27
|
+
| --- | --- | --- | --- |
|
|
28
|
+
| `check [--docker-only] [--no-docker]` | Run diagnostics for local Node version, Hexabot project detection, env files, and optionally Docker. | Flags narrow which checks run; outputs PASS/FAIL rows and sets exit code 1 if any check fails. | `src/commands/check.ts` calls `checkNodeVersion()`, `isHexabotProject()`, `loadProjectConfig()`, `listEnvStatus()`, and `checkDocker()` in sequence and prints results with chalk. |
|
|
29
|
+
| `create <projectName>` | Scaffold a project from a GitHub template, configure package manager, bootstrap env files, install deps, and optionally start dev mode. | `--template`, `--pm`, `--no-install`, `--dev`, `--docker`, `--force`. Writes `hexabot.config.json` and new env files, installs deps (unless skipped), and can immediately run `hexabot dev`. | `src/commands/create.ts` validates the slug, downloads the latest release zip of `hexastack/hexabot-template-*` via `downloadAndExtractTemplate()`, ensures config/env files exist, installs dependencies via `installDependencies()`, and optionally calls `runDev()` for the new project. |
|
|
30
|
+
| `config show` | Print the effective `hexabot.config.json` after merging defaults. | No flags; emits pretty-printed JSON. | `src/commands/config.ts` resolves the project root, asserts the workspace, and calls `loadProjectConfig()`. |
|
|
31
|
+
| `config set <key> <value>` | Update nested config values via dot notation with type-aware parsing. | Supports booleans, numbers, JSON literals, comma lists (for `*Services` keys), and package-manager normalization. | Uses `parseValue()` + `buildOverride()` to construct overrides, writes via `updateProjectConfig()`, and echoes the new config. |
|
|
32
|
+
| `dev [--docker] [--services] [-d] [--env] [--no-env-bootstrap] [--pm]` | Run the project in development mode using either the package script or Docker compose stack. | When not using Docker it runs the configured dev script (`dev` default) after optional env bootstrapping. Docker mode bootstraps the docker env file, resolves compose overlays, and runs `docker compose ... up --build [-d]`. | `src/commands/dev.ts` auto-detects the package manager, persists it into the config, handles env bootstrapping (local or docker), then either calls `runPackageScript()` or `runDockerDev()` (which uses `generateComposeFiles()` with mode `dev`). |
|
|
33
|
+
| `start [--docker] [--services] [-d] [--build] [--env] [--env-bootstrap] [--pm]` | Production entry point mirroring `dev` but defaulting to the configured start script and prod compose overlays. | Local mode optionally bootstraps env files when `--env-bootstrap` is passed; Docker mode layers prod compose files, supports `--build`/`--detach`, and logs which services start. | `src/commands/start.ts` shares the package-manager persistence logic with `dev`. `runDockerStart()` enforces Docker availability, resolves compose overlays with mode `prod`, and shells out via `dockerCompose()`. |
|
|
34
|
+
| ``docker up`` / ``docker down`` | Low-level Docker helpers for dev-mode compose stacks. | `up` supports `--services`, `--build`, `--detach`; `down` supports `--services` and `--volumes`. | `src/commands/docker.ts` ensures config + docker folder, resolves services (`parseServices()` with fallbacks to `config.docker.defaultServices`), bootstraps docker env files, and executes `docker compose ... up|down`. |
|
|
35
|
+
| ``docker logs [service]`` / ``docker ps`` | Introspection helpers for the dev stack. | Logs accept `--follow`/`--since`. | Reuse `generateComposeFiles(..., 'dev')` plus targeted docker compose commands. |
|
|
36
|
+
| ``docker start`` | Production-mode convenience wrapper for `hexabot start --docker`. | `--services`, `--detach`, `--build`, `--env-bootstrap`. | This subcommand simply calls `runStart({ docker: true, ... })` so behavior stays aligned with the main `start` command. |
|
|
37
|
+
| `env init [--docker] [--force]` | Copy the configured `.env*` files from their `.example` counterparts. | By default bootstraps the local env; `--docker` switches to docker env paths; `--force` overwrites. | `src/commands/env.ts` calls `bootstrapEnvFile()` with the paths from the loaded config. |
|
|
38
|
+
| `env list` | Show which env files exist. | None; prints ✓/✗ markers. | Uses `listEnvStatus()` results and chalk formatting. |
|
|
39
|
+
| `migrate [args...]` | Execute database migrations inside the `api` container. | Arbitrary args are appended to `npm run migrate`. Requires a dockerized project root. | `src/commands/migrate.ts` checks Docker, ensures `docker/` exists, and runs `dockerExec('api', 'npm run migrate …', '--user $(id -u):$(id -g)')` so generated files share the host UID/GID. |
|
|
40
|
+
|
|
41
|
+
## Shared Utilities & Services
|
|
42
|
+
|
|
43
|
+
- **Template download**: `downloadAndExtractTemplate()` (`src/services/templates.ts`) fetches the release archive with `fetch`, saves it as `template.zip`, extracts using `decompress` (`strip: 1`), and then deletes the zip. `create` is the only consumer.
|
|
44
|
+
- **Project metadata**: `readPackageJson()` (`src/core/project.ts`) powers package-manager validation and Hexabot project detection.
|
|
45
|
+
- **Versioning**: `getCliVersion()` (`src/utils/version.ts`) reads `packages/cli/package.json` and defaults to `3.0.0` on failure.
|
|
46
|
+
|
|
47
|
+
## Typical Workflow
|
|
48
|
+
|
|
49
|
+
1. `hexabot create my-bot --docker` — scaffold a workspace, persist the preferred package manager into `hexabot.config.json`, bootstrap both local and docker env files, and install dependencies.
|
|
50
|
+
2. `cd my-bot`
|
|
51
|
+
3. `hexabot check` — confirm Node/Docker availability, project structure, and env files before running heavier commands.
|
|
52
|
+
4. `hexabot env init` — re-run if you need to refresh `.env` files (use `--docker` to target docker envs).
|
|
53
|
+
5. `hexabot dev --docker --services postgres,ollama` — spin up the dev stack with the requested services and `docker-compose.<service>.dev.yml` overlays.
|
|
54
|
+
6. `hexabot start` or `hexabot start --docker --services api` — run production scripts locally or via Docker.
|
|
55
|
+
7. `hexabot docker down --volumes` / `hexabot migrate` / `hexabot env list` as needed for lifecycle management.
|
|
56
|
+
|
|
57
|
+
## Extending the CLI
|
|
58
|
+
|
|
59
|
+
- Follow the established pattern under `src/commands/` by implementing `registerFooCommand(program: Command)` modules and wiring them up inside `createCliProgram()` so help/version wiring stays centralized.
|
|
60
|
+
- Always gate project-specific commands with `assertHexabotProject()`, and guard docker-aware flows with both `checkDocker()` and `ensureDockerFolder()`.
|
|
61
|
+
- Use `loadProjectConfig()` / `updateProjectConfig()` instead of hard-coding script names, env files, or compose paths. That keeps future overrides working automatically.
|
|
62
|
+
- Share env initialization via `bootstrapEnvFile()` / `listEnvStatus()` and `--force` semantics rather than reimplementing file copies.
|
|
63
|
+
- When adding compose-aware commands, rely on `parseServices()` and `generateComposeFiles()` so service overlays, dev/prod modes, and future compose conventions stay consistent.
|
|
64
|
+
- Keep this AGENTS.md file updated whenever commands, defaults, or orchestrating helpers change so downstream automation never acts on stale assumptions.
|
package/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Hexabot CLI
|
|
2
|
+
|
|
3
|
+
Hexabot CLI is a powerful command-line tool to help manage your Hexabot project instance. With it, you can create new projects, initialize environments, start services in various modes, run database migrations, and more. The CLI aims to make managing your chatbot seamless and intuitive.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Not yet familiar with [Hexabot](https://hexabot.ai/)? It's a open-source chatbot / agent solution that allows users to create and manage AI-powered, multi-channel, and multilingual chatbots with ease. If you would like to learn more, please visit the [official github repo](https://github.com/Hexastack/Hexabot/).
|
|
7
|
+
|
|
8
|
+
## Getting Started
|
|
9
|
+
|
|
10
|
+
### Prerequisites
|
|
11
|
+
|
|
12
|
+
- Node.js >= 20.18.1
|
|
13
|
+
- One package manager (`npm`, `pnpm`, `yarn`, or `bun`)
|
|
14
|
+
- Docker Desktop/Engine (only required when you pass `--docker` or use `hexabot docker ...`)
|
|
15
|
+
|
|
16
|
+
### Installation
|
|
17
|
+
|
|
18
|
+
Install Hexabot CLI globally to have easy access to its commands:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npm install -g @hexabot-ai/cli
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Usage
|
|
25
|
+
|
|
26
|
+
Once installed, you can use the `hexabot` command anywhere. The CLI focuses on a “zero to bot” flow: create a project, `cd` into it, and run `hexabot dev`. Docker is optional and available via `--docker` or the `hexabot docker ...` helpers.
|
|
27
|
+
|
|
28
|
+
### Commands
|
|
29
|
+
|
|
30
|
+
#### `create <project-name>`
|
|
31
|
+
|
|
32
|
+
Scaffold a new Hexabot project from the official NestJS starter template.
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
hexabot create support-bot
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Common options:
|
|
39
|
+
|
|
40
|
+
- `-t, --template <name>` – template repository. Use `org/repo` or shorthand `starter`.
|
|
41
|
+
- `--pm <npm|pnpm|yarn|bun>` – force a package manager (auto-detected otherwise).
|
|
42
|
+
- `--no-install` – skip running the package manager after scaffolding.
|
|
43
|
+
- `--dev` – immediately run `hexabot dev` once creation is complete.
|
|
44
|
+
- `--docker` – bootstrap the Docker env file and hint about Docker-first commands.
|
|
45
|
+
- `--force` – allow scaffolding into a non-empty directory.
|
|
46
|
+
|
|
47
|
+
The command downloads the latest template release, installs dependencies (unless `--no-install`), and bootstraps `.env` (and `.env.docker` when `--docker` is passed).
|
|
48
|
+
|
|
49
|
+
#### `dev`
|
|
50
|
+
|
|
51
|
+
Run the current project in development mode. Defaults to local (SQLite) development by running the configured package script (defaults to `npm run dev`).
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
# Local dev
|
|
55
|
+
hexabot dev
|
|
56
|
+
|
|
57
|
+
# Docker dev with Postgres profile
|
|
58
|
+
hexabot dev --docker --services postgres
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Options:
|
|
62
|
+
|
|
63
|
+
- `--docker` – run Docker Compose instead of the package script.
|
|
64
|
+
- `--services <list>` – comma-separated Compose overlays/profiles to enable.
|
|
65
|
+
- `-d, --detach` – detach Docker Compose.
|
|
66
|
+
- `--env <file>` – custom env file for local dev (defaults to `.env`).
|
|
67
|
+
- `--no-env-bootstrap` – skip copying `.env.example` files automatically.
|
|
68
|
+
- `--pm <npm|pnpm|yarn|bun>` – temporarily override the package manager.
|
|
69
|
+
|
|
70
|
+
#### `env`
|
|
71
|
+
|
|
72
|
+
Helper commands to manage `.env` files.
|
|
73
|
+
|
|
74
|
+
- `hexabot env init` – copy `.env.example` ➜ `.env`.
|
|
75
|
+
- `hexabot env init --docker` – copy `.env.docker.example` ➜ `.env.docker`.
|
|
76
|
+
- `hexabot env list` – show which env files exist or are missing.
|
|
77
|
+
|
|
78
|
+
Flags: `--force` overwrites existing files when running `env init`.
|
|
79
|
+
|
|
80
|
+
#### `docker`
|
|
81
|
+
|
|
82
|
+
Quality-of-life wrappers around `docker compose` using the project’s `docker/` folder.
|
|
83
|
+
|
|
84
|
+
- `hexabot docker up [--services <list>] [--build] [-d]`
|
|
85
|
+
- `hexabot docker down [--services <list>] [--volumes]`
|
|
86
|
+
- `hexabot docker logs [service] [-f | --since <1h>]`
|
|
87
|
+
- `hexabot docker ps`
|
|
88
|
+
- `hexabot docker start [--services <list>] [--build] [-d]` – convenience alias for `hexabot start --docker`
|
|
89
|
+
|
|
90
|
+
The CLI automatically stitches together `docker-compose.yml` + `docker-compose.<service>.yml` overlays and can copy `.env.docker.example` on first run.
|
|
91
|
+
|
|
92
|
+
#### `start`
|
|
93
|
+
|
|
94
|
+
Production-oriented variant of `dev`.
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
hexabot start
|
|
98
|
+
hexabot start --docker --services api,postgres --build
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- Local mode runs the configured `start` script (defaults to `npm run start`).
|
|
102
|
+
- Docker mode uses the “prod” compose overlays (e.g. `docker-compose.<service>.prod.yml`) so no dev-specific files are chained.
|
|
103
|
+
- Pass `--env-bootstrap` if you still want the CLI to copy env examples automatically.
|
|
104
|
+
|
|
105
|
+
#### `check`
|
|
106
|
+
|
|
107
|
+
Run diagnostics for the current environment.
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
hexabot check
|
|
111
|
+
hexabot check --docker-only
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Outputs PASS/FAIL entries for Node.js version, project detection, env files, and optionally Docker.
|
|
115
|
+
|
|
116
|
+
#### `config`
|
|
117
|
+
|
|
118
|
+
Inspect or tweak `hexabot.config.json` without editing it manually.
|
|
119
|
+
|
|
120
|
+
- `hexabot config show`
|
|
121
|
+
- `hexabot config set <key> <value>` (supports dot notation, e.g. `docker.defaultServices "postgres,redis"`)
|
|
122
|
+
|
|
123
|
+
#### `migrate [args...]`
|
|
124
|
+
|
|
125
|
+
Run database migrations inside the Docker `api` container. Any extra args are forwarded to `npm run migrate`.
|
|
126
|
+
|
|
127
|
+
## Example Workflow
|
|
128
|
+
|
|
129
|
+
1. **Create a new project** (installs dependencies automatically unless `--no-install`):
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
npx @hexabot-ai/cli create support-bot
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
2. **Enter the project and start local dev (SQLite, no Docker required)**:
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
cd support-bot
|
|
139
|
+
hexabot dev
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
3. **Need infrastructure like Postgres or Redis? Opt in with Docker**:
|
|
143
|
+
|
|
144
|
+
```sh
|
|
145
|
+
hexabot dev --docker --services postgres,redis
|
|
146
|
+
# or manage Docker services directly
|
|
147
|
+
hexabot docker up --services postgres
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
That’s it—`create → cd → dev` is the happy path, while Docker and env helpers remain available on demand.
|
|
151
|
+
|
|
152
|
+
## Documentation
|
|
153
|
+
|
|
154
|
+
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).
|
|
155
|
+
|
|
156
|
+
You can also find specific documentation for different components of the project in the following locations:
|
|
157
|
+
|
|
158
|
+
- [API Documentation](api/README.md)
|
|
159
|
+
- [UI Documentation](frontend/README.md)
|
|
160
|
+
- [Live Chat Widget Documentation](widget/README.md)
|
|
161
|
+
- [NLU Engine Documentation](nlu/README.md)
|
|
162
|
+
|
|
163
|
+
## Contributing
|
|
164
|
+
|
|
165
|
+
We welcome contributions from the community! Whether you want to report a bug, suggest new features, or submit a pull request, your input is valuable to us.
|
|
166
|
+
|
|
167
|
+
Please refer to our contribution policy first : [How to contribute to Hexabot](./CONTRIBUTING.md)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
[](./CODE_OF_CONDUCT.md)
|
|
171
|
+
|
|
172
|
+
Feel free to join us on [Discord](https://discord.gg/rNb9t2MFkG)
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
Copyright (c) 2025 Hexastack.
|
|
177
|
+
|
|
178
|
+
This project is licensed under the **Fair Core License, Version 1.0**, with **Apache License 2.0** as the future license (abbrev. **FCL-1.0-ALv2**).
|
|
179
|
+
|
|
180
|
+
**Change date.** For each version of the software, the Fair Core License converts to Apache-2.0 on the **second anniversary** of the date that version is made available.
|
|
181
|
+
|
|
182
|
+
**Commercial features & license keys.** Certain features of Hexabot are protected by license-key checks. You **must not** remove, modify, disable, or circumvent those checks, nor enable access to protected functionality without a valid license key.
|
|
183
|
+
|
|
184
|
+
**Competing uses (non-compete).** Use that competes with Hexastack’s business—for example, offering Hexabot (or a substantially similar service) as a hosted or commercial product—is not permitted until the conversion to Apache-2.0 for the applicable version.
|
|
185
|
+
|
|
186
|
+
**Redistribution.** If you distribute copies, modifications, or derivatives, you must include this license and not remove copyright or proprietary notices.
|
|
187
|
+
|
|
188
|
+
**Patents.** A limited patent license is granted for permitted uses and terminates on patent aggression.
|
|
189
|
+
|
|
190
|
+
**Trademarks.** “Hexabot” and “Hexastack” are trademarks. Except to identify Hexastack as the origin of the software, no trademark rights are granted.
|
|
191
|
+
|
|
192
|
+
**Disclaimer.** The software is provided “AS IS,” without warranties or conditions of any kind, and Hexastack will not be liable for any damages arising from its use.
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Hexabot — Fair Core License (FCL-1.0-ALv2)
|
|
3
|
+
* Copyright (c) 2025 Hexastack.
|
|
4
|
+
* Full terms: see LICENSE.md.
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import { registerCheckCommand } from './commands/check.js';
|
|
8
|
+
import { registerConfigCommand } from './commands/config.js';
|
|
9
|
+
import { registerCreateCommand } from './commands/create.js';
|
|
10
|
+
import { registerDevCommand } from './commands/dev.js';
|
|
11
|
+
import { registerDockerCommand } from './commands/docker.js';
|
|
12
|
+
import { registerEnvCommand } from './commands/env.js';
|
|
13
|
+
import { registerMigrateCommand } from './commands/migrate.js';
|
|
14
|
+
import { registerStartCommand } from './commands/start.js';
|
|
15
|
+
import { getCliVersion } from './utils/version.js';
|
|
16
|
+
export const createCliProgram = () => {
|
|
17
|
+
const program = new Command();
|
|
18
|
+
program
|
|
19
|
+
.name('Hexabot')
|
|
20
|
+
.description('A CLI to manage your Hexabot project instance')
|
|
21
|
+
.version(getCliVersion());
|
|
22
|
+
registerCheckCommand(program);
|
|
23
|
+
registerCreateCommand(program);
|
|
24
|
+
registerConfigCommand(program);
|
|
25
|
+
registerDevCommand(program);
|
|
26
|
+
registerDockerCommand(program);
|
|
27
|
+
registerEnvCommand(program);
|
|
28
|
+
registerStartCommand(program);
|
|
29
|
+
registerMigrateCommand(program);
|
|
30
|
+
return program;
|
|
31
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Hexabot — Fair Core License (FCL-1.0-ALv2)
|
|
3
|
+
* Copyright (c) 2025 Hexastack.
|
|
4
|
+
* Full terms: see LICENSE.md.
|
|
5
|
+
*/
|
|
6
|
+
import { jest } from '@jest/globals';
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
const loadProjectConfig = jest.fn();
|
|
9
|
+
const listEnvStatus = jest.fn();
|
|
10
|
+
const checkDocker = jest.fn();
|
|
11
|
+
const checkNodeVersion = jest.fn();
|
|
12
|
+
const isHexabotProject = jest.fn();
|
|
13
|
+
jest.unstable_mockModule('../../core/config.js', () => ({
|
|
14
|
+
loadProjectConfig,
|
|
15
|
+
}));
|
|
16
|
+
jest.unstable_mockModule('../../core/env.js', () => ({
|
|
17
|
+
listEnvStatus,
|
|
18
|
+
}));
|
|
19
|
+
jest.unstable_mockModule('../../core/prerequisites.js', () => ({
|
|
20
|
+
checkDocker,
|
|
21
|
+
checkNodeVersion,
|
|
22
|
+
}));
|
|
23
|
+
jest.unstable_mockModule('../../core/project.js', () => ({
|
|
24
|
+
isHexabotProject,
|
|
25
|
+
}));
|
|
26
|
+
let registerCheckCommand;
|
|
27
|
+
beforeAll(async () => {
|
|
28
|
+
({ registerCheckCommand } = await import('../check.js'));
|
|
29
|
+
});
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
jest.resetAllMocks();
|
|
32
|
+
process.exitCode = undefined;
|
|
33
|
+
});
|
|
34
|
+
describe('registerCheckCommand', () => {
|
|
35
|
+
it('runs full diagnostics by default', async () => {
|
|
36
|
+
checkNodeVersion.mockReturnValue({ ok: true, message: 'Node OK' });
|
|
37
|
+
isHexabotProject.mockReturnValue(true);
|
|
38
|
+
loadProjectConfig.mockReturnValue({
|
|
39
|
+
env: {
|
|
40
|
+
local: '.env',
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
listEnvStatus.mockReturnValue([
|
|
44
|
+
{ file: '.env', exists: true },
|
|
45
|
+
{ file: '.env.docker', exists: true },
|
|
46
|
+
]);
|
|
47
|
+
checkDocker.mockReturnValue({ ok: true, message: 'Docker OK' });
|
|
48
|
+
const logs = [];
|
|
49
|
+
jest.spyOn(console, 'log').mockImplementation((message) => {
|
|
50
|
+
logs.push(String(message));
|
|
51
|
+
});
|
|
52
|
+
const program = new Command();
|
|
53
|
+
registerCheckCommand(program);
|
|
54
|
+
await program.parseAsync(['node', 'test', 'check']);
|
|
55
|
+
expect(logs.join('\n')).toContain('Node.js version');
|
|
56
|
+
expect(logs.join('\n')).toContain('Hexabot project');
|
|
57
|
+
expect(logs.join('\n')).toContain('Env file .env');
|
|
58
|
+
expect(logs.join('\n')).toContain('Docker');
|
|
59
|
+
expect(process.exitCode).toBeUndefined();
|
|
60
|
+
});
|
|
61
|
+
it('supports docker-only and no-docker modes', async () => {
|
|
62
|
+
checkDocker.mockReturnValue({ ok: true, message: 'Docker OK' });
|
|
63
|
+
checkNodeVersion.mockReturnValue({ ok: true, message: 'Node OK' });
|
|
64
|
+
const dockerOnlyProgram = new Command();
|
|
65
|
+
registerCheckCommand(dockerOnlyProgram);
|
|
66
|
+
await dockerOnlyProgram.parseAsync([
|
|
67
|
+
'node',
|
|
68
|
+
'test',
|
|
69
|
+
'check',
|
|
70
|
+
'--docker-only',
|
|
71
|
+
]);
|
|
72
|
+
expect(checkNodeVersion).not.toHaveBeenCalled();
|
|
73
|
+
expect(isHexabotProject).not.toHaveBeenCalled();
|
|
74
|
+
expect(checkDocker).toHaveBeenCalled();
|
|
75
|
+
jest.clearAllMocks();
|
|
76
|
+
checkDocker.mockReturnValue({ ok: true, message: 'Docker OK' });
|
|
77
|
+
checkNodeVersion.mockReturnValue({ ok: true, message: 'Node OK' });
|
|
78
|
+
const noDockerProgram = new Command();
|
|
79
|
+
registerCheckCommand(noDockerProgram);
|
|
80
|
+
await noDockerProgram.parseAsync(['node', 'test', 'check', '--no-docker']);
|
|
81
|
+
expect(checkDocker).not.toHaveBeenCalled();
|
|
82
|
+
});
|
|
83
|
+
it('sets process exit code when checks fail', async () => {
|
|
84
|
+
checkNodeVersion.mockReturnValue({ ok: true, message: 'Node OK' });
|
|
85
|
+
isHexabotProject.mockReturnValue(false);
|
|
86
|
+
checkDocker.mockReturnValue({ ok: false, message: 'Docker missing' });
|
|
87
|
+
const logs = [];
|
|
88
|
+
jest.spyOn(console, 'log').mockImplementation((message) => {
|
|
89
|
+
logs.push(String(message));
|
|
90
|
+
});
|
|
91
|
+
const program = new Command();
|
|
92
|
+
registerCheckCommand(program);
|
|
93
|
+
await program.parseAsync(['node', 'test', 'check', '--no-docker']);
|
|
94
|
+
expect(logs.join('\n')).toContain('Hexabot project');
|
|
95
|
+
expect(process.exitCode).toBe(1);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Hexabot — Fair Core License (FCL-1.0-ALv2)
|
|
3
|
+
* Copyright (c) 2025 Hexastack.
|
|
4
|
+
* Full terms: see LICENSE.md.
|
|
5
|
+
*/
|
|
6
|
+
import { jest } from '@jest/globals';
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
const loadProjectConfig = jest.fn();
|
|
9
|
+
const updateProjectConfig = jest.fn();
|
|
10
|
+
const normalizePackageManager = jest.fn();
|
|
11
|
+
const assertHexabotProject = jest.fn();
|
|
12
|
+
jest.unstable_mockModule('../../core/config.js', () => ({
|
|
13
|
+
loadProjectConfig,
|
|
14
|
+
updateProjectConfig,
|
|
15
|
+
}));
|
|
16
|
+
jest.unstable_mockModule('../../core/package-manager.js', () => ({
|
|
17
|
+
normalizePackageManager,
|
|
18
|
+
}));
|
|
19
|
+
jest.unstable_mockModule('../../core/project.js', () => ({
|
|
20
|
+
assertHexabotProject,
|
|
21
|
+
}));
|
|
22
|
+
let registerConfigCommand;
|
|
23
|
+
beforeAll(async () => {
|
|
24
|
+
({ registerConfigCommand } = await import('../config.js'));
|
|
25
|
+
});
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
jest.resetAllMocks();
|
|
28
|
+
loadProjectConfig.mockReturnValue({ packageManager: 'npm' });
|
|
29
|
+
normalizePackageManager.mockImplementation((value) => typeof value === 'string' ? value.toLowerCase() : undefined);
|
|
30
|
+
});
|
|
31
|
+
describe('registerConfigCommand', () => {
|
|
32
|
+
it('prints the current project config', async () => {
|
|
33
|
+
const logs = [];
|
|
34
|
+
jest.spyOn(console, 'log').mockImplementation((message) => {
|
|
35
|
+
logs.push(String(message));
|
|
36
|
+
});
|
|
37
|
+
const program = new Command();
|
|
38
|
+
registerConfigCommand(program);
|
|
39
|
+
await program.parseAsync(['node', 'test', 'config', 'show']);
|
|
40
|
+
expect(logs.some((line) => line.includes('"packageManager"'))).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
it('updates configuration values with automatic parsing', async () => {
|
|
43
|
+
const program = new Command();
|
|
44
|
+
registerConfigCommand(program);
|
|
45
|
+
await program.parseAsync([
|
|
46
|
+
'node',
|
|
47
|
+
'test',
|
|
48
|
+
'config',
|
|
49
|
+
'set',
|
|
50
|
+
'packageManager',
|
|
51
|
+
'PNPM',
|
|
52
|
+
]);
|
|
53
|
+
expect(normalizePackageManager).toHaveBeenCalledWith('PNPM');
|
|
54
|
+
expect(updateProjectConfig).toHaveBeenCalledWith(expect.any(String), {
|
|
55
|
+
packageManager: 'pnpm',
|
|
56
|
+
});
|
|
57
|
+
await program.parseAsync([
|
|
58
|
+
'node',
|
|
59
|
+
'test',
|
|
60
|
+
'config',
|
|
61
|
+
'set',
|
|
62
|
+
'docker.defaultServices',
|
|
63
|
+
'api, postgres',
|
|
64
|
+
]);
|
|
65
|
+
expect(updateProjectConfig).toHaveBeenCalledWith(expect.any(String), {
|
|
66
|
+
docker: { defaultServices: ['api', 'postgres'] },
|
|
67
|
+
});
|
|
68
|
+
await program.parseAsync([
|
|
69
|
+
'node',
|
|
70
|
+
'test',
|
|
71
|
+
'config',
|
|
72
|
+
'set',
|
|
73
|
+
'limits.rate',
|
|
74
|
+
'50',
|
|
75
|
+
]);
|
|
76
|
+
expect(updateProjectConfig).toHaveBeenCalledWith(expect.any(String), {
|
|
77
|
+
limits: { rate: 50 },
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Hexabot — Fair Core License (FCL-1.0-ALv2)
|
|
3
|
+
* Copyright (c) 2025 Hexastack.
|
|
4
|
+
* Full terms: see LICENSE.md.
|
|
5
|
+
*/
|
|
6
|
+
import { jest } from '@jest/globals';
|
|
7
|
+
const loadProjectConfig = jest.fn();
|
|
8
|
+
const updateProjectConfig = jest.fn();
|
|
9
|
+
const dockerCompose = jest.fn();
|
|
10
|
+
const generateComposeFiles = jest.fn();
|
|
11
|
+
const resolveComposeFile = jest.fn();
|
|
12
|
+
const bootstrapEnvFile = jest.fn();
|
|
13
|
+
const resolveEnvExample = jest.fn();
|
|
14
|
+
const detectPackageManager = jest.fn();
|
|
15
|
+
const normalizePackageManager = jest.fn();
|
|
16
|
+
const runPackageScript = jest.fn();
|
|
17
|
+
const checkDocker = jest.fn();
|
|
18
|
+
const assertHexabotProject = jest.fn();
|
|
19
|
+
const ensureDockerFolder = jest.fn();
|
|
20
|
+
jest.unstable_mockModule('../../core/config.js', () => ({
|
|
21
|
+
loadProjectConfig,
|
|
22
|
+
updateProjectConfig,
|
|
23
|
+
}));
|
|
24
|
+
jest.unstable_mockModule('../../core/docker.js', () => ({
|
|
25
|
+
dockerCompose,
|
|
26
|
+
generateComposeFiles,
|
|
27
|
+
resolveComposeFile,
|
|
28
|
+
}));
|
|
29
|
+
jest.unstable_mockModule('../../core/env.js', () => ({
|
|
30
|
+
bootstrapEnvFile,
|
|
31
|
+
resolveEnvExample,
|
|
32
|
+
}));
|
|
33
|
+
jest.unstable_mockModule('../../core/package-manager.js', () => ({
|
|
34
|
+
detectPackageManager,
|
|
35
|
+
normalizePackageManager,
|
|
36
|
+
runPackageScript,
|
|
37
|
+
}));
|
|
38
|
+
jest.unstable_mockModule('../../core/prerequisites.js', () => ({
|
|
39
|
+
checkDocker,
|
|
40
|
+
}));
|
|
41
|
+
jest.unstable_mockModule('../../core/project.js', () => ({
|
|
42
|
+
assertHexabotProject,
|
|
43
|
+
ensureDockerFolder,
|
|
44
|
+
}));
|
|
45
|
+
jest.unstable_mockModule('../../utils/services.js', () => ({
|
|
46
|
+
parseServices: (value) => value.split(',').filter(Boolean),
|
|
47
|
+
}));
|
|
48
|
+
let runDev;
|
|
49
|
+
beforeAll(async () => {
|
|
50
|
+
({ runDev } = await import('../dev.js'));
|
|
51
|
+
});
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
jest.resetAllMocks();
|
|
54
|
+
normalizePackageManager.mockImplementation((value) => typeof value === 'string' ? value.toLowerCase() : undefined);
|
|
55
|
+
resolveComposeFile.mockReturnValue('/tmp/docker/docker-compose.yml');
|
|
56
|
+
generateComposeFiles.mockReturnValue('-f docker-compose.yml');
|
|
57
|
+
});
|
|
58
|
+
const baseConfig = {
|
|
59
|
+
devScript: 'dev',
|
|
60
|
+
startScript: 'start',
|
|
61
|
+
packageManager: 'pnpm',
|
|
62
|
+
docker: {
|
|
63
|
+
composeFile: 'docker/docker-compose.yml',
|
|
64
|
+
defaultServices: ['api'],
|
|
65
|
+
},
|
|
66
|
+
env: {
|
|
67
|
+
local: '.env',
|
|
68
|
+
localExample: '.env.example',
|
|
69
|
+
docker: '.env.docker',
|
|
70
|
+
dockerExample: '.env.docker.example',
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
describe('runDev', () => {
|
|
74
|
+
it('runs the dev script locally with env bootstrapping', async () => {
|
|
75
|
+
loadProjectConfig.mockReturnValue(baseConfig);
|
|
76
|
+
resolveEnvExample.mockReturnValue('.env.custom');
|
|
77
|
+
await runDev({ env: '.env.local' });
|
|
78
|
+
expect(assertHexabotProject).toHaveBeenCalled();
|
|
79
|
+
expect(resolveEnvExample).toHaveBeenCalledWith(expect.any(String), '.env.local', baseConfig.env.localExample);
|
|
80
|
+
expect(bootstrapEnvFile).toHaveBeenCalledWith(expect.any(String), '.env.custom', '.env.local');
|
|
81
|
+
expect(runPackageScript).toHaveBeenCalledWith('pnpm', 'dev', expect.any(String));
|
|
82
|
+
expect(dockerCompose).not.toHaveBeenCalled();
|
|
83
|
+
});
|
|
84
|
+
it('updates the stored package manager when overridden', async () => {
|
|
85
|
+
loadProjectConfig.mockReturnValue({
|
|
86
|
+
...baseConfig,
|
|
87
|
+
packageManager: 'npm',
|
|
88
|
+
});
|
|
89
|
+
await runDev({ pm: 'yarn', envBootstrap: false });
|
|
90
|
+
expect(updateProjectConfig).toHaveBeenCalledWith(expect.any(String), {
|
|
91
|
+
packageManager: 'yarn',
|
|
92
|
+
});
|
|
93
|
+
expect(bootstrapEnvFile).not.toHaveBeenCalled();
|
|
94
|
+
});
|
|
95
|
+
it('runs docker compose when the docker flag is provided', async () => {
|
|
96
|
+
loadProjectConfig.mockReturnValue(baseConfig);
|
|
97
|
+
await runDev({ docker: true, services: 'api,postgres', detach: true });
|
|
98
|
+
expect(bootstrapEnvFile).toHaveBeenCalledWith(expect.any(String), baseConfig.env.dockerExample, baseConfig.env.docker);
|
|
99
|
+
expect(checkDocker).toHaveBeenCalled();
|
|
100
|
+
expect(ensureDockerFolder).toHaveBeenCalled();
|
|
101
|
+
expect(generateComposeFiles).toHaveBeenCalledWith('/tmp/docker/docker-compose.yml', ['api', 'postgres'], 'dev');
|
|
102
|
+
expect(dockerCompose).toHaveBeenCalledWith(expect.stringContaining('up --build -d'));
|
|
103
|
+
expect(runPackageScript).not.toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
});
|