@kodus/cli 0.0.1
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/index.js +538 -0
- package/package.json +33 -0
package/index.js
ADDED
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { program } from "commander";
|
|
4
|
+
import inquirer from "inquirer";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import ora from "ora";
|
|
7
|
+
import fs from "fs-extra";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { execSync } from "child_process";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
import { dirname } from "path";
|
|
12
|
+
import crypto from "crypto";
|
|
13
|
+
import fetch from "node-fetch";
|
|
14
|
+
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = dirname(__filename);
|
|
17
|
+
|
|
18
|
+
// Helper functions
|
|
19
|
+
const generateSecretKey = () => crypto.randomBytes(32).toString("base64");
|
|
20
|
+
const generateDbPassword = () =>
|
|
21
|
+
crypto
|
|
22
|
+
.randomBytes(16)
|
|
23
|
+
.toString("base64")
|
|
24
|
+
.replace(/[^a-zA-Z0-9]/g, "")
|
|
25
|
+
.slice(0, 16);
|
|
26
|
+
|
|
27
|
+
const backupEnv = () => {
|
|
28
|
+
if (fs.existsSync(".env")) {
|
|
29
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
30
|
+
fs.copySync(".env", `.env.backup.${timestamp}`);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const restoreEnv = (backupFile) => {
|
|
37
|
+
if (fs.existsSync(backupFile)) {
|
|
38
|
+
fs.copySync(backupFile, ".env");
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const getLatestVersion = () => {
|
|
45
|
+
try {
|
|
46
|
+
const result = execSync("git describe --tags --abbrev=0", { stdio: "pipe" })
|
|
47
|
+
.toString()
|
|
48
|
+
.trim();
|
|
49
|
+
return result;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
Y;
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const getCurrentVersion = () => {
|
|
57
|
+
try {
|
|
58
|
+
const result = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
59
|
+
stdio: "pipe",
|
|
60
|
+
})
|
|
61
|
+
.toString()
|
|
62
|
+
.trim();
|
|
63
|
+
return result;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// CLI version
|
|
70
|
+
program.version("1.0.0");
|
|
71
|
+
|
|
72
|
+
// Install command
|
|
73
|
+
program
|
|
74
|
+
.command("install")
|
|
75
|
+
.description("Install and configure Kodus")
|
|
76
|
+
.action(async () => {
|
|
77
|
+
try {
|
|
78
|
+
// Check prerequisites
|
|
79
|
+
console.log(chalk.blue("\nš Checking prerequisites..."));
|
|
80
|
+
const spinner = ora("Checking Docker installation").start();
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
execSync("docker --version", { stdio: "ignore" });
|
|
84
|
+
spinner.succeed("Docker is installed");
|
|
85
|
+
} catch (error) {
|
|
86
|
+
spinner.fail("Docker is not installed");
|
|
87
|
+
console.error(
|
|
88
|
+
chalk.red(
|
|
89
|
+
"\nPlease install Docker first: https://docs.docker.com/get-docker/"
|
|
90
|
+
)
|
|
91
|
+
);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Environment type selection
|
|
96
|
+
const { envType } = await inquirer.prompt([
|
|
97
|
+
{
|
|
98
|
+
type: "list",
|
|
99
|
+
name: "envType",
|
|
100
|
+
message: "What type of environment are you setting up?",
|
|
101
|
+
choices: [
|
|
102
|
+
{ name: "Local (localhost)", value: "local" },
|
|
103
|
+
{ name: "External (with public URL)", value: "external" },
|
|
104
|
+
],
|
|
105
|
+
default: "local",
|
|
106
|
+
},
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
let baseUrl = "http://localhost:3000";
|
|
110
|
+
if (envType === "external") {
|
|
111
|
+
const { url } = await inquirer.prompt([
|
|
112
|
+
{
|
|
113
|
+
type: "input",
|
|
114
|
+
name: "url",
|
|
115
|
+
message: "Enter your public URL (e.g., https://kodus.yourdomain.com):",
|
|
116
|
+
validate: (input) => {
|
|
117
|
+
try {
|
|
118
|
+
const url = new URL(input);
|
|
119
|
+
if (!url.protocol.startsWith('http')) {
|
|
120
|
+
return "URL must start with http:// or https://";
|
|
121
|
+
}
|
|
122
|
+
if (!url.hostname.includes('.')) {
|
|
123
|
+
return "URL must include a valid domain name";
|
|
124
|
+
}
|
|
125
|
+
return true;
|
|
126
|
+
} catch (e) {
|
|
127
|
+
return "Please enter a valid URL (e.g., https://kodus.yourdomain.com)";
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
]);
|
|
132
|
+
baseUrl = url;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Git service selection
|
|
136
|
+
const { gitService } = await inquirer.prompt([
|
|
137
|
+
{
|
|
138
|
+
type: "list",
|
|
139
|
+
name: "gitService",
|
|
140
|
+
message: "Which Git service will you use?",
|
|
141
|
+
choices: [
|
|
142
|
+
{ name: "GitHub", value: "github" },
|
|
143
|
+
{ name: "GitLab", value: "gitlab" },
|
|
144
|
+
{ name: "Bitbucket", value: "bitbucket" },
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
// Git service configuration
|
|
150
|
+
let gitConfig = {};
|
|
151
|
+
|
|
152
|
+
if (envType === "local") {
|
|
153
|
+
console.log(chalk.yellow("\nā ļø IMPORTANT: If you're using a cloud Git service (GitHub, GitLab, Bitbucket), you'll need to configure the webhook manually."));
|
|
154
|
+
console.log(chalk.yellow("For local or self-hosted Git instances, no additional configuration is needed."));
|
|
155
|
+
console.log(chalk.yellow("The webhook URL will be:"));
|
|
156
|
+
console.log(chalk.white(`${baseUrl}/api/webhook/${gitService}`));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Configure webhook URL based on the selected Git service
|
|
160
|
+
if (gitService === "github") {
|
|
161
|
+
gitConfig = {
|
|
162
|
+
API_GITHUB_CODE_MANAGEMENT_WEBHOOK: `${baseUrl}/api/webhook/${gitService}`,
|
|
163
|
+
};
|
|
164
|
+
} else if (gitService === "gitlab") {
|
|
165
|
+
gitConfig = {
|
|
166
|
+
API_GITLAB_CODE_MANAGEMENT_WEBHOOK: `${baseUrl}/api/webhook/${gitService}`,
|
|
167
|
+
};
|
|
168
|
+
} else if (gitService === "bitbucket") {
|
|
169
|
+
gitConfig = {
|
|
170
|
+
GLOBAL_BITBUCKET_CODE_MANAGEMENT_WEBHOOK: `${baseUrl}/api/webhook/${gitService}`,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Database configuration
|
|
175
|
+
const { useDefaultDb } = await inquirer.prompt([
|
|
176
|
+
{
|
|
177
|
+
type: "confirm",
|
|
178
|
+
name: "useDefaultDb",
|
|
179
|
+
message: "Would you like to use default database configurations?",
|
|
180
|
+
default: true,
|
|
181
|
+
},
|
|
182
|
+
]);
|
|
183
|
+
|
|
184
|
+
// LLM API Keys configuration
|
|
185
|
+
console.log(chalk.blue("\nš Configuring LLM API Keys..."));
|
|
186
|
+
const llmKeys = await inquirer.prompt([
|
|
187
|
+
{
|
|
188
|
+
type: "input",
|
|
189
|
+
name: "openAiKey",
|
|
190
|
+
message: "Enter your OpenAI API key (optional):",
|
|
191
|
+
default: "",
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
type: "input",
|
|
195
|
+
name: "googleAiKey",
|
|
196
|
+
message: "Enter your Google AI API key (optional):",
|
|
197
|
+
default: "",
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
type: "input",
|
|
201
|
+
name: "anthropicKey",
|
|
202
|
+
message: "Enter your Anthropic API key (optional):",
|
|
203
|
+
default: "",
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
type: "input",
|
|
207
|
+
name: "fireworksKey",
|
|
208
|
+
message: "Enter your Fireworks API key (optional):",
|
|
209
|
+
default: "",
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
type: "input",
|
|
213
|
+
name: "novitaKey",
|
|
214
|
+
message: "Enter your Novita AI API key (optional):",
|
|
215
|
+
default: "",
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
type: "input",
|
|
219
|
+
name: "vertexKey",
|
|
220
|
+
message: "Enter your Vertex AI API key (optional):",
|
|
221
|
+
default: "",
|
|
222
|
+
},
|
|
223
|
+
]);
|
|
224
|
+
|
|
225
|
+
// Start installation
|
|
226
|
+
console.log(chalk.blue("\nš Starting installation..."));
|
|
227
|
+
|
|
228
|
+
// Generate .env content
|
|
229
|
+
const envContent = {
|
|
230
|
+
// Base configuration
|
|
231
|
+
WEB_NODE_ENV: "development",
|
|
232
|
+
WEB_HOSTNAME_API: "localhost",
|
|
233
|
+
WEB_PORT_API: "3001",
|
|
234
|
+
WEB_PORT: "3000",
|
|
235
|
+
WEB_NEXTAUTH_URL: "http://localhost:3000",
|
|
236
|
+
WEB_NEXTAUTH_SECRET: generateSecretKey(),
|
|
237
|
+
WEB_JWT_SECRET_KEY: generateSecretKey(),
|
|
238
|
+
|
|
239
|
+
API_NODE_ENV: "development",
|
|
240
|
+
API_LOG_LEVEL: "error",
|
|
241
|
+
API_LOG_PRETTY: "true",
|
|
242
|
+
API_HOST: "0.0.0.0",
|
|
243
|
+
API_PORT: "3001",
|
|
244
|
+
API_RATE_MAX_REQUEST: "100",
|
|
245
|
+
API_RATE_INTERVAL: "1000",
|
|
246
|
+
API_JWT_EXPIRES_IN: "365d",
|
|
247
|
+
API_JWT_SECRET: generateSecretKey(),
|
|
248
|
+
API_JWT_REFRESHSECRET: generateSecretKey(),
|
|
249
|
+
API_JWT_REFRESH_EXPIRES_IN: "7d",
|
|
250
|
+
|
|
251
|
+
GLOBAL_API_CONTAINER_NAME: "kodus-orchestrator-prod",
|
|
252
|
+
|
|
253
|
+
// Database
|
|
254
|
+
API_DATABASE_ENV: "development",
|
|
255
|
+
API_PG_DB_USERNAME: "kodusdev",
|
|
256
|
+
API_PG_DB_PASSWORD: generateDbPassword(),
|
|
257
|
+
API_PG_DB_DATABASE: "kodus_db",
|
|
258
|
+
API_PG_DB_HOST: "db_kodus_postgres",
|
|
259
|
+
API_PG_DB_PORT: "5432",
|
|
260
|
+
|
|
261
|
+
API_MG_DB_USERNAME: "kodusdev",
|
|
262
|
+
API_MG_DB_PASSWORD: generateDbPassword(),
|
|
263
|
+
API_MG_DB_DATABASE: "kodus_db",
|
|
264
|
+
API_MG_DB_HOST: "db_kodus_mongodb",
|
|
265
|
+
API_MG_DB_PORT: "27017",
|
|
266
|
+
API_MG_DB_PRODUCTION_CONFIG: "",
|
|
267
|
+
|
|
268
|
+
// LLM API Keys
|
|
269
|
+
API_OPEN_AI_APIKEY: llmKeys.openAiKey,
|
|
270
|
+
API_GOOGLE_AI_API_KEY: llmKeys.googleAiKey,
|
|
271
|
+
API_ANTHROPIC_API_KEY: llmKeys.anthropicKey,
|
|
272
|
+
API_FIREWORKS_API_KEY: llmKeys.fireworksKey,
|
|
273
|
+
API_NOVITA_AI_API_KEY: llmKeys.novitaKey,
|
|
274
|
+
API_VERTEX_AI_API_KEY: llmKeys.vertexKey,
|
|
275
|
+
|
|
276
|
+
// LLM Models
|
|
277
|
+
API_LLM_MODEL_CHATGPT_3_5_TURBO: "gpt-4o-mini",
|
|
278
|
+
API_LLM_MODEL_CHATGPT_3_5_TURBO_16K: "gpt-4o-mini",
|
|
279
|
+
API_LLM_MODEL_CHATGPT_4: "gpt-4o-mini",
|
|
280
|
+
API_LLM_MODEL_CHATGPT_4_TURBO: "gpt-4o-mini",
|
|
281
|
+
API_LLM_MODEL_CHATGPT_4_ALL: "gpt-4o",
|
|
282
|
+
API_LLM_MODEL_CHATGPT_4_ALL_MINI: "gpt-4o-mini",
|
|
283
|
+
API_LLM_MODEL_CLAUDE_3_5_SONNET: "claude-3-5-sonnet-20241022",
|
|
284
|
+
API_LLM_MODEL_CLAUDE_3_5_SONNET_20241022: "claude-3-5-sonnet-20241022",
|
|
285
|
+
API_LLM_MODEL_GEMINI_1_5_PRO: "gpt-4o-mini",
|
|
286
|
+
API_LLM_MODEL_GEMINI_1_5_PRO_EXP: "gpt-4o-mini",
|
|
287
|
+
|
|
288
|
+
// Git service configuration
|
|
289
|
+
...gitConfig,
|
|
290
|
+
|
|
291
|
+
// Ports
|
|
292
|
+
WEB_PORT: "3000",
|
|
293
|
+
API_PORT: "3001",
|
|
294
|
+
|
|
295
|
+
// RabbitMQ
|
|
296
|
+
RABBITMQ_DEFAULT_USER: "kodus",
|
|
297
|
+
RABBITMQ_DEFAULT_PASS: generateDbPassword(),
|
|
298
|
+
|
|
299
|
+
// Monitoring
|
|
300
|
+
GRAFANA_ADMIN_PASSWORD: generateDbPassword(),
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// Create .env file
|
|
304
|
+
const envSpinner = ora("Creating .env file").start();
|
|
305
|
+
const envContentString = Object.entries(envContent)
|
|
306
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
307
|
+
.join("\n");
|
|
308
|
+
fs.writeFileSync(".env", envContentString);
|
|
309
|
+
envSpinner.succeed("Created .env file");
|
|
310
|
+
|
|
311
|
+
// Start containers
|
|
312
|
+
const dockerSpinner = ora("Starting containers").start();
|
|
313
|
+
try {
|
|
314
|
+
execSync("docker-compose up -d", { stdio: "pipe" });
|
|
315
|
+
dockerSpinner.succeed("Containers started");
|
|
316
|
+
} catch (error) {
|
|
317
|
+
dockerSpinner.fail("Failed to start containers");
|
|
318
|
+
console.error(chalk.red("\nError details:"));
|
|
319
|
+
console.error(chalk.white(error.stdout?.toString() || error.message));
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const maxAttempts = 30; // 5 minutes with 10 second intervals
|
|
324
|
+
|
|
325
|
+
// Wait for PostgreSQL to be ready
|
|
326
|
+
const pgSpinner = ora("Waiting for PostgreSQL to be ready").start();
|
|
327
|
+
let isPgReady = false;
|
|
328
|
+
let pgAttempts = 0;
|
|
329
|
+
|
|
330
|
+
while (!isPgReady && pgAttempts < maxAttempts) {
|
|
331
|
+
try {
|
|
332
|
+
const logs = execSync("docker-compose logs db_kodus_postgres", { stdio: "pipe" }).toString();
|
|
333
|
+
if (logs.includes("database system is ready to accept connections")) {
|
|
334
|
+
isPgReady = true;
|
|
335
|
+
pgSpinner.succeed("PostgreSQL is ready");
|
|
336
|
+
} else {
|
|
337
|
+
pgAttempts++;
|
|
338
|
+
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
339
|
+
}
|
|
340
|
+
} catch (error) {
|
|
341
|
+
pgAttempts++;
|
|
342
|
+
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (!isPgReady) {
|
|
347
|
+
pgSpinner.fail("PostgreSQL failed to start");
|
|
348
|
+
console.error(chalk.red("\nError: PostgreSQL failed to start properly"));
|
|
349
|
+
console.error(chalk.yellow("\nTroubleshooting steps:"));
|
|
350
|
+
console.error(chalk.white("1. Check PostgreSQL logs: docker-compose logs db_kodus_postgres"));
|
|
351
|
+
console.error(chalk.white("2. Verify PostgreSQL container is running: docker-compose ps db_kodus_postgres"));
|
|
352
|
+
process.exit(1);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Wait for MongoDB to be ready
|
|
356
|
+
const mongoSpinner = ora("Waiting for MongoDB to be ready").start();
|
|
357
|
+
let isMongoReady = false;
|
|
358
|
+
let mongoAttempts = 0;
|
|
359
|
+
|
|
360
|
+
while (!isMongoReady && mongoAttempts < maxAttempts) {
|
|
361
|
+
try {
|
|
362
|
+
const logs = execSync("docker-compose logs db_kodus_mongodb", { stdio: "pipe" }).toString();
|
|
363
|
+
if (logs.includes("Waiting for connections") || logs.includes("Connection accepted")) {
|
|
364
|
+
isMongoReady = true;
|
|
365
|
+
mongoSpinner.succeed("MongoDB is ready");
|
|
366
|
+
} else {
|
|
367
|
+
mongoAttempts++;
|
|
368
|
+
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
369
|
+
}
|
|
370
|
+
} catch (error) {
|
|
371
|
+
mongoAttempts++;
|
|
372
|
+
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (!isMongoReady) {
|
|
377
|
+
mongoSpinner.fail("MongoDB failed to start");
|
|
378
|
+
console.error(chalk.red("\nError: MongoDB failed to start properly"));
|
|
379
|
+
console.error(chalk.yellow("\nTroubleshooting steps:"));
|
|
380
|
+
console.error(chalk.white("1. Check MongoDB logs: docker-compose logs db_kodus_mongodb"));
|
|
381
|
+
console.error(chalk.white("2. Verify MongoDB container is running: docker-compose ps db_kodus_mongodb"));
|
|
382
|
+
process.exit(1);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Now wait for orchestrator to be ready
|
|
386
|
+
const healthSpinner = ora("Waiting for orchestrator to be ready").start();
|
|
387
|
+
let isOrchestratorReady = false;
|
|
388
|
+
let attempts = 0;
|
|
389
|
+
|
|
390
|
+
const criticalErrors = [
|
|
391
|
+
"password authentication failed",
|
|
392
|
+
"Unable to connect to the database",
|
|
393
|
+
"FATAL:",
|
|
394
|
+
"MongoServerError:",
|
|
395
|
+
"connection refused"
|
|
396
|
+
];
|
|
397
|
+
|
|
398
|
+
while (!isOrchestratorReady && attempts < maxAttempts) {
|
|
399
|
+
try {
|
|
400
|
+
// Check if container is running
|
|
401
|
+
const containerStatus = execSync("docker-compose ps orchestrator --format json", { stdio: "pipe" }).toString();
|
|
402
|
+
const status = JSON.parse(containerStatus)[0];
|
|
403
|
+
|
|
404
|
+
if (status.State === "running") {
|
|
405
|
+
// Check logs for any errors
|
|
406
|
+
const logs = execSync("docker-compose logs --tail=50 orchestrator", { stdio: "pipe" }).toString();
|
|
407
|
+
|
|
408
|
+
// Check for critical errors that should stop the process
|
|
409
|
+
const foundCriticalError = criticalErrors.find(error => logs.includes(error));
|
|
410
|
+
if (foundCriticalError) {
|
|
411
|
+
healthSpinner.fail("Critical error detected");
|
|
412
|
+
console.error(chalk.red("\nError: Database connection failed"));
|
|
413
|
+
console.error(chalk.yellow("\nTroubleshooting steps:"));
|
|
414
|
+
console.error(chalk.white("1. Check if the database passwords in .env match the ones in your database"));
|
|
415
|
+
console.error(chalk.white("2. Verify if the database containers are running: docker-compose ps"));
|
|
416
|
+
console.error(chalk.white("3. Check database logs: docker-compose logs db_kodus_postgres db_kodus_mongodb"));
|
|
417
|
+
console.error(chalk.white("\nFull error log:"));
|
|
418
|
+
console.error(chalk.white(logs));
|
|
419
|
+
process.exit(1);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// If no critical errors and we see successful connection messages, proceed
|
|
423
|
+
if (logs.includes("Database connection established") || logs.includes("Connected to MongoDB")) {
|
|
424
|
+
isOrchestratorReady = true;
|
|
425
|
+
healthSpinner.succeed("Orchestrator is ready");
|
|
426
|
+
} else {
|
|
427
|
+
attempts++;
|
|
428
|
+
await new Promise(resolve => setTimeout(resolve, 10000)); // Wait 10 seconds
|
|
429
|
+
}
|
|
430
|
+
} else {
|
|
431
|
+
attempts++;
|
|
432
|
+
await new Promise(resolve => setTimeout(resolve, 10000)); // Wait 10 seconds
|
|
433
|
+
}
|
|
434
|
+
} catch (error) {
|
|
435
|
+
attempts++;
|
|
436
|
+
await new Promise(resolve => setTimeout(resolve, 10000)); // Wait 10 seconds
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (!isOrchestratorReady) {
|
|
441
|
+
healthSpinner.fail("Orchestrator failed to start");
|
|
442
|
+
console.error(chalk.red("\nError: The orchestrator service failed to start properly."));
|
|
443
|
+
console.error(chalk.yellow("\nTroubleshooting steps:"));
|
|
444
|
+
console.error(chalk.white("1. Check the orchestrator logs: docker-compose logs orchestrator"));
|
|
445
|
+
console.error(chalk.white("2. Verify all required services are running: docker-compose ps"));
|
|
446
|
+
console.error(chalk.white("3. Try restarting the services: docker-compose restart"));
|
|
447
|
+
process.exit(1);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Setup database
|
|
451
|
+
const dbSpinner = ora("Setting up database").start();
|
|
452
|
+
try {
|
|
453
|
+
execSync("./scripts/setup-db.sh", { stdio: "pipe" });
|
|
454
|
+
dbSpinner.succeed("Database setup completed");
|
|
455
|
+
} catch (error) {
|
|
456
|
+
dbSpinner.fail("Failed to setup database");
|
|
457
|
+
console.error(chalk.red("\nError details:"));
|
|
458
|
+
console.error(chalk.white(error.stdout?.toString() || error.message));
|
|
459
|
+
process.exit(1);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Verify database connection by checking orchestrator logs
|
|
463
|
+
const dbCheckSpinner = ora("Verifying database connection").start();
|
|
464
|
+
try {
|
|
465
|
+
const logs = execSync("docker-compose logs --tail=50 orchestrator", { stdio: "pipe" }).toString();
|
|
466
|
+
|
|
467
|
+
// Look for database connection errors in logs
|
|
468
|
+
if (logs.includes("database connection error") || logs.includes("Database connection failed")) {
|
|
469
|
+
throw new Error("Database connection errors found in logs");
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
dbCheckSpinner.succeed("Database connection verified");
|
|
473
|
+
} catch (error) {
|
|
474
|
+
dbCheckSpinner.fail("Failed to verify database connection");
|
|
475
|
+
console.error(chalk.red("\nError: Could not verify database connection."));
|
|
476
|
+
console.error(chalk.yellow("Please check the logs with: docker-compose logs orchestrator"));
|
|
477
|
+
process.exit(1);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Installation complete
|
|
481
|
+
console.log(chalk.green("\n⨠Installation completed successfully!"));
|
|
482
|
+
|
|
483
|
+
console.log(chalk.blue("\nš Installation Summary:"));
|
|
484
|
+
console.log(
|
|
485
|
+
chalk.white(
|
|
486
|
+
` - Environment: ${envType === "local" ? "Local" : "External"}`
|
|
487
|
+
)
|
|
488
|
+
);
|
|
489
|
+
if (envType === "external") {
|
|
490
|
+
console.log(chalk.white(` - Base URL: ${baseUrl}`));
|
|
491
|
+
}
|
|
492
|
+
console.log(
|
|
493
|
+
chalk.white(
|
|
494
|
+
` - Git Service: ${
|
|
495
|
+
gitService.charAt(0).toUpperCase() + gitService.slice(1)
|
|
496
|
+
}`
|
|
497
|
+
)
|
|
498
|
+
);
|
|
499
|
+
console.log(
|
|
500
|
+
chalk.white(
|
|
501
|
+
` - Database: ${
|
|
502
|
+
useDefaultDb ? "Default configuration" : "Custom configuration"
|
|
503
|
+
}`
|
|
504
|
+
)
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
console.log(chalk.blue("\nš Access URLs:"));
|
|
508
|
+
console.log(chalk.white(` - Web Interface: ${baseUrl}`));
|
|
509
|
+
console.log(chalk.white(" - Grafana Dashboard: http://localhost:3001"));
|
|
510
|
+
console.log(
|
|
511
|
+
chalk.white(" - RabbitMQ Management: http://localhost:15672")
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
console.log(chalk.blue("\nš Next Steps:"));
|
|
515
|
+
console.log(chalk.white(" 1. Access the web interface"));
|
|
516
|
+
console.log(chalk.white(" 2. Set up your first user"));
|
|
517
|
+
console.log(chalk.white(" 3. Start using Kodus!"));
|
|
518
|
+
|
|
519
|
+
const { startServices } = await inquirer.prompt([
|
|
520
|
+
{
|
|
521
|
+
type: "confirm",
|
|
522
|
+
name: "startServices",
|
|
523
|
+
message: "Would you like to start the services now?",
|
|
524
|
+
default: true,
|
|
525
|
+
},
|
|
526
|
+
]);
|
|
527
|
+
|
|
528
|
+
if (startServices) {
|
|
529
|
+
execSync("docker-compose up -d", { stdio: "ignore" });
|
|
530
|
+
console.log(chalk.green("\nServices started successfully!"));
|
|
531
|
+
}
|
|
532
|
+
} catch (error) {
|
|
533
|
+
console.error(chalk.red("\nā Installation failed:"), error.message);
|
|
534
|
+
process.exit(1);
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
program.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kodus/cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CLI tool for Kodus installation and management",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"preferGlobal": true,
|
|
8
|
+
"bin": {
|
|
9
|
+
"kodus": "./index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
13
|
+
},
|
|
14
|
+
"keywords": ["kodus", "cli", "installation", "deployment"],
|
|
15
|
+
"author": "Kodus",
|
|
16
|
+
"license": "ISC",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/kodus/kodus-cli.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/kodus/kodus-cli/issues"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/kodus/kodus-cli#readme",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"chalk": "^4.1.2",
|
|
27
|
+
"commander": "^11.1.0",
|
|
28
|
+
"dotenv": "^16.4.1",
|
|
29
|
+
"fs-extra": "^11.2.0",
|
|
30
|
+
"inquirer": "^8.2.6",
|
|
31
|
+
"ora": "^5.4.1"
|
|
32
|
+
}
|
|
33
|
+
}
|