@hexabot-ai/cli 3.1.4-alpha.0 → 3.2.1-alpha.0
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/AGENTS.md +1 -1
- package/README.md +7 -5
- package/dist/commands/__tests__/create.test.js +4 -2
- package/dist/commands/create.js +31 -5
- package/dist/core/prerequisites.js +2 -2
- package/eslint.config.cjs +1 -1
- package/package.json +2 -4
- package/src/commands/__tests__/create.test.ts +4 -2
- package/src/commands/create.ts +39 -5
- package/src/core/prerequisites.ts +2 -2
- package/test/__mocks__/chalk.ts +7 -1
- package/tsconfig.eslint.json +5 -0
package/AGENTS.md
CHANGED
|
@@ -6,7 +6,7 @@ This file is the authoritative cheat-sheet for the Hexabot CLI. It summarizes th
|
|
|
6
6
|
|
|
7
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
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.
|
|
9
|
+
- **Prerequisite gate**: `checkPrerequisites()` / `checkNodeVersion()` / `checkDocker()` (`src/core/prerequisites.ts`) centralize system checks. The top-level bootstrap validates Node (>= 20.19.0) while Docker checks are performed by commands that actually need Docker.
|
|
10
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
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
12
|
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Not yet familiar with [Hexabot](https://hexabot.ai/)? It's a open-source chatbot
|
|
|
9
9
|
|
|
10
10
|
### Prerequisites
|
|
11
11
|
|
|
12
|
-
- Node.js >= 20.
|
|
12
|
+
- Node.js >= 20.19.0
|
|
13
13
|
- One package manager (`npm`, `pnpm`, `yarn`, or `bun`)
|
|
14
14
|
- Docker Desktop/Engine (only required when you pass `--docker` or use `hexabot docker ...`)
|
|
15
15
|
|
|
@@ -155,10 +155,12 @@ For detailed information on how to get started, as well as in-depth user and dev
|
|
|
155
155
|
|
|
156
156
|
You can also find specific documentation for different components of the project in the following locations:
|
|
157
157
|
|
|
158
|
-
- [API Documentation](api/README.md)
|
|
159
|
-
- [UI Documentation](frontend/README.md)
|
|
160
|
-
- [
|
|
161
|
-
- [
|
|
158
|
+
- [API Documentation](../api/README.md)
|
|
159
|
+
- [UI Documentation](../frontend/README.md)
|
|
160
|
+
- [Agentic Package Documentation](../agentic/README.md)
|
|
161
|
+
- [Types Package Documentation](../types/README.md)
|
|
162
|
+
- [Workflow Graph Documentation](../graph/README.md)
|
|
163
|
+
- [Live Chat Widget Documentation](../widget/README.md)
|
|
162
164
|
|
|
163
165
|
## Contributing
|
|
164
166
|
|
|
@@ -118,7 +118,9 @@ describe('registerCreateCommand', () => {
|
|
|
118
118
|
.mockResolvedValueOnce('Anis')
|
|
119
119
|
.mockResolvedValueOnce('Bot')
|
|
120
120
|
.mockResolvedValueOnce('anis@example.com');
|
|
121
|
-
password
|
|
121
|
+
password
|
|
122
|
+
.mockResolvedValueOnce('Admin#123')
|
|
123
|
+
.mockResolvedValueOnce('Admin#123');
|
|
122
124
|
const program = new Command();
|
|
123
125
|
registerCreateCommand(program);
|
|
124
126
|
await program.parseAsync([
|
|
@@ -142,7 +144,7 @@ describe('registerCreateCommand', () => {
|
|
|
142
144
|
});
|
|
143
145
|
expect(exitSpy).not.toHaveBeenCalled();
|
|
144
146
|
expect(input).toHaveBeenCalledTimes(3);
|
|
145
|
-
expect(password).toHaveBeenCalledTimes(
|
|
147
|
+
expect(password).toHaveBeenCalledTimes(2);
|
|
146
148
|
});
|
|
147
149
|
it('fails cleanly when create runs in a non-interactive terminal', async () => {
|
|
148
150
|
setTTY(false);
|
package/dist/commands/create.js
CHANGED
|
@@ -135,6 +135,18 @@ const validateEmail = (value) => {
|
|
|
135
135
|
}
|
|
136
136
|
return true;
|
|
137
137
|
};
|
|
138
|
+
const validateAdminPassword = (value) => {
|
|
139
|
+
const trimmed = value.trim();
|
|
140
|
+
if (!trimmed) {
|
|
141
|
+
return 'Password is required.';
|
|
142
|
+
}
|
|
143
|
+
// Keep the rule simple and explicit for CLI UX. Complexity policies vary by org.
|
|
144
|
+
const minLength = 12;
|
|
145
|
+
if (trimmed.length < minLength) {
|
|
146
|
+
return `Password must be at least ${minLength} characters.`;
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
};
|
|
138
150
|
const assertInteractiveTerminal = () => {
|
|
139
151
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
140
152
|
throw new Error('hexabot create requires an interactive terminal to capture admin credentials.');
|
|
@@ -142,22 +154,36 @@ const assertInteractiveTerminal = () => {
|
|
|
142
154
|
};
|
|
143
155
|
const promptSeedAdminCredentials = async () => {
|
|
144
156
|
assertInteractiveTerminal();
|
|
157
|
+
console.log('\n');
|
|
158
|
+
console.log(chalk.bold('Admin account (initial credentials)'));
|
|
159
|
+
console.log(chalk.gray('These details are used to seed the first admin user. You can change them later in your env file.'));
|
|
160
|
+
console.log('\n');
|
|
145
161
|
const firstName = (await input({
|
|
146
|
-
message: '
|
|
162
|
+
message: 'First name (e.g. Jhon)',
|
|
147
163
|
validate: requireValue('First name'),
|
|
148
164
|
})).trim();
|
|
149
165
|
const lastName = (await input({
|
|
150
|
-
message: '
|
|
166
|
+
message: 'Last name (e.g. Doe)',
|
|
151
167
|
validate: requireValue('Last name'),
|
|
152
168
|
})).trim();
|
|
153
169
|
const email = (await input({
|
|
154
|
-
message: '
|
|
170
|
+
message: 'Email (e.g. admin@company.com)',
|
|
155
171
|
validate: validateEmail,
|
|
156
172
|
})).trim();
|
|
157
173
|
const adminPassword = await password({
|
|
158
|
-
message: '
|
|
174
|
+
message: 'Password (min 12 chars)',
|
|
175
|
+
mask: '*',
|
|
176
|
+
validate: validateAdminPassword,
|
|
177
|
+
});
|
|
178
|
+
await password({
|
|
179
|
+
message: 'Confirm password',
|
|
159
180
|
mask: '*',
|
|
160
|
-
validate:
|
|
181
|
+
validate: (value) => {
|
|
182
|
+
if (value !== adminPassword) {
|
|
183
|
+
return 'Passwords do not match.';
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
},
|
|
161
187
|
});
|
|
162
188
|
return {
|
|
163
189
|
firstName,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { execSync } from 'child_process';
|
|
7
7
|
import chalk from 'chalk';
|
|
8
|
-
const REQUIRED_NODE_VERSION = '20.
|
|
8
|
+
const REQUIRED_NODE_VERSION = '20.19.0';
|
|
9
9
|
export const checkPrerequisites = (options = {}) => {
|
|
10
10
|
checkNodeVersion(options);
|
|
11
11
|
if (options.docker) {
|
|
@@ -28,7 +28,7 @@ export const checkNodeVersion = (options = {}) => {
|
|
|
28
28
|
return { ok: false, message };
|
|
29
29
|
}
|
|
30
30
|
catch (error) {
|
|
31
|
-
const message = "Node.js is not accessible or installed correctly. Install Node.js v20.
|
|
31
|
+
const message = "Node.js is not accessible or installed correctly. Install Node.js v20.19.0+ and ensure it's in your PATH.";
|
|
32
32
|
handleFailure(message, options, error);
|
|
33
33
|
return { ok: false, message, details: error.message };
|
|
34
34
|
}
|
package/eslint.config.cjs
CHANGED
|
@@ -42,7 +42,7 @@ const createConfig = ({ headerYear = '2025' } = {}) => {
|
|
|
42
42
|
languageOptions: {
|
|
43
43
|
parser: tsParser,
|
|
44
44
|
parserOptions: {
|
|
45
|
-
project: path.join(__dirname, 'tsconfig.json'),
|
|
45
|
+
project: path.join(__dirname, 'tsconfig.eslint.json'),
|
|
46
46
|
tsconfigRootDir: __dirname,
|
|
47
47
|
sourceType: 'module',
|
|
48
48
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hexabot-ai/cli",
|
|
3
|
-
"version": "3.1
|
|
3
|
+
"version": "3.2.1-alpha.0",
|
|
4
4
|
"description": "Official Hexabot CLI for creating and managing AI chatbot/agent projects built with Hexabot.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"hexabot": "dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"engines": {
|
|
11
|
-
"node": ">=20.
|
|
11
|
+
"node": ">=20.19.0"
|
|
12
12
|
},
|
|
13
13
|
"keywords": [],
|
|
14
14
|
"author": "Hexastack",
|
|
@@ -55,8 +55,6 @@
|
|
|
55
55
|
"start": "node dist/index.js",
|
|
56
56
|
"dev": "tsx watch src/index.ts",
|
|
57
57
|
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
|
|
58
|
-
"release:patch": "npm version patch && git push origin main --tags",
|
|
59
|
-
"release:minor": "npm version minor && git push origin main --tags",
|
|
60
58
|
"lint": "eslint \"src/**/*.ts\"",
|
|
61
59
|
"lint:fix": "eslint \"src/**/*.ts\" --fix"
|
|
62
60
|
}
|
|
@@ -138,7 +138,9 @@ describe('registerCreateCommand', () => {
|
|
|
138
138
|
.mockResolvedValueOnce('Anis')
|
|
139
139
|
.mockResolvedValueOnce('Bot')
|
|
140
140
|
.mockResolvedValueOnce('anis@example.com');
|
|
141
|
-
(password as any)
|
|
141
|
+
(password as any)
|
|
142
|
+
.mockResolvedValueOnce('Admin#123')
|
|
143
|
+
.mockResolvedValueOnce('Admin#123');
|
|
142
144
|
|
|
143
145
|
const program = new Command();
|
|
144
146
|
registerCreateCommand(program);
|
|
@@ -172,7 +174,7 @@ describe('registerCreateCommand', () => {
|
|
|
172
174
|
});
|
|
173
175
|
expect(exitSpy).not.toHaveBeenCalled();
|
|
174
176
|
expect(input).toHaveBeenCalledTimes(3);
|
|
175
|
-
expect(password).toHaveBeenCalledTimes(
|
|
177
|
+
expect(password).toHaveBeenCalledTimes(2);
|
|
176
178
|
});
|
|
177
179
|
|
|
178
180
|
it('fails cleanly when create runs in a non-interactive terminal', async () => {
|
package/src/commands/create.ts
CHANGED
|
@@ -206,6 +206,20 @@ const validateEmail = (value: string) => {
|
|
|
206
206
|
|
|
207
207
|
return true;
|
|
208
208
|
};
|
|
209
|
+
const validateAdminPassword = (value: string) => {
|
|
210
|
+
const trimmed = value.trim();
|
|
211
|
+
if (!trimmed) {
|
|
212
|
+
return 'Password is required.';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Keep the rule simple and explicit for CLI UX. Complexity policies vary by org.
|
|
216
|
+
const minLength = 12;
|
|
217
|
+
if (trimmed.length < minLength) {
|
|
218
|
+
return `Password must be at least ${minLength} characters.`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return true;
|
|
222
|
+
};
|
|
209
223
|
const assertInteractiveTerminal = () => {
|
|
210
224
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
211
225
|
throw new Error(
|
|
@@ -216,28 +230,48 @@ const assertInteractiveTerminal = () => {
|
|
|
216
230
|
const promptSeedAdminCredentials = async (): Promise<AdminSeedCredentials> => {
|
|
217
231
|
assertInteractiveTerminal();
|
|
218
232
|
|
|
233
|
+
console.log('\n');
|
|
234
|
+
console.log(chalk.bold('Admin account (initial credentials)'));
|
|
235
|
+
console.log(
|
|
236
|
+
chalk.gray(
|
|
237
|
+
'These details are used to seed the first admin user. You can change them later in your env file.',
|
|
238
|
+
),
|
|
239
|
+
);
|
|
240
|
+
console.log('\n');
|
|
241
|
+
|
|
219
242
|
const firstName = (
|
|
220
243
|
await input({
|
|
221
|
-
message: '
|
|
244
|
+
message: 'First name (e.g. Jhon)',
|
|
222
245
|
validate: requireValue('First name'),
|
|
223
246
|
})
|
|
224
247
|
).trim();
|
|
225
248
|
const lastName = (
|
|
226
249
|
await input({
|
|
227
|
-
message: '
|
|
250
|
+
message: 'Last name (e.g. Doe)',
|
|
228
251
|
validate: requireValue('Last name'),
|
|
229
252
|
})
|
|
230
253
|
).trim();
|
|
231
254
|
const email = (
|
|
232
255
|
await input({
|
|
233
|
-
message: '
|
|
256
|
+
message: 'Email (e.g. admin@company.com)',
|
|
234
257
|
validate: validateEmail,
|
|
235
258
|
})
|
|
236
259
|
).trim();
|
|
237
260
|
const adminPassword = await password({
|
|
238
|
-
message: '
|
|
261
|
+
message: 'Password (min 12 chars)',
|
|
239
262
|
mask: '*',
|
|
240
|
-
validate:
|
|
263
|
+
validate: validateAdminPassword,
|
|
264
|
+
});
|
|
265
|
+
await password({
|
|
266
|
+
message: 'Confirm password',
|
|
267
|
+
mask: '*',
|
|
268
|
+
validate: (value: string) => {
|
|
269
|
+
if (value !== adminPassword) {
|
|
270
|
+
return 'Passwords do not match.';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return true;
|
|
274
|
+
},
|
|
241
275
|
});
|
|
242
276
|
|
|
243
277
|
return {
|
|
@@ -8,7 +8,7 @@ import { execSync } from 'child_process';
|
|
|
8
8
|
|
|
9
9
|
import chalk from 'chalk';
|
|
10
10
|
|
|
11
|
-
const REQUIRED_NODE_VERSION = '20.
|
|
11
|
+
const REQUIRED_NODE_VERSION = '20.19.0';
|
|
12
12
|
|
|
13
13
|
export interface PrerequisiteOptions {
|
|
14
14
|
docker?: boolean;
|
|
@@ -51,7 +51,7 @@ export const checkNodeVersion = (
|
|
|
51
51
|
return { ok: false, message };
|
|
52
52
|
} catch (error) {
|
|
53
53
|
const message =
|
|
54
|
-
"Node.js is not accessible or installed correctly. Install Node.js v20.
|
|
54
|
+
"Node.js is not accessible or installed correctly. Install Node.js v20.19.0+ and ensure it's in your PATH.";
|
|
55
55
|
|
|
56
56
|
handleFailure(message, options, error);
|
|
57
57
|
|
package/test/__mocks__/chalk.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
/*
|
|
2
|
+
* Hexabot — Fair Core License (FCL-1.0-ALv2)
|
|
3
|
+
* Copyright (c) 2026 Hexastack.
|
|
4
|
+
* Full terms: see LICENSE.md.
|
|
5
|
+
*/
|
|
2
6
|
|
|
7
|
+
const passthrough = (value: string) => value;
|
|
3
8
|
const chalkMock = {
|
|
4
9
|
blue: passthrough,
|
|
10
|
+
bold: passthrough,
|
|
5
11
|
yellow: passthrough,
|
|
6
12
|
cyan: passthrough,
|
|
7
13
|
red: passthrough,
|