@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.
Files changed (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +293 -0
  3. package/dist/cli.d.ts +7 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +317 -0
  6. package/dist/commands/add.d.ts +11 -0
  7. package/dist/commands/add.d.ts.map +1 -0
  8. package/dist/commands/add.js +2126 -0
  9. package/dist/commands/create.d.ts +12 -0
  10. package/dist/commands/create.d.ts.map +1 -0
  11. package/dist/commands/create.js +1703 -0
  12. package/dist/commands/deploy.d.ts +11 -0
  13. package/dist/commands/deploy.d.ts.map +1 -0
  14. package/dist/commands/deploy.js +219 -0
  15. package/dist/commands/dev.d.ts +17 -0
  16. package/dist/commands/dev.d.ts.map +1 -0
  17. package/dist/commands/dev.js +206 -0
  18. package/dist/commands/doctor.d.ts +11 -0
  19. package/dist/commands/doctor.d.ts.map +1 -0
  20. package/dist/commands/doctor.js +728 -0
  21. package/dist/commands/generate.d.ts +19 -0
  22. package/dist/commands/generate.d.ts.map +1 -0
  23. package/dist/commands/generate.js +429 -0
  24. package/dist/commands/init.d.ts +11 -0
  25. package/dist/commands/init.d.ts.map +1 -0
  26. package/dist/commands/init.js +269 -0
  27. package/dist/commands/list.d.ts +12 -0
  28. package/dist/commands/list.d.ts.map +1 -0
  29. package/dist/commands/list.js +328 -0
  30. package/dist/commands/migrate.d.ts +14 -0
  31. package/dist/commands/migrate.d.ts.map +1 -0
  32. package/dist/commands/migrate.js +197 -0
  33. package/dist/commands/plugin.d.ts +10 -0
  34. package/dist/commands/plugin.d.ts.map +1 -0
  35. package/dist/commands/plugin.js +239 -0
  36. package/dist/commands/remove.d.ts +11 -0
  37. package/dist/commands/remove.d.ts.map +1 -0
  38. package/dist/commands/remove.js +472 -0
  39. package/dist/commands/secret.d.ts +12 -0
  40. package/dist/commands/secret.d.ts.map +1 -0
  41. package/dist/commands/secret.js +102 -0
  42. package/dist/commands/setup.d.ts +9 -0
  43. package/dist/commands/setup.d.ts.map +1 -0
  44. package/dist/commands/setup.js +788 -0
  45. package/dist/commands/update.d.ts +14 -0
  46. package/dist/commands/update.d.ts.map +1 -0
  47. package/dist/commands/update.js +211 -0
  48. package/dist/commands/use.d.ts +9 -0
  49. package/dist/commands/use.d.ts.map +1 -0
  50. package/dist/commands/use.js +51 -0
  51. package/dist/index.d.ts +22 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +45 -0
  54. package/dist/license.d.ts +55 -0
  55. package/dist/license.d.ts.map +1 -0
  56. package/dist/license.js +258 -0
  57. package/dist/scanner.d.ts +31 -0
  58. package/dist/scanner.d.ts.map +1 -0
  59. package/dist/scanner.js +113 -0
  60. package/dist/schema-manager.d.ts +26 -0
  61. package/dist/schema-manager.d.ts.map +1 -0
  62. package/dist/schema-manager.js +132 -0
  63. package/dist/templates/admin.d.ts +49 -0
  64. package/dist/templates/admin.d.ts.map +1 -0
  65. package/dist/templates/admin.js +1358 -0
  66. package/dist/templates/ai-routes.d.ts +17 -0
  67. package/dist/templates/ai-routes.d.ts.map +1 -0
  68. package/dist/templates/ai-routes.js +1130 -0
  69. package/dist/templates/ai-service-tools.d.ts +22 -0
  70. package/dist/templates/ai-service-tools.d.ts.map +1 -0
  71. package/dist/templates/ai-service-tools.js +1424 -0
  72. package/dist/templates/ai-service.d.ts +66 -0
  73. package/dist/templates/ai-service.d.ts.map +1 -0
  74. package/dist/templates/ai-service.js +2202 -0
  75. package/dist/templates/api-routes.d.ts +108 -0
  76. package/dist/templates/api-routes.d.ts.map +1 -0
  77. package/dist/templates/api-routes.js +1219 -0
  78. package/dist/templates/auth.d.ts +48 -0
  79. package/dist/templates/auth.d.ts.map +1 -0
  80. package/dist/templates/auth.js +381 -0
  81. package/dist/templates/billing.d.ts +44 -0
  82. package/dist/templates/billing.d.ts.map +1 -0
  83. package/dist/templates/billing.js +551 -0
  84. package/dist/templates/chat.d.ts +63 -0
  85. package/dist/templates/chat.d.ts.map +1 -0
  86. package/dist/templates/chat.js +1979 -0
  87. package/dist/templates/components.d.ts +22 -0
  88. package/dist/templates/components.d.ts.map +1 -0
  89. package/dist/templates/components.js +672 -0
  90. package/dist/templates/config.d.ts +6 -0
  91. package/dist/templates/config.d.ts.map +1 -0
  92. package/dist/templates/config.js +86 -0
  93. package/dist/templates/docker.d.ts +25 -0
  94. package/dist/templates/docker.d.ts.map +1 -0
  95. package/dist/templates/docker.js +165 -0
  96. package/dist/templates/gdpr.d.ts +16 -0
  97. package/dist/templates/gdpr.d.ts.map +1 -0
  98. package/dist/templates/gdpr.js +259 -0
  99. package/dist/templates/index.d.ts +77 -0
  100. package/dist/templates/index.d.ts.map +1 -0
  101. package/dist/templates/index.js +339 -0
  102. package/dist/templates/layout.d.ts +67 -0
  103. package/dist/templates/layout.d.ts.map +1 -0
  104. package/dist/templates/layout.js +670 -0
  105. package/dist/templates/mfa.d.ts +23 -0
  106. package/dist/templates/mfa.d.ts.map +1 -0
  107. package/dist/templates/mfa.js +353 -0
  108. package/dist/templates/middleware.d.ts +12 -0
  109. package/dist/templates/middleware.d.ts.map +1 -0
  110. package/dist/templates/middleware.js +116 -0
  111. package/dist/templates/prisma.d.ts +35 -0
  112. package/dist/templates/prisma.d.ts.map +1 -0
  113. package/dist/templates/prisma.js +724 -0
  114. package/dist/templates/provider-routes.d.ts +21 -0
  115. package/dist/templates/provider-routes.d.ts.map +1 -0
  116. package/dist/templates/provider-routes.js +1203 -0
  117. package/dist/templates/rag.d.ts +48 -0
  118. package/dist/templates/rag.d.ts.map +1 -0
  119. package/dist/templates/rag.js +532 -0
  120. package/dist/templates/widget.d.ts +64 -0
  121. package/dist/templates/widget.d.ts.map +1 -0
  122. package/dist/templates/widget.js +1360 -0
  123. package/dist/utils/provider-db.d.ts +63 -0
  124. package/dist/utils/provider-db.d.ts.map +1 -0
  125. package/dist/utils/provider-db.js +300 -0
  126. package/dist/utils.d.ts +78 -0
  127. package/dist/utils.d.ts.map +1 -0
  128. package/dist/utils.js +330 -0
  129. package/package.json +60 -0
@@ -0,0 +1,788 @@
1
+ "use strict";
2
+ /**
3
+ * Setup Command - Configure integrations
4
+ * REFACTORED: Provider setup creates DB entries (Phase 2), falls back to .env
5
+ * REFACTORED PHASE 4: Uses shared updateEnvFile() from utils
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ var __importDefault = (this && this.__importDefault) || function (mod) {
41
+ return (mod && mod.__esModule) ? mod : { "default": mod };
42
+ };
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.setupCommand = setupCommand;
45
+ const inquirer_1 = __importDefault(require("inquirer"));
46
+ const chalk_1 = __importDefault(require("chalk"));
47
+ const ora_1 = __importDefault(require("ora"));
48
+ const fs_extra_1 = __importDefault(require("fs-extra"));
49
+ const path_1 = __importDefault(require("path"));
50
+ const provider_db_js_1 = require("../utils/provider-db.js");
51
+ const utils_js_1 = require("../utils.js");
52
+ const templates = __importStar(require("../templates/index.js"));
53
+ async function setupCommand(service, options = { dir: '.' }) {
54
+ console.log(chalk_1.default.bold.cyan(`\nāš™ļø Setting up ${service} integration\n`));
55
+ const targetDir = (0, utils_js_1.resolveTargetDir)(options.dir);
56
+ switch (service) {
57
+ case 'database':
58
+ await setupDatabase(targetDir);
59
+ break;
60
+ case 'auth':
61
+ await setupAuth(targetDir);
62
+ break;
63
+ case 'openai':
64
+ await setupOpenAI(targetDir);
65
+ break;
66
+ case 'anthropic':
67
+ await setupAnthropic(targetDir);
68
+ break;
69
+ case 'ollama':
70
+ await setupOllama(targetDir);
71
+ break;
72
+ case 'providers':
73
+ await setupProviders(targetDir);
74
+ break;
75
+ case 'azure':
76
+ await setupAzure(targetDir);
77
+ break;
78
+ case 'stripe':
79
+ await setupStripe(targetDir);
80
+ break;
81
+ case 'permissions':
82
+ await setupPermissions(targetDir);
83
+ break;
84
+ default:
85
+ console.log(chalk_1.default.red(`āŒ Unknown service: ${service}`));
86
+ console.log(chalk_1.default.yellow('\nAvailable services:'));
87
+ console.log(chalk_1.default.white(' - database'));
88
+ console.log(chalk_1.default.white(' - auth'));
89
+ console.log(chalk_1.default.white(' - openai'));
90
+ console.log(chalk_1.default.white(' - anthropic'));
91
+ console.log(chalk_1.default.white(' - ollama'));
92
+ console.log(chalk_1.default.white(' - azure'));
93
+ console.log(chalk_1.default.white(' - providers (configure all AI providers)'));
94
+ console.log(chalk_1.default.white(' - stripe (billing)'));
95
+ console.log(chalk_1.default.white(' - permissions (RBAC system)'));
96
+ (0, utils_js_1.handleCliError)(`Unknown service: ${service}`);
97
+ }
98
+ console.log(chalk_1.default.bold.green(`\n✨ ${service} setup complete!\n`));
99
+ }
100
+ async function setupDatabase(targetDir) {
101
+ const answers = await inquirer_1.default.prompt([
102
+ {
103
+ type: 'list',
104
+ name: 'provider',
105
+ message: 'Which database provider?',
106
+ choices: ['postgresql', 'mysql', 'sqlite'],
107
+ default: 'postgresql',
108
+ },
109
+ {
110
+ type: 'input',
111
+ name: 'url',
112
+ message: 'Database connection URL:',
113
+ default: 'postgresql://user:password@localhost:5432/chimerai',
114
+ },
115
+ ]);
116
+ const spinner = (0, ora_1.default)('Configuring database...').start();
117
+ try {
118
+ await (0, utils_js_1.updateEnvFile)({ DATABASE_URL: answers.url }, path_1.default.join(targetDir, '.env.local'));
119
+ // === Scaffold Prisma schema if it doesn't exist ===
120
+ const schemaPath = path_1.default.join(targetDir, 'prisma', 'schema.prisma');
121
+ if (!(await fs_extra_1.default.pathExists(schemaPath))) {
122
+ await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'prisma'));
123
+ const baseSchema = `generator client {
124
+ provider = "prisma-client-js"
125
+ }
126
+
127
+ datasource db {
128
+ provider = "${answers.provider}"
129
+ url = env("DATABASE_URL")
130
+ }
131
+ `;
132
+ await fs_extra_1.default.writeFile(schemaPath, baseSchema);
133
+ console.log(chalk_1.default.cyan(`\n šŸ“„ Created prisma/schema.prisma (provider: ${answers.provider})`));
134
+ }
135
+ // === Create lib/prisma.ts (PrismaClient singleton) if not present ===
136
+ const prismaLibPath = path_1.default.join(targetDir, 'lib', 'prisma.ts');
137
+ if (!(await fs_extra_1.default.pathExists(prismaLibPath))) {
138
+ await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'lib'));
139
+ await fs_extra_1.default.writeFile(prismaLibPath, templates.generatePrismaLib());
140
+ console.log(chalk_1.default.cyan(` šŸ“„ Created lib/prisma.ts`));
141
+ }
142
+ // === Add Prisma dependencies + scripts to package.json ===
143
+ const packageJsonPath = path_1.default.join(targetDir, 'package.json');
144
+ if (await fs_extra_1.default.pathExists(packageJsonPath)) {
145
+ const packageJson = await fs_extra_1.default.readJson(packageJsonPath);
146
+ let depsChanged = false;
147
+ packageJson.dependencies = packageJson.dependencies || {};
148
+ packageJson.devDependencies = packageJson.devDependencies || {};
149
+ if (!packageJson.dependencies['@prisma/client']) {
150
+ packageJson.dependencies['@prisma/client'] = '^5.22.0';
151
+ depsChanged = true;
152
+ }
153
+ if (!packageJson.devDependencies['prisma']) {
154
+ packageJson.devDependencies['prisma'] = '^5.22.0';
155
+ depsChanged = true;
156
+ }
157
+ packageJson.scripts = packageJson.scripts || {};
158
+ if (!packageJson.scripts['db:push']) {
159
+ packageJson.scripts['db:push'] = 'prisma db push';
160
+ }
161
+ if (!packageJson.scripts['db:generate']) {
162
+ packageJson.scripts['db:generate'] = 'prisma generate';
163
+ }
164
+ if (!packageJson.scripts['db:studio']) {
165
+ packageJson.scripts['db:studio'] = 'prisma studio';
166
+ }
167
+ await fs_extra_1.default.writeJson(packageJsonPath, packageJson, { spaces: 2 });
168
+ if (depsChanged) {
169
+ console.log(chalk_1.default.cyan(` šŸ“¦ Added @prisma/client + prisma to package.json`));
170
+ console.log(chalk_1.default.yellow(` → Run: pnpm install`));
171
+ }
172
+ }
173
+ spinner.succeed(chalk_1.default.green('Database configured'));
174
+ console.log(chalk_1.default.cyan('\nNext steps:'));
175
+ console.log(chalk_1.default.white(' 1. Run: pnpm install'));
176
+ console.log(chalk_1.default.white(' 2. Run: pnpm db:push'));
177
+ console.log(chalk_1.default.white(' 3. Run: pnpm db:seed (optional)'));
178
+ }
179
+ catch (error) {
180
+ spinner.fail(chalk_1.default.red('Failed to configure database'));
181
+ console.error(error.message);
182
+ }
183
+ }
184
+ async function setupAuth(targetDir) {
185
+ const answers = await inquirer_1.default.prompt([
186
+ {
187
+ type: 'input',
188
+ name: 'secret',
189
+ message: 'NextAuth secret (leave empty to generate):',
190
+ default: '',
191
+ },
192
+ {
193
+ type: 'input',
194
+ name: 'url',
195
+ message: 'NextAuth URL:',
196
+ default: 'http://localhost:3000',
197
+ },
198
+ ]);
199
+ const spinner = (0, ora_1.default)('Configuring authentication...').start();
200
+ try {
201
+ const secret = answers.secret || generateRandomSecret();
202
+ await (0, utils_js_1.updateEnvFile)({
203
+ NEXTAUTH_SECRET: secret,
204
+ NEXTAUTH_URL: answers.url,
205
+ }, path_1.default.join(targetDir, '.env.local'));
206
+ spinner.succeed(chalk_1.default.green('Authentication configured in .env'));
207
+ }
208
+ catch (error) {
209
+ spinner.fail(chalk_1.default.red('Failed to configure authentication'));
210
+ console.error(error.message);
211
+ }
212
+ }
213
+ async function setupOpenAI(targetDir) {
214
+ const answers = await inquirer_1.default.prompt([
215
+ {
216
+ type: 'password',
217
+ name: 'apiKey',
218
+ message: 'OpenAI API Key:',
219
+ mask: '*',
220
+ },
221
+ ]);
222
+ // 1. Validate key
223
+ const validateSpinner = (0, ora_1.default)('Validating API Key...').start();
224
+ const validation = await (0, provider_db_js_1.validateOpenAIKey)(answers.apiKey);
225
+ if (validation.valid) {
226
+ validateSpinner.succeed(chalk_1.default.green(`API Key valid (${validation.modelCount} models available)`));
227
+ }
228
+ else {
229
+ validateSpinner.warn(chalk_1.default.yellow('Could not validate key (offline?) — saving anyway'));
230
+ }
231
+ // 2. Try to create provider in database
232
+ const dbSpinner = (0, ora_1.default)('Creating provider in database...').start();
233
+ const dbSuccess = await (0, provider_db_js_1.createProviderInDB)({
234
+ targetDir,
235
+ providerId: 'cli-openai',
236
+ name: 'OpenAI',
237
+ type: 'openai',
238
+ description: 'Configured via ChimerAI CLI',
239
+ apiKey: answers.apiKey,
240
+ baseUrl: 'https://api.openai.com/v1',
241
+ isDefault: true,
242
+ priority: 1,
243
+ models: provider_db_js_1.OPENAI_MODELS,
244
+ });
245
+ if (dbSuccess) {
246
+ dbSpinner.succeed(chalk_1.default.green('Provider "OpenAI" created in database'));
247
+ console.log(chalk_1.default.green(` āœ“ Models registered: ${provider_db_js_1.OPENAI_MODELS.map((m) => m.modelId).join(', ')}`));
248
+ console.log(chalk_1.default.green(' āœ“ Set as default provider'));
249
+ console.log(chalk_1.default.cyan(`\n Manage at: ${chalk_1.default.white('http://localhost:3000/providers')}`));
250
+ }
251
+ else {
252
+ dbSpinner.warn(chalk_1.default.yellow('Could not create provider in database'));
253
+ console.log(chalk_1.default.gray(' Database might not be running — falling back to .env'));
254
+ // 3. Fallback: Write to .env
255
+ await (0, utils_js_1.updateEnvFile)({ OPENAI_API_KEY: answers.apiKey }, path_1.default.join(targetDir, '.env.local'));
256
+ console.log(chalk_1.default.green(' āœ“ OpenAI API Key saved to .env.local'));
257
+ console.log(chalk_1.default.cyan(' šŸ’” Run ') +
258
+ chalk_1.default.white('chimerai doctor --fix') +
259
+ chalk_1.default.cyan(' to migrate to database later'));
260
+ }
261
+ }
262
+ async function setupProviders(targetDir) {
263
+ console.log(chalk_1.default.bold.cyan('\nšŸ”Œ Configure AI Model Providers\n'));
264
+ const answers = await inquirer_1.default.prompt([
265
+ {
266
+ type: 'checkbox',
267
+ name: 'providers',
268
+ message: 'Which providers do you want to configure?',
269
+ choices: [
270
+ { name: 'OpenAI', value: 'openai', checked: true },
271
+ { name: 'Anthropic', value: 'anthropic', checked: true },
272
+ { name: 'Azure OpenAI', value: 'azure', checked: false },
273
+ { name: 'Ollama (local)', value: 'ollama', checked: false },
274
+ ],
275
+ },
276
+ ]);
277
+ for (const provider of answers.providers) {
278
+ console.log(chalk_1.default.cyan(`\n→ Configuring ${provider}...\n`));
279
+ switch (provider) {
280
+ case 'openai':
281
+ await setupOpenAI(targetDir);
282
+ break;
283
+ case 'anthropic':
284
+ await setupAnthropic(targetDir);
285
+ break;
286
+ case 'azure':
287
+ await setupAzure(targetDir);
288
+ break;
289
+ case 'ollama':
290
+ await setupOllama(targetDir);
291
+ break;
292
+ }
293
+ }
294
+ console.log(chalk_1.default.bold.green('\n✨ All providers configured!'));
295
+ console.log(chalk_1.default.cyan('\nNext steps:'));
296
+ console.log(chalk_1.default.white(' 1. Add provider configurations to database'));
297
+ console.log(chalk_1.default.white(' 2. Test connections at /providers-demo'));
298
+ console.log(chalk_1.default.white(' 3. Discover available models'));
299
+ }
300
+ async function setupAzure(targetDir) {
301
+ const answers = await inquirer_1.default.prompt([
302
+ {
303
+ type: 'input',
304
+ name: 'apiKey',
305
+ message: 'Azure OpenAI API Key:',
306
+ },
307
+ {
308
+ type: 'input',
309
+ name: 'endpoint',
310
+ message: 'Azure OpenAI Endpoint:',
311
+ default: 'https://your-resource.openai.azure.com',
312
+ },
313
+ {
314
+ type: 'input',
315
+ name: 'deployment',
316
+ message: 'Deployment name:',
317
+ default: 'gpt-4',
318
+ },
319
+ ]);
320
+ const spinner = (0, ora_1.default)('Configuring Azure OpenAI...').start();
321
+ try {
322
+ await (0, utils_js_1.updateEnvFile)({
323
+ AZURE_OPENAI_API_KEY: answers.apiKey,
324
+ AZURE_OPENAI_ENDPOINT: answers.endpoint,
325
+ AZURE_OPENAI_DEPLOYMENT: answers.deployment,
326
+ }, path_1.default.join(targetDir, '.env.local'));
327
+ spinner.succeed(chalk_1.default.green('Azure OpenAI configured in .env.local'));
328
+ }
329
+ catch (error) {
330
+ spinner.fail(chalk_1.default.red('Failed to configure Azure OpenAI'));
331
+ console.error(error.message);
332
+ }
333
+ }
334
+ async function setupStripe(targetDir) {
335
+ const answers = await inquirer_1.default.prompt([
336
+ {
337
+ type: 'input',
338
+ name: 'publishableKey',
339
+ message: 'Stripe Publishable Key:',
340
+ },
341
+ {
342
+ type: 'input',
343
+ name: 'secretKey',
344
+ message: 'Stripe Secret Key:',
345
+ },
346
+ {
347
+ type: 'input',
348
+ name: 'webhookSecret',
349
+ message: 'Stripe Webhook Secret (optional):',
350
+ default: '',
351
+ },
352
+ ]);
353
+ const spinner = (0, ora_1.default)('Configuring Stripe...').start();
354
+ try {
355
+ const envVars = {
356
+ STRIPE_PUBLISHABLE_KEY: answers.publishableKey,
357
+ STRIPE_SECRET_KEY: answers.secretKey,
358
+ };
359
+ if (answers.webhookSecret) {
360
+ envVars.STRIPE_WEBHOOK_SECRET = answers.webhookSecret;
361
+ }
362
+ await (0, utils_js_1.updateEnvFile)(envVars, path_1.default.join(targetDir, '.env.local'));
363
+ spinner.succeed(chalk_1.default.green('Stripe configured in .env.local'));
364
+ console.log(chalk_1.default.cyan('\nNext steps:'));
365
+ console.log(chalk_1.default.white(' 1. Test webhook: stripe listen --forward-to localhost:3000/api/webhooks/stripe'));
366
+ console.log(chalk_1.default.white(' 2. Create products in Stripe Dashboard'));
367
+ console.log(chalk_1.default.white(' 3. Configure pricing tiers in your app'));
368
+ }
369
+ catch (error) {
370
+ spinner.fail(chalk_1.default.red('Failed to configure Stripe'));
371
+ console.error(error.message);
372
+ }
373
+ }
374
+ async function setupAnthropic(targetDir) {
375
+ const answers = await inquirer_1.default.prompt([
376
+ {
377
+ type: 'password',
378
+ name: 'apiKey',
379
+ message: 'Anthropic API Key:',
380
+ mask: '*',
381
+ },
382
+ ]);
383
+ // 1. Validate key
384
+ const validateSpinner = (0, ora_1.default)('Validating API Key...').start();
385
+ const validation = await (0, provider_db_js_1.validateAnthropicKey)(answers.apiKey);
386
+ if (validation.valid) {
387
+ validateSpinner.succeed(chalk_1.default.green('API Key valid'));
388
+ }
389
+ else {
390
+ validateSpinner.warn(chalk_1.default.yellow('Could not validate key (offline?) — saving anyway'));
391
+ }
392
+ // 2. Try to create provider in database
393
+ const dbSpinner = (0, ora_1.default)('Creating provider in database...').start();
394
+ const dbSuccess = await (0, provider_db_js_1.createProviderInDB)({
395
+ targetDir,
396
+ providerId: 'cli-anthropic',
397
+ name: 'Anthropic',
398
+ type: 'anthropic',
399
+ description: 'Configured via ChimerAI CLI',
400
+ apiKey: answers.apiKey,
401
+ baseUrl: 'https://api.anthropic.com/v1',
402
+ isDefault: false,
403
+ priority: 2,
404
+ models: provider_db_js_1.ANTHROPIC_MODELS,
405
+ });
406
+ if (dbSuccess) {
407
+ dbSpinner.succeed(chalk_1.default.green('Provider "Anthropic" created in database'));
408
+ console.log(chalk_1.default.green(` āœ“ Models registered: ${provider_db_js_1.ANTHROPIC_MODELS.map((m) => m.modelId).join(', ')}`));
409
+ console.log(chalk_1.default.cyan(`\n Manage at: ${chalk_1.default.white('http://localhost:3000/providers')}`));
410
+ }
411
+ else {
412
+ dbSpinner.warn(chalk_1.default.yellow('Could not create provider in database'));
413
+ console.log(chalk_1.default.gray(' Database might not be running — falling back to .env'));
414
+ await (0, utils_js_1.updateEnvFile)({ ANTHROPIC_API_KEY: answers.apiKey }, path_1.default.join(targetDir, '.env.local'));
415
+ console.log(chalk_1.default.green(' āœ“ Anthropic API Key saved to .env.local'));
416
+ console.log(chalk_1.default.cyan(' šŸ’” Run ') +
417
+ chalk_1.default.white('chimerai doctor --fix') +
418
+ chalk_1.default.cyan(' to migrate to database later'));
419
+ }
420
+ }
421
+ async function setupOllama(targetDir) {
422
+ const answers = await inquirer_1.default.prompt([
423
+ {
424
+ type: 'input',
425
+ name: 'url',
426
+ message: 'Ollama API URL:',
427
+ default: 'http://localhost:11434',
428
+ },
429
+ ]);
430
+ // 1. Validate connection + discover models
431
+ const validateSpinner = (0, ora_1.default)('Connecting to Ollama...').start();
432
+ const validation = await (0, provider_db_js_1.validateOllamaConnection)(answers.url);
433
+ if (validation.valid) {
434
+ validateSpinner.succeed(chalk_1.default.green(`Connected! ${validation.models.length} models available`));
435
+ if (validation.models.length > 0) {
436
+ console.log(chalk_1.default.gray(` Models: ${validation.models.slice(0, 5).join(', ')}${validation.models.length > 5 ? '...' : ''}`));
437
+ }
438
+ }
439
+ else {
440
+ validateSpinner.warn(chalk_1.default.yellow(`Could not connect to Ollama at ${answers.url}`));
441
+ console.log(chalk_1.default.gray(' Make sure Ollama is running: ollama serve'));
442
+ }
443
+ // 2. Build model definitions from discovered models (or empty)
444
+ const ollamaModels = validation.models.map((name) => ({
445
+ modelId: `ollama/${name}`,
446
+ name: name,
447
+ capabilities: ['chat'],
448
+ contextWindow: 4096,
449
+ inputCost: 0,
450
+ outputCost: 0,
451
+ }));
452
+ // 3. Try to create provider in database
453
+ const dbSpinner = (0, ora_1.default)('Creating provider in database...').start();
454
+ const dbSuccess = await (0, provider_db_js_1.createProviderInDB)({
455
+ targetDir,
456
+ providerId: 'cli-ollama',
457
+ name: 'Ollama (Local)',
458
+ type: 'ollama',
459
+ description: 'Local Ollama instance',
460
+ baseUrl: answers.url,
461
+ isDefault: false,
462
+ priority: 10,
463
+ models: ollamaModels,
464
+ });
465
+ if (dbSuccess) {
466
+ dbSpinner.succeed(chalk_1.default.green('Provider "Ollama" created in database'));
467
+ if (ollamaModels.length > 0) {
468
+ console.log(chalk_1.default.green(` āœ“ ${ollamaModels.length} models registered`));
469
+ }
470
+ console.log(chalk_1.default.cyan(`\n Manage at: ${chalk_1.default.white('http://localhost:3000/providers')}`));
471
+ }
472
+ else {
473
+ dbSpinner.warn(chalk_1.default.yellow('Could not create provider in database'));
474
+ console.log(chalk_1.default.gray(' Database might not be running — falling back to .env'));
475
+ await (0, utils_js_1.updateEnvFile)({ OLLAMA_API_URL: answers.url }, path_1.default.join(targetDir, '.env.local'));
476
+ console.log(chalk_1.default.green(' āœ“ Ollama URL saved to .env.local'));
477
+ console.log(chalk_1.default.cyan(' šŸ’” Run ') +
478
+ chalk_1.default.white('chimerai doctor --fix') +
479
+ chalk_1.default.cyan(' to migrate to database later'));
480
+ }
481
+ }
482
+ function generateRandomSecret() {
483
+ return Array.from({ length: 32 }, () => Math.floor(Math.random() * 16).toString(16)).join('');
484
+ }
485
+ async function setupPermissions(targetDir) {
486
+ const spinner = (0, ora_1.default)('Setting up RBAC permissions system...').start();
487
+ try {
488
+ // 1. Create lib/permissions.ts - Core permission utilities
489
+ const permissionsUtilContent = `/**
490
+ * Permission utility functions
491
+ * Handles permission checks and role-based access control
492
+ */
493
+
494
+ export const AVAILABLE_PERMISSIONS = [
495
+ 'users:read',
496
+ 'users:write',
497
+ 'users:delete',
498
+ 'roles:read',
499
+ 'roles:write',
500
+ 'roles:delete',
501
+ 'settings:read',
502
+ 'settings:write',
503
+ 'admin:*',
504
+ ] as const;
505
+
506
+ export type Permission = typeof AVAILABLE_PERMISSIONS[number];
507
+
508
+ interface User {
509
+ id: string;
510
+ email: string;
511
+ roles?: Array<{ permissions: string[] }>;
512
+ }
513
+
514
+ /**
515
+ * Check if user has a specific permission
516
+ */
517
+ export function hasPermission(user: User | null, permission: string): boolean {
518
+ if (!user || !user.roles) return false;
519
+
520
+ // Collect all permissions from all roles
521
+ const allPermissions = user.roles.flatMap(role => role.permissions || []);
522
+
523
+ // Check for admin wildcard
524
+ if (allPermissions.includes('admin:*')) return true;
525
+
526
+ // Check for specific permission
527
+ return allPermissions.includes(permission);
528
+ }
529
+
530
+ /**
531
+ * Check if user has ANY of the specified permissions
532
+ */
533
+ export function hasAnyPermission(user: User | null, permissions: string[]): boolean {
534
+ if (!user) return false;
535
+ return permissions.some(permission => hasPermission(user, permission));
536
+ }
537
+
538
+ /**
539
+ * Check if user has ALL of the specified permissions
540
+ */
541
+ export function hasAllPermissions(user: User | null, permissions: string[]): boolean {
542
+ if (!user) return false;
543
+ return permissions.every(permission => hasPermission(user, permission));
544
+ }
545
+
546
+ /**
547
+ * Get all permissions for a user (from all their roles)
548
+ */
549
+ export function getUserPermissions(user: User | null): string[] {
550
+ if (!user || !user.roles) return [];
551
+ return [...new Set(user.roles.flatMap(role => role.permissions || []))];
552
+ }
553
+
554
+ /**
555
+ * Check if a permission string is valid
556
+ */
557
+ export function isValidPermission(permission: string): boolean {
558
+ return AVAILABLE_PERMISSIONS.includes(permission as Permission);
559
+ }
560
+ `;
561
+ await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'lib'));
562
+ await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'lib/permissions.ts'), permissionsUtilContent);
563
+ // 2. Create lib/auth/require-permission.ts - Server-side helper
564
+ const requirePermissionContent = `import { getServerSession } from 'next-auth';
565
+ import { NextResponse } from 'next/server';
566
+ import { hasPermission } from '@/lib/permissions';
567
+
568
+ /**
569
+ * Server-side permission check for API routes
570
+ * Returns NextResponse with 401/403 if check fails
571
+ */
572
+ export async function requirePermission(permission: string) {
573
+ const session = await getServerSession();
574
+
575
+ if (!session || !session.user) {
576
+ return NextResponse.json(
577
+ { error: 'Unauthorized - Please sign in' },
578
+ { status: 401 }
579
+ );
580
+ }
581
+
582
+ // Get user with roles from database
583
+ const user = await getServerSessionWithPermissions();
584
+
585
+ if (!user || !hasPermission(user as any, permission)) {
586
+ return NextResponse.json(
587
+ { error: \`Forbidden - Required permission: \${permission}\` },
588
+ { status: 403 }
589
+ );
590
+ }
591
+
592
+ return null; // No error, permission granted
593
+ }
594
+
595
+ /**
596
+ * Get server session with full user data including roles
597
+ */
598
+ export async function getServerSessionWithPermissions() {
599
+ const session = await getServerSession();
600
+ if (!session?.user?.email) return null;
601
+
602
+ // Fetch user with roles from database (through UserRole join table)
603
+ const { prisma } = await import('@/lib/prisma');
604
+ const user = await prisma.user.findUnique({
605
+ where: { email: session.user.email },
606
+ include: {
607
+ roles: {
608
+ select: {
609
+ role: {
610
+ select: {
611
+ id: true,
612
+ name: true,
613
+ permissions: true
614
+ }
615
+ }
616
+ }
617
+ }
618
+ }
619
+ });
620
+
621
+ if (!user) return null;
622
+
623
+ // Flatten UserRole[] → { permissions: string[] }[]
624
+ return {
625
+ ...user,
626
+ roles: (user.roles as any[]).map((ur: any) => ur.role),
627
+ };
628
+ }
629
+ `;
630
+ await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'lib/auth'));
631
+ await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'lib/auth/require-permission.ts'), requirePermissionContent);
632
+ // 3. Create hooks/usePermissions.ts - Client-side hook
633
+ const usePermissionsContent = `'use client';
634
+
635
+ import { useSession } from 'next-auth/react';
636
+ import { useMemo } from 'react';
637
+ import { hasPermission, hasAnyPermission, hasAllPermissions, getUserPermissions } from '@/lib/permissions';
638
+
639
+ /**
640
+ * Client-side hook for permission checks
641
+ * Usage:
642
+ * const { hasPermission, permissions } = usePermissions();
643
+ * if (hasPermission('users:write')) { ... }
644
+ */
645
+ export function usePermissions() {
646
+ const { data: session } = useSession();
647
+ const user = session?.user as any;
648
+
649
+ const permissions = useMemo(() => getUserPermissions(user), [user]);
650
+
651
+ return {
652
+ /**
653
+ * Check if current user has a specific permission
654
+ */
655
+ hasPermission: (permission: string) => hasPermission(user, permission),
656
+
657
+ /**
658
+ * Check if current user has ANY of the permissions
659
+ */
660
+ hasAnyPermission: (perms: string[]) => hasAnyPermission(user, perms),
661
+
662
+ /**
663
+ * Check if current user has ALL of the permissions
664
+ */
665
+ hasAllPermissions: (perms: string[]) => hasAllPermissions(user, perms),
666
+
667
+ /**
668
+ * All permissions the user has (from all roles)
669
+ */
670
+ permissions,
671
+
672
+ /**
673
+ * Is user admin (has admin:* permission)
674
+ */
675
+ isAdmin: permissions.includes('admin:*'),
676
+
677
+ /**
678
+ * Is session loading
679
+ */
680
+ isLoading: !session,
681
+ };
682
+ }
683
+
684
+ /**
685
+ * Simple hook to check a single permission
686
+ * Usage: const canWrite = useHasPermission('users:write');
687
+ */
688
+ export function useHasPermission(permission: string): boolean {
689
+ const { hasPermission } = usePermissions();
690
+ return hasPermission(permission);
691
+ }
692
+ `;
693
+ await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'hooks'));
694
+ await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'hooks/usePermissions.ts'), usePermissionsContent);
695
+ // 4. Create components/PermissionGate.tsx - Conditional rendering component
696
+ const permissionGateContent = `'use client';
697
+
698
+ import { usePermissions } from '@/hooks/usePermissions';
699
+ import { ReactNode } from 'react';
700
+
701
+ interface PermissionGateProps {
702
+ /** Required permission */
703
+ permission?: string;
704
+ /** List of permissions (requires ANY by default) */
705
+ permissions?: string[];
706
+ /** If true, requires ALL permissions instead of ANY */
707
+ requireAll?: boolean;
708
+ /** Content to show if user has permission */
709
+ children: ReactNode;
710
+ /** Optional fallback content if permission denied */
711
+ fallback?: ReactNode;
712
+ }
713
+
714
+ /**
715
+ * Conditionally render content based on permissions
716
+ *
717
+ * Usage:
718
+ * <PermissionGate permission="users:write">
719
+ * <button>Edit User</button>
720
+ * </PermissionGate>
721
+ */
722
+ export function PermissionGate({
723
+ permission,
724
+ permissions,
725
+ requireAll = false,
726
+ children,
727
+ fallback = null,
728
+ }: PermissionGateProps) {
729
+ const { hasPermission, hasAnyPermission, hasAllPermissions } = usePermissions();
730
+
731
+ let hasAccess = false;
732
+
733
+ if (permission) {
734
+ hasAccess = hasPermission(permission);
735
+ } else if (permissions) {
736
+ hasAccess = requireAll
737
+ ? hasAllPermissions(permissions)
738
+ : hasAnyPermission(permissions);
739
+ }
740
+
741
+ return <>{hasAccess ? children : fallback}</>;
742
+ }
743
+ `;
744
+ await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'components'));
745
+ await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'components/PermissionGate.tsx'), permissionGateContent);
746
+ spinner.succeed(chalk_1.default.green('RBAC permissions system installed'));
747
+ console.log(chalk_1.default.cyan('\\nšŸ“ Created files:'));
748
+ console.log(chalk_1.default.white(' āœ“ lib/permissions.ts - Core utilities'));
749
+ console.log(chalk_1.default.white(' āœ“ lib/auth/require-permission.ts - Server helpers'));
750
+ console.log(chalk_1.default.white(' āœ“ hooks/usePermissions.ts - Client hooks'));
751
+ console.log(chalk_1.default.white(' āœ“ components/PermissionGate.tsx - Conditional rendering'));
752
+ console.log(chalk_1.default.cyan('\\nšŸ“– Usage Examples:'));
753
+ console.log(chalk_1.default.yellow('\\nServer-side (API Routes):'));
754
+ console.log(chalk_1.default.white(`
755
+ import { requirePermission } from '@/lib/auth/require-permission';
756
+
757
+ export async function POST(req: NextRequest) {
758
+ const error = await requirePermission('users:write');
759
+ if (error) return error;
760
+
761
+ // User has permission, proceed...
762
+ }`));
763
+ console.log(chalk_1.default.yellow('\\nClient-side (Components):'));
764
+ console.log(chalk_1.default.white(`
765
+ import { usePermissions } from '@/hooks/usePermissions';
766
+
767
+ function MyComponent() {
768
+ const { hasPermission } = usePermissions();
769
+
770
+ return (
771
+ <button disabled={!hasPermission('users:write')}>
772
+ Edit User
773
+ </button>
774
+ );
775
+ }`));
776
+ console.log(chalk_1.default.yellow('\\nConditional Rendering:'));
777
+ console.log(chalk_1.default.white(`
778
+ import { PermissionGate } from '@/components/PermissionGate';
779
+
780
+ <PermissionGate permission="users:delete">
781
+ <button>Delete User</button>
782
+ </PermissionGate>`));
783
+ }
784
+ catch (error) {
785
+ spinner.fail(chalk_1.default.red('Failed to setup permissions system'));
786
+ console.error(error.message);
787
+ }
788
+ }