@chimerai/cli 0.2.73
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/LICENSE +21 -0
- package/README.md +293 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +317 -0
- package/dist/commands/add.d.ts +11 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +2126 -0
- package/dist/commands/create.d.ts +12 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +1703 -0
- package/dist/commands/deploy.d.ts +11 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +219 -0
- package/dist/commands/dev.d.ts +17 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +206 -0
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +728 -0
- package/dist/commands/generate.d.ts +19 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +429 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +269 -0
- package/dist/commands/list.d.ts +12 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +328 -0
- package/dist/commands/migrate.d.ts +14 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +197 -0
- package/dist/commands/plugin.d.ts +10 -0
- package/dist/commands/plugin.d.ts.map +1 -0
- package/dist/commands/plugin.js +239 -0
- package/dist/commands/remove.d.ts +11 -0
- package/dist/commands/remove.d.ts.map +1 -0
- package/dist/commands/remove.js +472 -0
- package/dist/commands/secret.d.ts +12 -0
- package/dist/commands/secret.d.ts.map +1 -0
- package/dist/commands/secret.js +102 -0
- package/dist/commands/setup.d.ts +9 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +788 -0
- package/dist/commands/update.d.ts +14 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +211 -0
- package/dist/commands/use.d.ts +9 -0
- package/dist/commands/use.d.ts.map +1 -0
- package/dist/commands/use.js +51 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/license.d.ts +55 -0
- package/dist/license.d.ts.map +1 -0
- package/dist/license.js +258 -0
- package/dist/scanner.d.ts +31 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +113 -0
- package/dist/schema-manager.d.ts +26 -0
- package/dist/schema-manager.d.ts.map +1 -0
- package/dist/schema-manager.js +132 -0
- package/dist/templates/admin.d.ts +49 -0
- package/dist/templates/admin.d.ts.map +1 -0
- package/dist/templates/admin.js +1358 -0
- package/dist/templates/ai-routes.d.ts +17 -0
- package/dist/templates/ai-routes.d.ts.map +1 -0
- package/dist/templates/ai-routes.js +1130 -0
- package/dist/templates/ai-service-tools.d.ts +22 -0
- package/dist/templates/ai-service-tools.d.ts.map +1 -0
- package/dist/templates/ai-service-tools.js +1424 -0
- package/dist/templates/ai-service.d.ts +66 -0
- package/dist/templates/ai-service.d.ts.map +1 -0
- package/dist/templates/ai-service.js +2202 -0
- package/dist/templates/api-routes.d.ts +108 -0
- package/dist/templates/api-routes.d.ts.map +1 -0
- package/dist/templates/api-routes.js +1219 -0
- package/dist/templates/auth.d.ts +48 -0
- package/dist/templates/auth.d.ts.map +1 -0
- package/dist/templates/auth.js +381 -0
- package/dist/templates/billing.d.ts +44 -0
- package/dist/templates/billing.d.ts.map +1 -0
- package/dist/templates/billing.js +551 -0
- package/dist/templates/chat.d.ts +63 -0
- package/dist/templates/chat.d.ts.map +1 -0
- package/dist/templates/chat.js +1979 -0
- package/dist/templates/components.d.ts +22 -0
- package/dist/templates/components.d.ts.map +1 -0
- package/dist/templates/components.js +672 -0
- package/dist/templates/config.d.ts +6 -0
- package/dist/templates/config.d.ts.map +1 -0
- package/dist/templates/config.js +86 -0
- package/dist/templates/docker.d.ts +25 -0
- package/dist/templates/docker.d.ts.map +1 -0
- package/dist/templates/docker.js +165 -0
- package/dist/templates/gdpr.d.ts +16 -0
- package/dist/templates/gdpr.d.ts.map +1 -0
- package/dist/templates/gdpr.js +259 -0
- package/dist/templates/index.d.ts +77 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +339 -0
- package/dist/templates/layout.d.ts +67 -0
- package/dist/templates/layout.d.ts.map +1 -0
- package/dist/templates/layout.js +670 -0
- package/dist/templates/mfa.d.ts +23 -0
- package/dist/templates/mfa.d.ts.map +1 -0
- package/dist/templates/mfa.js +353 -0
- package/dist/templates/middleware.d.ts +12 -0
- package/dist/templates/middleware.d.ts.map +1 -0
- package/dist/templates/middleware.js +116 -0
- package/dist/templates/prisma.d.ts +35 -0
- package/dist/templates/prisma.d.ts.map +1 -0
- package/dist/templates/prisma.js +724 -0
- package/dist/templates/provider-routes.d.ts +21 -0
- package/dist/templates/provider-routes.d.ts.map +1 -0
- package/dist/templates/provider-routes.js +1203 -0
- package/dist/templates/rag.d.ts +48 -0
- package/dist/templates/rag.d.ts.map +1 -0
- package/dist/templates/rag.js +532 -0
- package/dist/templates/widget.d.ts +64 -0
- package/dist/templates/widget.d.ts.map +1 -0
- package/dist/templates/widget.js +1360 -0
- package/dist/utils/provider-db.d.ts +63 -0
- package/dist/utils/provider-db.d.ts.map +1 -0
- package/dist/utils/provider-db.js +300 -0
- package/dist/utils.d.ts +78 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +330 -0
- package/package.json +60 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deploy Command - Deployment preparation and config generation
|
|
3
|
+
*/
|
|
4
|
+
interface DeployOptions {
|
|
5
|
+
dir: string;
|
|
6
|
+
target: string;
|
|
7
|
+
checkOnly: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function deployCommand(options: DeployOptions): Promise<void>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=deploy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAAA;;GAEG;AAaH,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;CACpB;AAQD,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,iBAkCzD"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Deploy Command - Deployment preparation and config generation
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.deployCommand = deployCommand;
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const ora_1 = __importDefault(require("ora"));
|
|
12
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const utils_js_1 = require("../utils.js");
|
|
15
|
+
const index_js_1 = require("../templates/index.js");
|
|
16
|
+
async function deployCommand(options) {
|
|
17
|
+
console.log(chalk_1.default.bold.cyan('\n🚀 ChimerAI Deploy\n'));
|
|
18
|
+
const targetDir = (0, utils_js_1.resolveTargetDir)(options.dir);
|
|
19
|
+
if (!(0, utils_js_1.validateWorkspace)(targetDir))
|
|
20
|
+
return;
|
|
21
|
+
const checks = await runChecks(targetDir);
|
|
22
|
+
displayChecks(checks);
|
|
23
|
+
const fails = checks.filter((c) => c.status === 'fail');
|
|
24
|
+
if (fails.length > 0) {
|
|
25
|
+
console.log(chalk_1.default.bold.red('\n❌ Fix issues above before deploying.\n'));
|
|
26
|
+
if (options.checkOnly)
|
|
27
|
+
return;
|
|
28
|
+
return; // Graceful exit instead of process.exit(1)
|
|
29
|
+
}
|
|
30
|
+
if (options.checkOnly) {
|
|
31
|
+
console.log(chalk_1.default.bold.green('\n✅ All deployment checks passed!\n'));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
switch (options.target) {
|
|
35
|
+
case 'vercel':
|
|
36
|
+
await deployVercel(targetDir);
|
|
37
|
+
break;
|
|
38
|
+
case 'docker':
|
|
39
|
+
await deployDocker(targetDir);
|
|
40
|
+
break;
|
|
41
|
+
case 'railway':
|
|
42
|
+
await deployRailway(targetDir);
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
console.log(chalk_1.default.red(`❌ Unknown target: ${options.target}`));
|
|
46
|
+
console.log(chalk_1.default.yellow('Targets: vercel, docker, railway'));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function runChecks(targetDir) {
|
|
50
|
+
const checks = [];
|
|
51
|
+
const spinner = (0, ora_1.default)('Running deployment checks...').start();
|
|
52
|
+
try {
|
|
53
|
+
const pkg = await fs_extra_1.default.readJson(path_1.default.join(targetDir, 'package.json'));
|
|
54
|
+
checks.push(pkg.scripts?.build
|
|
55
|
+
? { name: 'Build script', status: 'pass', message: 'Found' }
|
|
56
|
+
: { name: 'Build script', status: 'fail', message: 'Missing in package.json' });
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
checks.push({ name: 'Build script', status: 'fail', message: 'Cannot read package.json' });
|
|
60
|
+
}
|
|
61
|
+
const envPath = path_1.default.join(targetDir, '.env');
|
|
62
|
+
if (fs_extra_1.default.existsSync(envPath)) {
|
|
63
|
+
const env = await fs_extra_1.default.readFile(envPath, 'utf-8');
|
|
64
|
+
checks.push(env.includes('localhost')
|
|
65
|
+
? {
|
|
66
|
+
name: 'DATABASE_URL',
|
|
67
|
+
status: 'warn',
|
|
68
|
+
message: 'Using localhost - update for production',
|
|
69
|
+
}
|
|
70
|
+
: { name: 'DATABASE_URL', status: 'pass', message: 'Production URL detected' });
|
|
71
|
+
checks.push(env.includes('your-secret-key-change-in-production')
|
|
72
|
+
? {
|
|
73
|
+
name: 'NEXTAUTH_SECRET',
|
|
74
|
+
status: 'warn',
|
|
75
|
+
message: 'Using default - change for production',
|
|
76
|
+
}
|
|
77
|
+
: { name: 'NEXTAUTH_SECRET', status: 'pass', message: 'Custom secret set' });
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
checks.push({ name: 'Environment', status: 'fail', message: 'No .env file' });
|
|
81
|
+
}
|
|
82
|
+
checks.push(fs_extra_1.default.existsSync(path_1.default.join(targetDir, 'prisma/schema.prisma'))
|
|
83
|
+
? { name: 'Prisma schema', status: 'pass', message: 'Found' }
|
|
84
|
+
: { name: 'Prisma schema', status: 'fail', message: 'Missing' });
|
|
85
|
+
const gpath = path_1.default.join(targetDir, '.gitignore');
|
|
86
|
+
if (fs_extra_1.default.existsSync(gpath)) {
|
|
87
|
+
const g = await fs_extra_1.default.readFile(gpath, 'utf-8');
|
|
88
|
+
checks.push(g.includes('.env')
|
|
89
|
+
? { name: 'Secrets protection', status: 'pass', message: '.env excluded' }
|
|
90
|
+
: { name: 'Secrets protection', status: 'fail', message: '.env may be committed!' });
|
|
91
|
+
}
|
|
92
|
+
spinner.stop();
|
|
93
|
+
return checks;
|
|
94
|
+
}
|
|
95
|
+
function displayChecks(checks) {
|
|
96
|
+
console.log(chalk_1.default.bold('📋 Deployment Readiness:\n'));
|
|
97
|
+
for (const c of checks) {
|
|
98
|
+
const icon = c.status === 'pass'
|
|
99
|
+
? chalk_1.default.green('✓')
|
|
100
|
+
: c.status === 'warn'
|
|
101
|
+
? chalk_1.default.yellow('⚠')
|
|
102
|
+
: chalk_1.default.red('✗');
|
|
103
|
+
console.log(` ${icon} ${c.name}: ${c.message}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/* ------------------------------------------------------------------ */
|
|
107
|
+
/* Deployment Targets */
|
|
108
|
+
/* ------------------------------------------------------------------ */
|
|
109
|
+
async function deployVercel(targetDir) {
|
|
110
|
+
const spinner = (0, ora_1.default)('Generating Vercel config...').start();
|
|
111
|
+
try {
|
|
112
|
+
await fs_extra_1.default.writeJson(path_1.default.join(targetDir, 'vercel.json'), {
|
|
113
|
+
$schema: 'https://openapi.vercel.sh/vercel.json',
|
|
114
|
+
framework: 'nextjs',
|
|
115
|
+
buildCommand: 'prisma generate && next build',
|
|
116
|
+
installCommand: 'pnpm install',
|
|
117
|
+
}, { spaces: 2 });
|
|
118
|
+
spinner.succeed('Created vercel.json');
|
|
119
|
+
console.log(chalk_1.default.bold.green('\n✅ Vercel config generated!\n'));
|
|
120
|
+
console.log(chalk_1.default.cyan('Next steps:'));
|
|
121
|
+
console.log(chalk_1.default.white(' 1. Set env vars in Vercel dashboard'));
|
|
122
|
+
console.log(chalk_1.default.white(' 2. Deploy: vercel --prod\n'));
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
spinner.fail('Failed to generate Vercel config');
|
|
126
|
+
console.error(chalk_1.default.red(error.message));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function deployDocker(targetDir) {
|
|
130
|
+
const spinner = (0, ora_1.default)('Generating Docker config...').start();
|
|
131
|
+
try {
|
|
132
|
+
// Generate Docker files using inline generators (no external template dependency)
|
|
133
|
+
await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'Dockerfile'), (0, index_js_1.generateDockerfile)());
|
|
134
|
+
spinner.succeed('Created Dockerfile (multi-stage)');
|
|
135
|
+
await fs_extra_1.default.writeFile(path_1.default.join(targetDir, '.dockerignore'), (0, index_js_1.generateDockerignore)());
|
|
136
|
+
spinner.succeed('Created .dockerignore');
|
|
137
|
+
await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'docker-compose.prod.yml'), (0, index_js_1.generateDockerComposeProd)());
|
|
138
|
+
spinner.succeed('Created docker-compose.prod.yml');
|
|
139
|
+
console.log(chalk_1.default.bold.green('\n✅ Docker config generated!\n'));
|
|
140
|
+
console.log(chalk_1.default.cyan('Next steps:'));
|
|
141
|
+
console.log(chalk_1.default.white(' 1. Build: docker build -t chimerai-app .'));
|
|
142
|
+
console.log(chalk_1.default.white(' 2. Or: docker-compose -f docker-compose.prod.yml up -d\n'));
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
spinner.fail('Failed to generate Docker config');
|
|
146
|
+
console.error(chalk_1.default.red(error.message));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function deployRailway(targetDir) {
|
|
150
|
+
const spinner = (0, ora_1.default)('Generating Railway config...').start();
|
|
151
|
+
try {
|
|
152
|
+
// Detect start command based on package.json
|
|
153
|
+
let startCommand = 'npx prisma migrate deploy && pnpm start';
|
|
154
|
+
try {
|
|
155
|
+
const pkg = await fs_extra_1.default.readJson(path_1.default.join(targetDir, 'package.json'));
|
|
156
|
+
if (pkg.scripts?.start) {
|
|
157
|
+
startCommand = `npx prisma migrate deploy && ${pkg.scripts.start.includes('node') ? pkg.scripts.start : 'pnpm start'}`;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// No start script — check for standalone output
|
|
161
|
+
const nextConfig = path_1.default.join(targetDir, 'next.config.js');
|
|
162
|
+
const nextConfigMjs = path_1.default.join(targetDir, 'next.config.mjs');
|
|
163
|
+
let hasStandalone = false;
|
|
164
|
+
for (const cfg of [nextConfig, nextConfigMjs]) {
|
|
165
|
+
if (fs_extra_1.default.existsSync(cfg)) {
|
|
166
|
+
const content = await fs_extra_1.default.readFile(cfg, 'utf-8');
|
|
167
|
+
if (content.includes("'standalone'") || content.includes('"standalone"')) {
|
|
168
|
+
hasStandalone = true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
startCommand = hasStandalone
|
|
173
|
+
? 'npx prisma migrate deploy && node server.js'
|
|
174
|
+
: 'npx prisma migrate deploy && pnpm start';
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Fallback to default
|
|
179
|
+
}
|
|
180
|
+
await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'railway.toml'), `[build]
|
|
181
|
+
builder = "nixpacks"
|
|
182
|
+
buildCommand = "pnpm install && npx prisma generate && pnpm build"
|
|
183
|
+
|
|
184
|
+
[deploy]
|
|
185
|
+
startCommand = "${startCommand}"
|
|
186
|
+
healthcheckPath = "/api/health"
|
|
187
|
+
restartPolicyType = "on_failure"
|
|
188
|
+
restartPolicyMaxRetries = 3
|
|
189
|
+
|
|
190
|
+
[service]
|
|
191
|
+
internalPort = 3000
|
|
192
|
+
`);
|
|
193
|
+
spinner.succeed('Created railway.toml');
|
|
194
|
+
console.log(chalk_1.default.gray(` Start command: ${startCommand}`));
|
|
195
|
+
const healthPath = path_1.default.join(targetDir, 'app/api/health/route.ts');
|
|
196
|
+
if (!fs_extra_1.default.existsSync(healthPath)) {
|
|
197
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(healthPath));
|
|
198
|
+
await fs_extra_1.default.writeFile(healthPath, `import { NextResponse } from 'next/server';
|
|
199
|
+
|
|
200
|
+
export async function GET() {
|
|
201
|
+
return NextResponse.json({
|
|
202
|
+
status: 'ok',
|
|
203
|
+
timestamp: new Date().toISOString(),
|
|
204
|
+
uptime: process.uptime(),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
`);
|
|
208
|
+
spinner.succeed('Created health check endpoint');
|
|
209
|
+
}
|
|
210
|
+
console.log(chalk_1.default.bold.green('\n✅ Railway config generated!\n'));
|
|
211
|
+
console.log(chalk_1.default.cyan('Next steps:'));
|
|
212
|
+
console.log(chalk_1.default.white(' 1. Install: npm i -g @railway/cli'));
|
|
213
|
+
console.log(chalk_1.default.white(' 2. Deploy: railway up\n'));
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
spinner.fail('Failed to generate Railway config');
|
|
217
|
+
console.error(chalk_1.default.red(error.message));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev Command - PHASE 3.3
|
|
3
|
+
* Orchestrates complete development environment setup
|
|
4
|
+
* Starts Docker, installs dependencies, runs migrations, then starts dev server
|
|
5
|
+
*/
|
|
6
|
+
export interface DevOptions {
|
|
7
|
+
dir: string;
|
|
8
|
+
skipDocker: boolean;
|
|
9
|
+
skipMigrate: boolean;
|
|
10
|
+
port: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Start dev environment: Docker → Dependencies → Migrations → Dev Server
|
|
14
|
+
* @param options - DevOptions with directory and flags
|
|
15
|
+
*/
|
|
16
|
+
export declare function devCommand(options: DevOptions): Promise<void>;
|
|
17
|
+
//# sourceMappingURL=dev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAWD;;;GAGG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BnE"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Dev Command - PHASE 3.3
|
|
4
|
+
* Orchestrates complete development environment setup
|
|
5
|
+
* Starts Docker, installs dependencies, runs migrations, then starts dev server
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.devCommand = devCommand;
|
|
12
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
13
|
+
const ora_1 = __importDefault(require("ora"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
16
|
+
const execa_1 = require("execa");
|
|
17
|
+
const scanner_js_1 = require("../scanner.js");
|
|
18
|
+
const utils_js_1 = require("../utils.js");
|
|
19
|
+
/**
|
|
20
|
+
* Validate project structure
|
|
21
|
+
* @param targetDir - Project root directory
|
|
22
|
+
* @returns true if valid ChimerAI project
|
|
23
|
+
*/
|
|
24
|
+
function validateWorkspace(targetDir) {
|
|
25
|
+
return fs_extra_1.default.existsSync(path_1.default.join(targetDir, 'package.json'));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Start dev environment: Docker → Dependencies → Migrations → Dev Server
|
|
29
|
+
* @param options - DevOptions with directory and flags
|
|
30
|
+
*/
|
|
31
|
+
async function devCommand(options) {
|
|
32
|
+
const targetDir = (0, utils_js_1.resolveTargetDir)(options.dir || '.');
|
|
33
|
+
// Validate project
|
|
34
|
+
if (!validateWorkspace(targetDir)) {
|
|
35
|
+
(0, utils_js_1.handleCliError)('Not a valid ChimerAI project. Run: chimerai create <name>');
|
|
36
|
+
}
|
|
37
|
+
// Scan project
|
|
38
|
+
const project = await (0, scanner_js_1.scanProject)(targetDir);
|
|
39
|
+
console.log(chalk_1.default.blue.bold('\n 🚀 ChimerAI Dev Environment\n'));
|
|
40
|
+
console.log(chalk_1.default.cyan(` Project: ${chalk_1.default.white(path_1.default.basename(targetDir))}`));
|
|
41
|
+
console.log(chalk_1.default.cyan(` Next.js: ${chalk_1.default.white(project.nextVersion || 'not found')}`));
|
|
42
|
+
console.log(chalk_1.default.cyan(` Router: ${chalk_1.default.white(project.hasAppRouter ? 'app' : 'pages')}\n`));
|
|
43
|
+
// 1. Docker Compose
|
|
44
|
+
if (!options.skipDocker) {
|
|
45
|
+
await startDockerServices(targetDir);
|
|
46
|
+
}
|
|
47
|
+
// 2. Dependencies
|
|
48
|
+
await ensureDependencies(targetDir, project);
|
|
49
|
+
// 3. Prisma Migrations
|
|
50
|
+
if (!options.skipMigrate) {
|
|
51
|
+
await applyMigrations(targetDir, project);
|
|
52
|
+
}
|
|
53
|
+
// 4. Start dev server
|
|
54
|
+
await startDevServer(targetDir, project, options.port);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Start Docker Compose services (PostgreSQL, Redis, etc.)
|
|
58
|
+
*/
|
|
59
|
+
async function startDockerServices(targetDir) {
|
|
60
|
+
const composePath = path_1.default.join(targetDir, 'docker-compose.yml');
|
|
61
|
+
if (!(await fs_extra_1.default.pathExists(composePath))) {
|
|
62
|
+
return; // No docker-compose.yml, skip
|
|
63
|
+
}
|
|
64
|
+
const spinner = (0, ora_1.default)('Starting Docker services...').start();
|
|
65
|
+
try {
|
|
66
|
+
await (0, execa_1.execa)('docker', ['compose', 'up', '-d'], { cwd: targetDir });
|
|
67
|
+
spinner.succeed(chalk_1.default.green('Docker services running (PostgreSQL + Redis)'));
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
spinner.warn(chalk_1.default.yellow('Docker not available - skipping'));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Install dependencies if node_modules doesn't exist
|
|
75
|
+
*/
|
|
76
|
+
async function ensureDependencies(targetDir, project) {
|
|
77
|
+
const nmPath = path_1.default.join(targetDir, 'node_modules');
|
|
78
|
+
if (await fs_extra_1.default.pathExists(nmPath)) {
|
|
79
|
+
return; // Dependencies already installed
|
|
80
|
+
}
|
|
81
|
+
const pm = project.packageManager === 'unknown' ? 'npm' : project.packageManager;
|
|
82
|
+
const spinner = (0, ora_1.default)(`Installing dependencies with ${pm}...`).start();
|
|
83
|
+
try {
|
|
84
|
+
await (0, execa_1.execa)(pm, ['install'], { cwd: targetDir });
|
|
85
|
+
spinner.succeed(chalk_1.default.green('Dependencies installed'));
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
spinner.fail(chalk_1.default.red('Failed to install dependencies'));
|
|
89
|
+
(0, utils_js_1.handleCliError)(error.message);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Apply Prisma database migrations
|
|
94
|
+
*/
|
|
95
|
+
async function applyMigrations(targetDir, project) {
|
|
96
|
+
if (!project.hasPrisma) {
|
|
97
|
+
return; // No Prisma, skip
|
|
98
|
+
}
|
|
99
|
+
const spinner = (0, ora_1.default)('Applying database migrations...').start();
|
|
100
|
+
try {
|
|
101
|
+
await (0, execa_1.execa)('npx', ['prisma', 'db', 'push', '--skip-generate'], { cwd: targetDir });
|
|
102
|
+
spinner.succeed(chalk_1.default.green('Database schema applied'));
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
spinner.warn(chalk_1.default.yellow('Database migration failed - check DATABASE_URL in .env'));
|
|
106
|
+
console.log(chalk_1.default.gray(` Error: ${error.message}`));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Start Next.js dev server with graceful shutdown handling
|
|
111
|
+
* Also starts AI service in parallel if services/ai/ exists
|
|
112
|
+
*/
|
|
113
|
+
async function startDevServer(targetDir, project, port) {
|
|
114
|
+
const pm = project.packageManager === 'unknown' ? 'npm' : project.packageManager;
|
|
115
|
+
// Check if AI service exists
|
|
116
|
+
const aiServiceDir = path_1.default.join(targetDir, 'services', 'ai');
|
|
117
|
+
const hasAiService = await fs_extra_1.default.pathExists(path_1.default.join(aiServiceDir, 'main.py'));
|
|
118
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
119
|
+
let aiProcess = null;
|
|
120
|
+
// Start AI service if present
|
|
121
|
+
if (hasAiService) {
|
|
122
|
+
const spinner = (0, ora_1.default)('Preparing AI service...').start();
|
|
123
|
+
try {
|
|
124
|
+
const isWindows = process.platform === 'win32';
|
|
125
|
+
const venvDir = path_1.default.join(aiServiceDir, '.venv');
|
|
126
|
+
// Create venv if not exists
|
|
127
|
+
if (!(await fs_extra_1.default.pathExists(venvDir))) {
|
|
128
|
+
spinner.text = 'Creating Python virtual environment...';
|
|
129
|
+
await (0, execa_1.execa)('python', ['-m', 'venv', venvDir], { cwd: aiServiceDir });
|
|
130
|
+
// Install requirements
|
|
131
|
+
spinner.text = 'Installing Python dependencies...';
|
|
132
|
+
const pipPath = isWindows
|
|
133
|
+
? path_1.default.join(venvDir, 'Scripts', 'pip')
|
|
134
|
+
: path_1.default.join(venvDir, 'bin', 'pip');
|
|
135
|
+
await (0, execa_1.execa)(pipPath, ['install', '-r', 'requirements.txt'], { cwd: aiServiceDir });
|
|
136
|
+
}
|
|
137
|
+
// Start uvicorn
|
|
138
|
+
const uvicornPath = isWindows
|
|
139
|
+
? path_1.default.join(venvDir, 'Scripts', 'uvicorn')
|
|
140
|
+
: path_1.default.join(venvDir, 'bin', 'uvicorn');
|
|
141
|
+
const aiChild = (0, execa_1.execa)(uvicornPath, ['main:app', '--reload', '--port', '8002'], {
|
|
142
|
+
cwd: aiServiceDir,
|
|
143
|
+
stdio: 'pipe',
|
|
144
|
+
});
|
|
145
|
+
aiProcess = aiChild;
|
|
146
|
+
// Prefix AI service output
|
|
147
|
+
aiChild.stdout?.on('data', (data) => {
|
|
148
|
+
const lines = data.toString().trim().split('\n');
|
|
149
|
+
lines.forEach((line) => {
|
|
150
|
+
console.log(chalk_1.default.magenta('[AI] ') + line);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
aiChild.stderr?.on('data', (data) => {
|
|
154
|
+
const lines = data.toString().trim().split('\n');
|
|
155
|
+
lines.forEach((line) => {
|
|
156
|
+
console.log(chalk_1.default.magenta('[AI] ') + chalk_1.default.yellow(line));
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
spinner.succeed(chalk_1.default.green('AI service starting on http://localhost:8002'));
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
spinner.warn(chalk_1.default.yellow(`AI service failed to start: ${error.message}`));
|
|
163
|
+
aiProcess = null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
console.log(chalk_1.default.green(`\n ▶️ Starting dev server on ${chalk_1.default.white(`http://localhost:${port}`)}\n`));
|
|
167
|
+
if (hasAiService) {
|
|
168
|
+
console.log(chalk_1.default.green(` ▶️ AI service on ${chalk_1.default.white('http://localhost:8002')}\n`));
|
|
169
|
+
}
|
|
170
|
+
console.log(chalk_1.default.gray(' Press Ctrl+C to stop\n'));
|
|
171
|
+
// Spawn child process with stdio inheritance (shows all output)
|
|
172
|
+
const child = (0, execa_1.execa)(pm, ['run', 'dev'], {
|
|
173
|
+
cwd: targetDir,
|
|
174
|
+
stdio: 'inherit',
|
|
175
|
+
env: { ...process.env, PORT: String(port) },
|
|
176
|
+
});
|
|
177
|
+
// Handle graceful shutdown on Ctrl+C
|
|
178
|
+
const shutdownHandler = async () => {
|
|
179
|
+
console.log(chalk_1.default.yellow('\n\n ⏹️ Shutting down...\n'));
|
|
180
|
+
// Kill dev server
|
|
181
|
+
child.kill();
|
|
182
|
+
// Kill AI service if running
|
|
183
|
+
if (aiProcess) {
|
|
184
|
+
aiProcess.kill();
|
|
185
|
+
console.log(chalk_1.default.magenta(' [AI] Service stopped'));
|
|
186
|
+
}
|
|
187
|
+
// Stop Docker services if running
|
|
188
|
+
const composePath = path_1.default.join(targetDir, 'docker-compose.yml');
|
|
189
|
+
if (!(await fs_extra_1.default.pathExists(composePath))) {
|
|
190
|
+
process.exit(0);
|
|
191
|
+
}
|
|
192
|
+
const spinner = (0, ora_1.default)('Stopping Docker services...').start();
|
|
193
|
+
try {
|
|
194
|
+
await (0, execa_1.execa)('docker', ['compose', 'stop'], { cwd: targetDir });
|
|
195
|
+
spinner.succeed(chalk_1.default.green('Docker services stopped'));
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
spinner.warn(chalk_1.default.yellow('Could not stop Docker services'));
|
|
199
|
+
}
|
|
200
|
+
process.exit(0);
|
|
201
|
+
};
|
|
202
|
+
process.on('SIGINT', shutdownHandler);
|
|
203
|
+
process.on('SIGTERM', shutdownHandler);
|
|
204
|
+
// Wait for dev server to finish
|
|
205
|
+
await child;
|
|
206
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor Command - Health check for ChimerAI projects
|
|
3
|
+
* Validates configuration, dependencies, and environment setup
|
|
4
|
+
*/
|
|
5
|
+
interface DoctorOptions {
|
|
6
|
+
dir: string;
|
|
7
|
+
fix: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function doctorCommand(options: DoctorOptions): Promise<void>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkBH,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,OAAO,CAAC;CACd;AASD,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,iBAoFzD"}
|