@chimerai/cli 0.2.89 → 0.2.91

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.
@@ -231,7 +231,15 @@ async function createCommand(projectName, options) {
231
231
  */
232
232
  function validateFeatureCombination(features) {
233
233
  const warnings = [];
234
- const authRequired = ['rbac', 'admin-dashboard', 'billing', 'mfa', 'gdpr', 'audit-log', 'chat-ui'];
234
+ const authRequired = [
235
+ 'rbac',
236
+ 'admin-dashboard',
237
+ 'billing',
238
+ 'mfa',
239
+ 'gdpr',
240
+ 'audit-log',
241
+ 'chat-ui',
242
+ ];
235
243
  if (!features.includes('auth')) {
236
244
  authRequired.forEach((f) => {
237
245
  if (features.includes(f)) {
@@ -312,10 +320,10 @@ async function createBaseStructure(targetDir, projectName, features) {
312
320
  'styles',
313
321
  ];
314
322
  // Add feature-specific directories
315
- if (features.includes('admin')) {
323
+ if (features.includes('admin-dashboard')) {
316
324
  dirs.push('app/admin', 'app/admin/settings', 'app/admin/logs');
317
325
  }
318
- if (features.includes('chat')) {
326
+ if (features.includes('ai-chat')) {
319
327
  dirs.push('app/(app)', 'app/(app)/chat', 'components/chat', 'app/api/conversations/[id]');
320
328
  }
321
329
  if (features.includes('auth')) {
@@ -327,7 +335,7 @@ async function createBaseStructure(targetDir, projectName, features) {
327
335
  if (features.includes('model-providers')) {
328
336
  dirs.push('app/dashboard/providers');
329
337
  }
330
- if (features.includes('prompts')) {
338
+ if (features.includes('prompt-management')) {
331
339
  dirs.push('app/dashboard/prompts', 'app/api/prompts', 'app/api/prompts/[id]');
332
340
  }
333
341
  for (const dir of dirs) {
@@ -385,10 +393,10 @@ async function createPackageJson(targetDir, projectName, features) {
385
393
  if (features.includes('model-providers')) {
386
394
  // dependencies['@chimerai/model-providers'] = 'workspace:*'; // Not available standalone yet
387
395
  }
388
- if (features.includes('admin')) {
396
+ if (features.includes('admin-dashboard')) {
389
397
  // dependencies['@chimerai/admin-ui'] = 'workspace:*'; // Not available standalone yet
390
398
  }
391
- if (features.includes('chat')) {
399
+ if (features.includes('ai-chat')) {
392
400
  dependencies['react-markdown'] = '^9.0.0';
393
401
  dependencies['remark-gfm'] = '^4.0.0';
394
402
  }
@@ -445,322 +453,322 @@ async function createTsConfig(targetDir) {
445
453
  }
446
454
  async function createPrismaSchema(targetDir, features, sqlite) {
447
455
  const dbProvider = sqlite ? 'sqlite' : 'postgresql';
448
- let schemaContent = `generator client {
449
- provider = "prisma-client-js"
450
- }
451
-
452
- datasource db {
453
- provider = "${dbProvider}"
454
- url = env("DATABASE_URL")
455
- }
456
-
456
+ let schemaContent = `generator client {
457
+ provider = "prisma-client-js"
458
+ }
459
+
460
+ datasource db {
461
+ provider = "${dbProvider}"
462
+ url = env("DATABASE_URL")
463
+ }
464
+
457
465
  `;
458
466
  // Add User model if auth is included
459
467
  if (features.includes('auth')) {
460
- schemaContent += `model User {
461
- id String @id @default(cuid())
462
- name String?
463
- email String? @unique
464
- emailVerified DateTime?
465
- image String?
466
- password String?
467
- createdAt DateTime @default(now())
468
- updatedAt DateTime @updatedAt
469
-
470
- accounts Account[]
471
- sessions Session[]
468
+ schemaContent += `model User {
469
+ id String @id @default(cuid())
470
+ name String?
471
+ email String? @unique
472
+ emailVerified DateTime?
473
+ image String?
474
+ password String?
475
+ createdAt DateTime @default(now())
476
+ updatedAt DateTime @updatedAt
477
+
478
+ accounts Account[]
479
+ sessions Session[]
472
480
  `;
473
481
  if (features.includes('rbac')) {
474
- schemaContent += ` roles UserRole[]
482
+ schemaContent += ` roles UserRole[]
475
483
  `;
476
484
  }
477
485
  // Provider relations (ALWAYS included)
478
- schemaContent += ` providers Provider[] @relation("CreatedProviders")
479
- apiUsage ApiUsage[]
480
- apiKeys ApiKey[]
486
+ schemaContent += ` providers Provider[] @relation("CreatedProviders")
487
+ apiUsage ApiUsage[]
488
+ apiKeys ApiKey[]
481
489
  `;
482
490
  if (features.includes('rbac')) {
483
- schemaContent += ` modelAccess ModelAccess[]
484
- auditLogs AuditLog[]
491
+ schemaContent += ` modelAccess ModelAccess[]
492
+ auditLogs AuditLog[]
485
493
  `;
486
494
  }
487
- if (features.includes('chat')) {
488
- schemaContent += ` conversations Conversation[]
495
+ if (features.includes('ai-chat')) {
496
+ schemaContent += ` conversations Conversation[]
489
497
  `;
490
498
  }
491
- schemaContent += `}
492
-
493
- model Account {
494
- id String @id @default(cuid())
495
- userId String
496
- type String
497
- provider String
498
- providerAccountId String
499
- refresh_token String? @db.Text
500
- access_token String? @db.Text
501
- expires_at Int?
502
- token_type String?
503
- scope String?
504
- id_token String? @db.Text
505
- session_state String?
506
-
507
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
508
-
509
- @@unique([provider, providerAccountId])
510
- }
511
-
512
- model Session {
513
- id String @id @default(cuid())
514
- sessionToken String @unique
515
- userId String
516
- expires DateTime
517
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
518
- }
519
-
520
- model VerificationToken {
521
- identifier String
522
- token String @unique
523
- expires DateTime
524
-
525
- @@unique([identifier, token])
526
- }
527
-
528
- model ApiKey {
529
- id String @id @default(cuid())
530
- name String
531
- keyHash String @unique
532
- userId String
533
- scopes String[] @default([])
534
- revoked Boolean @default(false)
535
- lastUsedAt DateTime?
536
- expiresAt DateTime?
537
- createdAt DateTime @default(now())
538
- updatedAt DateTime @updatedAt
539
-
540
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
541
-
542
- @@index([userId])
543
- }
544
-
499
+ schemaContent += `}
500
+
501
+ model Account {
502
+ id String @id @default(cuid())
503
+ userId String
504
+ type String
505
+ provider String
506
+ providerAccountId String
507
+ refresh_token String? @db.Text
508
+ access_token String? @db.Text
509
+ expires_at Int?
510
+ token_type String?
511
+ scope String?
512
+ id_token String? @db.Text
513
+ session_state String?
514
+
515
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
516
+
517
+ @@unique([provider, providerAccountId])
518
+ }
519
+
520
+ model Session {
521
+ id String @id @default(cuid())
522
+ sessionToken String @unique
523
+ userId String
524
+ expires DateTime
525
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
526
+ }
527
+
528
+ model VerificationToken {
529
+ identifier String
530
+ token String @unique
531
+ expires DateTime
532
+
533
+ @@unique([identifier, token])
534
+ }
535
+
536
+ model ApiKey {
537
+ id String @id @default(cuid())
538
+ name String
539
+ keyHash String @unique
540
+ userId String
541
+ scopes String[] @default([])
542
+ revoked Boolean @default(false)
543
+ lastUsedAt DateTime?
544
+ expiresAt DateTime?
545
+ createdAt DateTime @default(now())
546
+ updatedAt DateTime @updatedAt
547
+
548
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
549
+
550
+ @@index([userId])
551
+ }
552
+
545
553
  `;
546
554
  }
547
555
  // Add RBAC models
548
556
  if (features.includes('rbac')) {
549
- schemaContent += `model Role {
550
- id String @id @default(cuid())
551
- name String @unique
552
- description String?
553
- permissions String[]
554
- createdAt DateTime @default(now())
555
- updatedAt DateTime @updatedAt
556
-
557
- users UserRole[]
558
- }
559
-
560
- model UserRole {
561
- userId String
562
- roleId String
563
-
564
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
565
- role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
566
-
567
- @@id([userId, roleId])
568
- }
569
-
557
+ schemaContent += `model Role {
558
+ id String @id @default(cuid())
559
+ name String @unique
560
+ description String?
561
+ permissions String[]
562
+ createdAt DateTime @default(now())
563
+ updatedAt DateTime @updatedAt
564
+
565
+ users UserRole[]
566
+ }
567
+
568
+ model UserRole {
569
+ userId String
570
+ roleId String
571
+
572
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
573
+ role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
574
+
575
+ @@id([userId, roleId])
576
+ }
577
+
570
578
  `;
571
579
  }
572
580
  // ── PROVIDER MODELS (ALWAYS included — core infrastructure) ──────────
573
- schemaContent += `// === Provider Management (Core Infrastructure) ===
574
-
575
- model Provider {
576
- id String @id @default(cuid())
577
- name String
578
- type String // "openai", "anthropic", "ollama", "google", "custom"
579
- description String?
580
- baseUrl String?
581
- apiKey String? @db.Text // AES-256-GCM encrypted, null for keyless providers (e.g. Ollama)
582
- config Json @default("{}")
583
- status String @default("active")
584
- isDefault Boolean @default(false)
585
- priority Int @default(0)
586
- tags String[]
587
- createdAt DateTime @default(now())
588
- updatedAt DateTime @updatedAt
589
- createdBy String?
590
-
591
- models Model[]
592
- health ProviderHealth?
593
- apiUsage ApiUsage[]
594
- ${features.includes('chat') ? ' conversations Conversation[]\n' : ''}${features.includes('auth') ? ' creator User? @relation("CreatedProviders", fields: [createdBy], references: [id])\n' : ''}
595
- @@index([type])
596
- @@index([status])
597
- }
598
-
599
- model Model {
600
- id String @id @default(cuid())
601
- providerId String
602
- modelId String // e.g. "gpt-4", "claude-3-sonnet"
603
- name String
604
- description String?
605
- capabilities String[] // ["chat", "embedding", "image", "vision"]
606
- contextWindow Int @default(4096)
607
- maxOutputTokens Int?
608
- inputCost Float @default(0) // $ per 1M tokens
609
- outputCost Float @default(0)
610
- isAvailable Boolean @default(true)
611
- isDeprecated Boolean @default(false)
612
-
613
- provider Provider @relation(fields: [providerId], references: [id], onDelete: Cascade)
614
-
615
- @@unique([providerId, modelId])
616
- @@index([providerId])
617
- }
618
-
619
- model ProviderHealth {
620
- id String @id @default(cuid())
621
- providerId String @unique
622
- status String @default("unknown") // "healthy", "degraded", "unhealthy"
623
- responseTime Int? // ms
624
- lastCheck DateTime @default(now())
625
- errorMessage String?
626
- modelsAvailable Int @default(0)
627
- chatAvailable Boolean @default(false)
628
- embeddingAvailable Boolean @default(false)
629
- apiKeyValid Boolean @default(false)
630
-
631
- provider Provider @relation(fields: [providerId], references: [id], onDelete: Cascade)
632
- }
633
-
634
- model ApiUsage {
635
- id String @id @default(cuid())
636
- userId String
637
- providerId String?
638
- model String
639
- endpoint String
640
- promptTokens Int @default(0)
641
- completionTokens Int @default(0)
642
- totalTokens Int @default(0)
643
- tokensUsed Int @default(0)
644
- creditsUsed Int @default(0)
645
- cost Float @default(0)
646
- success Boolean @default(true)
647
- errorMessage String?
648
- responseTime Int @default(0) // ms
649
- createdAt DateTime @default(now())
650
-
651
- ${features.includes('auth') ? ' user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n' : ''} provider Provider? @relation(fields: [providerId], references: [id], onDelete: SetNull)
652
-
653
- @@index([userId])
654
- @@index([providerId])
655
- @@index([createdAt])
656
- }
657
-
581
+ schemaContent += `// === Provider Management (Core Infrastructure) ===
582
+
583
+ model Provider {
584
+ id String @id @default(cuid())
585
+ name String
586
+ type String // "openai", "anthropic", "ollama", "google", "custom"
587
+ description String?
588
+ baseUrl String?
589
+ apiKey String? @db.Text // AES-256-GCM encrypted, null for keyless providers (e.g. Ollama)
590
+ config Json @default("{}")
591
+ status String @default("active")
592
+ isDefault Boolean @default(false)
593
+ priority Int @default(0)
594
+ tags String[]
595
+ createdAt DateTime @default(now())
596
+ updatedAt DateTime @updatedAt
597
+ createdBy String?
598
+
599
+ models Model[]
600
+ health ProviderHealth?
601
+ apiUsage ApiUsage[]
602
+ ${features.includes('ai-chat') ? ' conversations Conversation[]\n' : ''}${features.includes('auth') ? ' creator User? @relation("CreatedProviders", fields: [createdBy], references: [id])\n' : ''}
603
+ @@index([type])
604
+ @@index([status])
605
+ }
606
+
607
+ model Model {
608
+ id String @id @default(cuid())
609
+ providerId String
610
+ modelId String // e.g. "gpt-4", "claude-3-sonnet"
611
+ name String
612
+ description String?
613
+ capabilities String[] // ["chat", "embedding", "image", "vision"]
614
+ contextWindow Int @default(4096)
615
+ maxOutputTokens Int?
616
+ inputCost Float @default(0) // $ per 1M tokens
617
+ outputCost Float @default(0)
618
+ isAvailable Boolean @default(true)
619
+ isDeprecated Boolean @default(false)
620
+
621
+ provider Provider @relation(fields: [providerId], references: [id], onDelete: Cascade)
622
+
623
+ @@unique([providerId, modelId])
624
+ @@index([providerId])
625
+ }
626
+
627
+ model ProviderHealth {
628
+ id String @id @default(cuid())
629
+ providerId String @unique
630
+ status String @default("unknown") // "healthy", "degraded", "unhealthy"
631
+ responseTime Int? // ms
632
+ lastCheck DateTime @default(now())
633
+ errorMessage String?
634
+ modelsAvailable Int @default(0)
635
+ chatAvailable Boolean @default(false)
636
+ embeddingAvailable Boolean @default(false)
637
+ apiKeyValid Boolean @default(false)
638
+
639
+ provider Provider @relation(fields: [providerId], references: [id], onDelete: Cascade)
640
+ }
641
+
642
+ model ApiUsage {
643
+ id String @id @default(cuid())
644
+ userId String
645
+ providerId String?
646
+ model String
647
+ endpoint String
648
+ promptTokens Int @default(0)
649
+ completionTokens Int @default(0)
650
+ totalTokens Int @default(0)
651
+ tokensUsed Int @default(0)
652
+ creditsUsed Int @default(0)
653
+ cost Float @default(0)
654
+ success Boolean @default(true)
655
+ errorMessage String?
656
+ responseTime Int @default(0) // ms
657
+ createdAt DateTime @default(now())
658
+
659
+ ${features.includes('auth') ? ' user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n' : ''} provider Provider? @relation(fields: [providerId], references: [id], onDelete: SetNull)
660
+
661
+ @@index([userId])
662
+ @@index([providerId])
663
+ @@index([createdAt])
664
+ }
665
+
658
666
  `;
659
667
  // Add ModelAccess when auth + rbac are enabled (for requireModelPermission)
660
668
  if (features.includes('auth') && features.includes('rbac')) {
661
- schemaContent += `model ModelAccess {
662
- id String @id @default(cuid())
663
- userId String
664
- modelId String
665
- granted Boolean @default(true)
666
- createdAt DateTime @default(now())
667
- updatedAt DateTime @updatedAt
668
-
669
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
670
-
671
- @@unique([userId, modelId])
672
- @@index([userId])
673
- @@index([modelId])
674
- }
675
-
676
- model AuditLog {
677
- id String @id @default(cuid())
678
- action String
679
- userId String
680
- targetType String?
681
- targetId String?
682
- metadata Json?
683
- ipAddress String?
684
- createdAt DateTime @default(now())
685
-
686
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
687
-
688
- @@index([userId])
689
- @@index([action])
690
- @@index([createdAt])
691
- }
692
-
669
+ schemaContent += `model ModelAccess {
670
+ id String @id @default(cuid())
671
+ userId String
672
+ modelId String
673
+ granted Boolean @default(true)
674
+ createdAt DateTime @default(now())
675
+ updatedAt DateTime @updatedAt
676
+
677
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
678
+
679
+ @@unique([userId, modelId])
680
+ @@index([userId])
681
+ @@index([modelId])
682
+ }
683
+
684
+ model AuditLog {
685
+ id String @id @default(cuid())
686
+ action String
687
+ userId String
688
+ targetType String?
689
+ targetId String?
690
+ metadata Json?
691
+ ipAddress String?
692
+ createdAt DateTime @default(now())
693
+
694
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
695
+
696
+ @@index([userId])
697
+ @@index([action])
698
+ @@index([createdAt])
699
+ }
700
+
693
701
  `;
694
702
  }
695
703
  // Add Prompt Templates
696
- if (features.includes('prompts')) {
697
- schemaContent += `model PromptTemplate {
698
- id String @id @default(cuid())
699
- name String @unique
700
- category String
701
- description String?
702
- content String @db.Text
703
- variables String[]
704
- language String @default("en")
705
- version Int @default(1)
706
- isActive Boolean @default(true)
707
- isDefault Boolean @default(false)
708
- tags String[]
709
- metadata Json?
710
- createdBy String?
711
- createdAt DateTime @default(now())
712
- updatedAt DateTime @updatedAt
713
-
714
- @@index([category])
715
- @@index([isActive])
716
- @@index([isDefault])
717
- }
718
-
704
+ if (features.includes('prompt-management')) {
705
+ schemaContent += `model PromptTemplate {
706
+ id String @id @default(cuid())
707
+ name String @unique
708
+ category String
709
+ description String?
710
+ content String @db.Text
711
+ variables String[]
712
+ language String @default("en")
713
+ version Int @default(1)
714
+ isActive Boolean @default(true)
715
+ isDefault Boolean @default(false)
716
+ tags String[]
717
+ metadata Json?
718
+ createdBy String?
719
+ createdAt DateTime @default(now())
720
+ updatedAt DateTime @updatedAt
721
+
722
+ @@index([category])
723
+ @@index([isActive])
724
+ @@index([isDefault])
725
+ }
726
+
719
727
  `;
720
728
  }
721
729
  // Add Conversation & Message models for chat feature
722
- if (features.includes('chat')) {
723
- schemaContent += `model Conversation {
724
- id String @id @default(cuid())
725
- userId String
726
- title String @default("New Chat")
727
- model String?
728
- providerId String?
729
- metadata Json?
730
- archived Boolean @default(false)
731
- createdAt DateTime @default(now())
732
- updatedAt DateTime @updatedAt
733
-
734
- ${features.includes('auth') ? ' user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n' : ''} provider Provider? @relation(fields: [providerId], references: [id], onDelete: SetNull)
735
- messages Message[]
736
-
737
- @@index([userId])
738
- @@index([archived])
739
- @@index([providerId])
740
- }
741
-
742
- model Message {
743
- id String @id @default(cuid())
744
- conversationId String
745
- role String
746
- content String @db.Text
747
- model String?
748
- tokens Int?
749
- createdAt DateTime @default(now())
750
-
751
- conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
752
-
753
- @@index([conversationId])
754
- }
755
-
730
+ if (features.includes('ai-chat')) {
731
+ schemaContent += `model Conversation {
732
+ id String @id @default(cuid())
733
+ userId String
734
+ title String @default("New Chat")
735
+ model String?
736
+ providerId String?
737
+ metadata Json?
738
+ archived Boolean @default(false)
739
+ createdAt DateTime @default(now())
740
+ updatedAt DateTime @updatedAt
741
+
742
+ ${features.includes('auth') ? ' user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n' : ''} provider Provider? @relation(fields: [providerId], references: [id], onDelete: SetNull)
743
+ messages Message[]
744
+
745
+ @@index([userId])
746
+ @@index([archived])
747
+ @@index([providerId])
748
+ }
749
+
750
+ model Message {
751
+ id String @id @default(cuid())
752
+ conversationId String
753
+ role String
754
+ content String @db.Text
755
+ model String?
756
+ tokens Int?
757
+ createdAt DateTime @default(now())
758
+
759
+ conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
760
+
761
+ @@index([conversationId])
762
+ }
763
+
756
764
  `;
757
765
  }
758
766
  // SystemSetting is always included (used by admin settings + app-settings API)
759
- schemaContent += `model SystemSetting {
760
- key String @id
761
- value String @db.Text
762
- updatedAt DateTime @updatedAt
763
- }
767
+ schemaContent += `model SystemSetting {
768
+ key String @id
769
+ value String @db.Text
770
+ updatedAt DateTime @updatedAt
771
+ }
764
772
  `;
765
773
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'prisma/schema.prisma'), schemaContent);
766
774
  // Apply SQLite compatibility transform if needed
@@ -816,55 +824,55 @@ async function createEnvFiles(targetDir, features, sqlite) {
816
824
  // Generate a random NextAuth secret (32 bytes hex = 64 chars)
817
825
  const nextAuthSecret = Array.from({ length: 64 }, () => '0123456789abcdef'.charAt(Math.floor(Math.random() * 16))).join('');
818
826
  const dbUrl = sqlite ? 'file:./dev.db' : 'postgresql://postgres:postgres@localhost:5432/myapp_db';
819
- let envContent = `# Database${sqlite ? ' (SQLite — no Docker needed)' : ''}
820
- DATABASE_URL=${dbUrl}
821
-
827
+ let envContent = `# Database${sqlite ? ' (SQLite — no Docker needed)' : ''}
828
+ DATABASE_URL=${dbUrl}
829
+
822
830
  `;
823
831
  if (features.includes('auth')) {
824
- envContent += `# Authentication
825
- NEXTAUTH_SECRET=${nextAuthSecret}
826
- NEXTAUTH_URL=http://localhost:3000
827
-
832
+ envContent += `# Authentication
833
+ NEXTAUTH_SECRET=${nextAuthSecret}
834
+ NEXTAUTH_URL=http://localhost:3000
835
+
828
836
  `;
829
837
  }
830
838
  // ── Provider Infrastructure (ALWAYS included — core) ──────────
831
- envContent += `# Provider Encryption (AES-256-GCM for API key storage)
832
- PROVIDER_ENCRYPTION_KEY=${encryptionKey}
833
-
834
- # Internal API Token (AI-Service ↔ Frontend communication)
835
- INTERNAL_API_TOKEN=${internalToken}
836
-
837
- # AI Service URL
838
- AI_SERVICE_URL=http://localhost:8002
839
-
840
- # AI Providers (Optional — can also be added via Provider Management UI)
841
- OPENAI_API_KEY=
842
- ANTHROPIC_API_KEY=
843
- AZURE_OPENAI_API_KEY=
844
- AZURE_OPENAI_ENDPOINT=
845
-
839
+ envContent += `# Provider Encryption (AES-256-GCM for API key storage)
840
+ PROVIDER_ENCRYPTION_KEY=${encryptionKey}
841
+
842
+ # Internal API Token (AI-Service ↔ Frontend communication)
843
+ INTERNAL_API_TOKEN=${internalToken}
844
+
845
+ # AI Service URL
846
+ AI_SERVICE_URL=http://localhost:8002
847
+
848
+ # AI Providers (Optional — can also be added via Provider Management UI)
849
+ OPENAI_API_KEY=
850
+ ANTHROPIC_API_KEY=
851
+ AZURE_OPENAI_API_KEY=
852
+ AZURE_OPENAI_ENDPOINT=
853
+
846
854
  `;
847
855
  if (features.includes('billing')) {
848
- envContent += `# Stripe
849
- STRIPE_PUBLISHABLE_KEY=
850
- STRIPE_SECRET_KEY=
851
- STRIPE_WEBHOOK_SECRET=
852
-
856
+ envContent += `# Stripe
857
+ STRIPE_PUBLISHABLE_KEY=
858
+ STRIPE_SECRET_KEY=
859
+ STRIPE_WEBHOOK_SECRET=
860
+
853
861
  `;
854
862
  }
855
863
  // Widget / External Integration
856
- envContent += `# CORS / Widget Configuration (External Chat Embedding)
857
- # Comma-separated list of allowed origins for cross-origin requests (e.g. Blazor, Vue, React embeds)
858
- # Use * to allow all origins (development only!)
859
- # Example: CORS_ALLOWED_ORIGINS=https://my-blog.com,https://my-shop.com
860
- CORS_ALLOWED_ORIGINS=
861
- # Legacy alias (still supported): WIDGET_ALLOWED_ORIGINS
862
-
863
- # Upstash Redis (optional — for rate-limiting in serverless/multi-instance)
864
- # Without this, rate-limiting uses in-memory fallback (fine for single-instance)
865
- # UPSTASH_REDIS_REST_URL=
866
- # UPSTASH_REDIS_REST_TOKEN=
867
-
864
+ envContent += `# CORS / Widget Configuration (External Chat Embedding)
865
+ # Comma-separated list of allowed origins for cross-origin requests (e.g. Blazor, Vue, React embeds)
866
+ # Use * to allow all origins (development only!)
867
+ # Example: CORS_ALLOWED_ORIGINS=https://my-blog.com,https://my-shop.com
868
+ CORS_ALLOWED_ORIGINS=
869
+ # Legacy alias (still supported): WIDGET_ALLOWED_ORIGINS
870
+
871
+ # Upstash Redis (optional — for rate-limiting in serverless/multi-instance)
872
+ # Without this, rate-limiting uses in-memory fallback (fine for single-instance)
873
+ # UPSTASH_REDIS_REST_URL=
874
+ # UPSTASH_REDIS_REST_TOKEN=
875
+
868
876
  `;
869
877
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, '.env'), envContent);
870
878
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, '.env.example'), envContent
@@ -1004,7 +1012,15 @@ async function copyFeatureFiles(targetDir, features) {
1004
1012
  const providersPage = templates.generateModelProvidersPage();
1005
1013
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'app/dashboard/providers'));
1006
1014
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'app/dashboard/providers/page.tsx'), providersPage);
1007
- if (features.includes('admin')) {
1015
+ // No-auth dashboard landing page + layout (when auth is not selected)
1016
+ if (!features.includes('auth')) {
1017
+ const noAuthDashPage = templates.generateDashboardPageNoAuth(features);
1018
+ await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'app/dashboard'));
1019
+ await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'app/dashboard/page.tsx'), noAuthDashPage);
1020
+ const noAuthDashLayout = templates.generateDashboardLayoutNoAuth();
1021
+ await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'app/dashboard/layout.tsx'), noAuthDashLayout);
1022
+ }
1023
+ if (features.includes('admin-dashboard')) {
1008
1024
  // Admin layout with session check + admin role guard
1009
1025
  const adminLayout = templates.generateAdminLayout();
1010
1026
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'app/admin'));
@@ -1059,136 +1075,136 @@ async function copyFeatureFiles(targetDir, features) {
1059
1075
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'lib'));
1060
1076
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'lib/use-app-name.ts'), useAppNameHook);
1061
1077
  // ── RBAC Permission utilities (required by admin API routes) ───
1062
- const permissionsLib = `/**
1063
- * Permission utility functions
1064
- * Handles permission checks and role-based access control
1065
- */
1066
-
1067
- export const AVAILABLE_PERMISSIONS = [
1068
- 'users:read',
1069
- 'users:write',
1070
- 'users:delete',
1071
- 'roles:read',
1072
- 'roles:write',
1073
- 'roles:delete',
1074
- 'settings:read',
1075
- 'settings:write',
1076
- 'admin:*',
1077
- ] as const;
1078
-
1079
- export type Permission = typeof AVAILABLE_PERMISSIONS[number];
1080
-
1081
- interface User {
1082
- id: string;
1083
- email: string;
1084
- roles?: Array<{ permissions: string[] }>;
1085
- }
1086
-
1087
- export function hasPermission(user: User | null, permission: string): boolean {
1088
- if (!user || !user.roles) return false;
1089
- const allPermissions = user.roles.flatMap(role => role.permissions || []);
1090
-
1091
- // Tier 1: Super-Wildcard — '*' matcht ALLES
1092
- if (allPermissions.includes('*')) return true;
1093
-
1094
- // Tier 2: Kategorie-Wildcard — 'admin:*' matcht 'admin:users:read', 'admin:roles:write', etc.
1095
- for (const perm of allPermissions) {
1096
- if (perm.endsWith(':*')) {
1097
- const prefix = perm.slice(0, -1); // 'admin:*' → 'admin:'
1098
- if (permission.startsWith(prefix)) return true;
1099
- }
1100
- }
1101
-
1102
- // Tier 3: Exakter Match
1103
- return allPermissions.includes(permission);
1104
- }
1105
-
1106
- export function hasAnyPermission(user: User | null, permissions: string[]): boolean {
1107
- if (!user) return false;
1108
- return permissions.some(permission => hasPermission(user, permission));
1109
- }
1110
-
1111
- export function hasAllPermissions(user: User | null, permissions: string[]): boolean {
1112
- if (!user) return false;
1113
- return permissions.every(permission => hasPermission(user, permission));
1114
- }
1115
-
1116
- export function getUserPermissions(user: User | null): string[] {
1117
- if (!user || !user.roles) return [];
1118
- return [...new Set(user.roles.flatMap(role => role.permissions || []))];
1119
- }
1120
-
1121
- export function isValidPermission(permission: string): boolean {
1122
- return AVAILABLE_PERMISSIONS.includes(permission as Permission);
1123
- }
1078
+ const permissionsLib = `/**
1079
+ * Permission utility functions
1080
+ * Handles permission checks and role-based access control
1081
+ */
1082
+
1083
+ export const AVAILABLE_PERMISSIONS = [
1084
+ 'users:read',
1085
+ 'users:write',
1086
+ 'users:delete',
1087
+ 'roles:read',
1088
+ 'roles:write',
1089
+ 'roles:delete',
1090
+ 'settings:read',
1091
+ 'settings:write',
1092
+ 'admin:*',
1093
+ ] as const;
1094
+
1095
+ export type Permission = typeof AVAILABLE_PERMISSIONS[number];
1096
+
1097
+ interface User {
1098
+ id: string;
1099
+ email: string;
1100
+ roles?: Array<{ permissions: string[] }>;
1101
+ }
1102
+
1103
+ export function hasPermission(user: User | null, permission: string): boolean {
1104
+ if (!user || !user.roles) return false;
1105
+ const allPermissions = user.roles.flatMap(role => role.permissions || []);
1106
+
1107
+ // Tier 1: Super-Wildcard — '*' matcht ALLES
1108
+ if (allPermissions.includes('*')) return true;
1109
+
1110
+ // Tier 2: Kategorie-Wildcard — 'admin:*' matcht 'admin:users:read', 'admin:roles:write', etc.
1111
+ for (const perm of allPermissions) {
1112
+ if (perm.endsWith(':*')) {
1113
+ const prefix = perm.slice(0, -1); // 'admin:*' → 'admin:'
1114
+ if (permission.startsWith(prefix)) return true;
1115
+ }
1116
+ }
1117
+
1118
+ // Tier 3: Exakter Match
1119
+ return allPermissions.includes(permission);
1120
+ }
1121
+
1122
+ export function hasAnyPermission(user: User | null, permissions: string[]): boolean {
1123
+ if (!user) return false;
1124
+ return permissions.some(permission => hasPermission(user, permission));
1125
+ }
1126
+
1127
+ export function hasAllPermissions(user: User | null, permissions: string[]): boolean {
1128
+ if (!user) return false;
1129
+ return permissions.every(permission => hasPermission(user, permission));
1130
+ }
1131
+
1132
+ export function getUserPermissions(user: User | null): string[] {
1133
+ if (!user || !user.roles) return [];
1134
+ return [...new Set(user.roles.flatMap(role => role.permissions || []))];
1135
+ }
1136
+
1137
+ export function isValidPermission(permission: string): boolean {
1138
+ return AVAILABLE_PERMISSIONS.includes(permission as Permission);
1139
+ }
1124
1140
  `;
1125
1141
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'lib/permissions.ts'), permissionsLib);
1126
- const requirePermissionLib = `import { getServerSession } from 'next-auth';
1127
- import { NextResponse } from 'next/server';
1128
- import { authOptions } from '@/lib/auth';
1129
- import { hasPermission } from '@/lib/permissions';
1130
-
1131
- /**
1132
- * Server-side permission check for API routes
1133
- * Returns NextResponse with 401/403 if check fails, null if OK
1134
- */
1135
- export async function requirePermission(permission: string) {
1136
- const session = await getServerSession(authOptions);
1137
-
1138
- if (!session || !session.user) {
1139
- return NextResponse.json(
1140
- { error: 'Unauthorized - Please sign in' },
1141
- { status: 401 }
1142
- );
1143
- }
1144
-
1145
- const user = await getServerSessionWithPermissions();
1146
-
1147
- if (!user || !hasPermission(user as any, permission)) {
1148
- return NextResponse.json(
1149
- { error: \`Forbidden - Required permission: \${permission}\` },
1150
- { status: 403 }
1151
- );
1152
- }
1153
-
1154
- return null;
1155
- }
1156
-
1157
- async function getServerSessionWithPermissions() {
1158
- const session = await getServerSession(authOptions);
1159
- if (!session?.user?.email) return null;
1160
-
1161
- const { prisma } = await import('@/lib/prisma');
1162
- const user = await prisma.user.findUnique({
1163
- where: { email: session.user.email },
1164
- include: {
1165
- roles: {
1166
- select: {
1167
- role: {
1168
- select: {
1169
- id: true,
1170
- name: true,
1171
- permissions: true
1172
- }
1173
- }
1174
- }
1175
- }
1176
- }
1177
- });
1178
-
1179
- if (!user) return null;
1180
-
1181
- // Flatten UserRole[] → { permissions: string[] }[]
1182
- return {
1183
- ...user,
1184
- roles: (user.roles as any[]).map((ur: any) => ur.role),
1185
- };
1186
- }
1142
+ const requirePermissionLib = `import { getServerSession } from 'next-auth';
1143
+ import { NextResponse } from 'next/server';
1144
+ import { authOptions } from '@/lib/auth';
1145
+ import { hasPermission } from '@/lib/permissions';
1146
+
1147
+ /**
1148
+ * Server-side permission check for API routes
1149
+ * Returns NextResponse with 401/403 if check fails, null if OK
1150
+ */
1151
+ export async function requirePermission(permission: string) {
1152
+ const session = await getServerSession(authOptions);
1153
+
1154
+ if (!session || !session.user) {
1155
+ return NextResponse.json(
1156
+ { error: 'Unauthorized - Please sign in' },
1157
+ { status: 401 }
1158
+ );
1159
+ }
1160
+
1161
+ const user = await getServerSessionWithPermissions();
1162
+
1163
+ if (!user || !hasPermission(user as any, permission)) {
1164
+ return NextResponse.json(
1165
+ { error: \`Forbidden - Required permission: \${permission}\` },
1166
+ { status: 403 }
1167
+ );
1168
+ }
1169
+
1170
+ return null;
1171
+ }
1172
+
1173
+ async function getServerSessionWithPermissions() {
1174
+ const session = await getServerSession(authOptions);
1175
+ if (!session?.user?.email) return null;
1176
+
1177
+ const { prisma } = await import('@/lib/prisma');
1178
+ const user = await prisma.user.findUnique({
1179
+ where: { email: session.user.email },
1180
+ include: {
1181
+ roles: {
1182
+ select: {
1183
+ role: {
1184
+ select: {
1185
+ id: true,
1186
+ name: true,
1187
+ permissions: true
1188
+ }
1189
+ }
1190
+ }
1191
+ }
1192
+ }
1193
+ });
1194
+
1195
+ if (!user) return null;
1196
+
1197
+ // Flatten UserRole[] → { permissions: string[] }[]
1198
+ return {
1199
+ ...user,
1200
+ roles: (user.roles as any[]).map((ur: any) => ur.role),
1201
+ };
1202
+ }
1187
1203
  `;
1188
1204
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'lib/auth'));
1189
1205
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'lib/auth/require-permission.ts'), requirePermissionLib);
1190
1206
  }
1191
- if (features.includes('prompts')) {
1207
+ if (features.includes('prompt-management')) {
1192
1208
  const promptsPage = templates.generatePromptManagementPage();
1193
1209
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'app/dashboard/prompts'));
1194
1210
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'app/dashboard/prompts/page.tsx'), promptsPage);
@@ -1200,7 +1216,7 @@ async function getServerSessionWithPermissions() {
1200
1216
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'app/api/prompts/[id]'));
1201
1217
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'app/api/prompts/[id]/route.ts'), promptsIdRoute);
1202
1218
  }
1203
- if (features.includes('chat')) {
1219
+ if (features.includes('ai-chat')) {
1204
1220
  // Modular chat components (useChat hook + individual components)
1205
1221
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'components/chat'));
1206
1222
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'components/chat/use-chat.ts'), templates.generateUseChatHook());
@@ -1218,7 +1234,7 @@ async function getServerSessionWithPermissions() {
1218
1234
  ].join('\n') + '\n');
1219
1235
  // Chat page
1220
1236
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'app/(app)/chat'));
1221
- await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'app/(app)/chat/page.tsx'), templates.generateChatPage());
1237
+ await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'app/(app)/chat/page.tsx'), features.includes('auth') ? templates.generateChatPage() : templates.generateChatPageNoAuth());
1222
1238
  // API routes
1223
1239
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'app/api/v1/chat/stream'));
1224
1240
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'app/api/v1/chat/stream/route.ts'), templates.generateChatStreamRouteWithPersistence());
@@ -1279,215 +1295,215 @@ async function getServerSessionWithPermissions() {
1279
1295
  async function createSeedScript(targetDir, features, sqlite) {
1280
1296
  // Without auth there are no users/providers to seed — write a minimal placeholder
1281
1297
  if (!features.includes('auth')) {
1282
- const minimalSeed = `import { PrismaClient } from '@prisma/client';
1283
- const prisma = new PrismaClient();
1284
- async function main() {
1285
- console.log('🌱 No auth feature selected — nothing to seed yet.');
1286
- console.log(' Run: chimerai add auth to add authentication.');
1287
- }
1288
- main().catch(console.error).finally(() => prisma.$disconnect());
1298
+ const minimalSeed = `import { PrismaClient } from '@prisma/client';
1299
+ const prisma = new PrismaClient();
1300
+ async function main() {
1301
+ console.log('🌱 No auth feature selected — nothing to seed yet.');
1302
+ console.log(' Run: chimerai add auth to add authentication.');
1303
+ }
1304
+ main().catch(console.error).finally(() => prisma.$disconnect());
1289
1305
  `;
1290
1306
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'prisma/seed.ts'), minimalSeed);
1291
1307
  return;
1292
1308
  }
1293
1309
  // Generate seed script with feature-specific configuration
1294
- const seedScript = `import { PrismaClient } from '@prisma/client';
1295
- import * as bcrypt from 'bcryptjs';
1296
- import crypto from 'crypto';
1297
-
1298
- const prisma = new PrismaClient();
1299
-
1300
- // ── Encryption helpers (same as lib/encryption.ts) ──────────────
1301
- function getKey(): Buffer {
1302
- const key = process.env.PROVIDER_ENCRYPTION_KEY;
1303
- if (!key) throw new Error('PROVIDER_ENCRYPTION_KEY required for seeding');
1304
- if (key.length === 64 && /^[0-9a-fA-F]+$/.test(key)) {
1305
- return Buffer.from(key, 'hex');
1306
- }
1307
- return crypto.createHash('sha256').update(key).digest();
1308
- }
1309
-
1310
- function encrypt(text: string): string {
1311
- if (!text) return '';
1312
- const key = getKey();
1313
- const iv = crypto.randomBytes(16);
1314
- const cipher = crypto.createCipheriv('aes-256-gcm', key, iv, { authTagLength: 16 });
1315
- let encrypted = cipher.update(text, 'utf8', 'base64');
1316
- encrypted += cipher.final('base64');
1317
- const authTag = cipher.getAuthTag();
1318
- return iv.toString('base64') + ':' + authTag.toString('base64') + ':' + encrypted;
1319
- }
1320
- // ────────────────────────────────────────────────────────────────
1321
-
1322
- async function main() {
1323
- console.log('🌱 Seeding database...');
1324
-
1325
- // Create default admin user (next-auth is always a core dependency)
1326
- const hashedPassword = await bcrypt.hash('admin123', 10);
1327
- const admin = await prisma.user.upsert({
1328
- where: { email: 'admin@example.com' },
1329
- update: {},
1330
- create: {
1331
- email: 'admin@example.com',
1332
- name: 'Admin User',
1333
- password: hashedPassword,
1334
- },
1335
- });
1336
-
1337
- console.log('✅ Admin user created: admin@example.com / admin123');
1310
+ const seedScript = `import { PrismaClient } from '@prisma/client';
1311
+ import * as bcrypt from 'bcryptjs';
1312
+ import crypto from 'crypto';
1313
+
1314
+ const prisma = new PrismaClient();
1315
+
1316
+ // ── Encryption helpers (same as lib/encryption.ts) ──────────────
1317
+ function getKey(): Buffer {
1318
+ const key = process.env.PROVIDER_ENCRYPTION_KEY;
1319
+ if (!key) throw new Error('PROVIDER_ENCRYPTION_KEY required for seeding');
1320
+ if (key.length === 64 && /^[0-9a-fA-F]+$/.test(key)) {
1321
+ return Buffer.from(key, 'hex');
1322
+ }
1323
+ return crypto.createHash('sha256').update(key).digest();
1324
+ }
1325
+
1326
+ function encrypt(text: string): string {
1327
+ if (!text) return '';
1328
+ const key = getKey();
1329
+ const iv = crypto.randomBytes(16);
1330
+ const cipher = crypto.createCipheriv('aes-256-gcm', key, iv, { authTagLength: 16 });
1331
+ let encrypted = cipher.update(text, 'utf8', 'base64');
1332
+ encrypted += cipher.final('base64');
1333
+ const authTag = cipher.getAuthTag();
1334
+ return iv.toString('base64') + ':' + authTag.toString('base64') + ':' + encrypted;
1335
+ }
1336
+ // ────────────────────────────────────────────────────────────────
1337
+
1338
+ async function main() {
1339
+ console.log('🌱 Seeding database...');
1340
+
1341
+ // Create default admin user (next-auth is always a core dependency)
1342
+ const hashedPassword = await bcrypt.hash('admin123', 10);
1343
+ const admin = await prisma.user.upsert({
1344
+ where: { email: 'admin@example.com' },
1345
+ update: {},
1346
+ create: {
1347
+ email: 'admin@example.com',
1348
+ name: 'Admin User',
1349
+ password: hashedPassword,
1350
+ },
1351
+ });
1352
+
1353
+ console.log('✅ Admin user created: admin@example.com / admin123');
1338
1354
  ${features.includes('rbac')
1339
- ? `
1355
+ ? `
1340
1356
  ${sqlite
1341
- ? ` // Create default roles (SQLite-compatible: individual upserts)
1342
- for (const role of [
1343
- { name: 'admin', description: 'Full system access', permissions: JSON.stringify(['*']) },
1344
- { name: 'user', description: 'Regular user access', permissions: JSON.stringify(['chat:read', 'chat:write', 'profile:read']) },
1345
- { name: 'viewer', description: 'Read-only access', permissions: JSON.stringify(['chat:read']) },
1346
- ]) {
1347
- await prisma.role.upsert({
1348
- where: { name: role.name },
1349
- update: {},
1350
- create: role,
1351
- });
1357
+ ? ` // Create default roles (SQLite-compatible: individual upserts)
1358
+ for (const role of [
1359
+ { name: 'admin', description: 'Full system access', permissions: JSON.stringify(['*']) },
1360
+ { name: 'user', description: 'Regular user access', permissions: JSON.stringify(['chat:read', 'chat:write', 'profile:read']) },
1361
+ { name: 'viewer', description: 'Read-only access', permissions: JSON.stringify(['chat:read']) },
1362
+ ]) {
1363
+ await prisma.role.upsert({
1364
+ where: { name: role.name },
1365
+ update: {},
1366
+ create: role,
1367
+ });
1352
1368
  }`
1353
- : ` // Create default roles
1354
- await prisma.role.createMany({
1355
- data: [
1356
- {
1357
- name: 'admin',
1358
- description: 'Full system access',
1359
- permissions: ['*'],
1360
- },
1361
- {
1362
- name: 'user',
1363
- description: 'Regular user access',
1364
- permissions: ['chat:read', 'chat:write', 'profile:read'],
1365
- },
1366
- {
1367
- name: 'viewer',
1368
- description: 'Read-only access',
1369
- permissions: ['chat:read'],
1370
- },
1371
- ],
1372
- skipDuplicates: true,
1373
- });`}
1374
-
1375
- console.log('✅ Default roles created');
1376
-
1377
- // Assign admin role to admin user
1378
- const adminRole = await prisma.role.findUnique({ where: { name: 'admin' } });
1379
- if (adminRole) {
1380
- await prisma.userRole.upsert({
1381
- where: { userId_roleId: { userId: admin.id, roleId: adminRole.id } },
1382
- update: {},
1383
- create: { userId: admin.id, roleId: adminRole.id },
1384
- });
1385
- console.log('✅ Admin user assigned admin role');
1386
- }
1369
+ : ` // Create default roles
1370
+ await prisma.role.createMany({
1371
+ data: [
1372
+ {
1373
+ name: 'admin',
1374
+ description: 'Full system access',
1375
+ permissions: ['*'],
1376
+ },
1377
+ {
1378
+ name: 'user',
1379
+ description: 'Regular user access',
1380
+ permissions: ['chat:read', 'chat:write', 'profile:read'],
1381
+ },
1382
+ {
1383
+ name: 'viewer',
1384
+ description: 'Read-only access',
1385
+ permissions: ['chat:read'],
1386
+ },
1387
+ ],
1388
+ skipDuplicates: true,
1389
+ });`}
1390
+
1391
+ console.log('✅ Default roles created');
1392
+
1393
+ // Assign admin role to admin user
1394
+ const adminRole = await prisma.role.findUnique({ where: { name: 'admin' } });
1395
+ if (adminRole) {
1396
+ await prisma.userRole.upsert({
1397
+ where: { userId_roleId: { userId: admin.id, roleId: adminRole.id } },
1398
+ update: {},
1399
+ create: { userId: admin.id, roleId: adminRole.id },
1400
+ });
1401
+ console.log('✅ Admin user assigned admin role');
1402
+ }
1387
1403
  `
1388
- : ''}
1389
- // ── Seed Providers (if API keys are in .env) ────────────────────
1390
- const openaiKey = process.env.OPENAI_API_KEY;
1391
- const anthropicKey = process.env.ANTHROPIC_API_KEY;
1392
-
1393
- if (openaiKey) {
1394
- const provider = await prisma.provider.upsert({
1395
- where: { id: 'seed-openai' },
1396
- update: {},
1397
- create: {
1398
- id: 'seed-openai',
1399
- name: 'OpenAI',
1400
- type: 'openai',
1401
- description: 'OpenAI API (seeded from .env)',
1402
- baseUrl: 'https://api.openai.com/v1',
1403
- apiKey: encrypt(openaiKey),
1404
- config: ${sqlite ? `JSON.stringify({ defaultModel: 'gpt-4o-mini' })` : `{ defaultModel: 'gpt-4o-mini' }`},
1405
- status: 'active',
1406
- isDefault: true,
1407
- priority: 0,
1408
- createdBy: admin.id,
1409
- },
1410
- });
1411
-
1412
- // Create default OpenAI models
1404
+ : ''}
1405
+ // ── Seed Providers (if API keys are in .env) ────────────────────
1406
+ const openaiKey = process.env.OPENAI_API_KEY;
1407
+ const anthropicKey = process.env.ANTHROPIC_API_KEY;
1408
+
1409
+ if (openaiKey) {
1410
+ const provider = await prisma.provider.upsert({
1411
+ where: { id: 'seed-openai' },
1412
+ update: {},
1413
+ create: {
1414
+ id: 'seed-openai',
1415
+ name: 'OpenAI',
1416
+ type: 'openai',
1417
+ description: 'OpenAI API (seeded from .env)',
1418
+ baseUrl: 'https://api.openai.com/v1',
1419
+ apiKey: encrypt(openaiKey),
1420
+ config: ${sqlite ? `JSON.stringify({ defaultModel: 'gpt-4o-mini' })` : `{ defaultModel: 'gpt-4o-mini' }`},
1421
+ status: 'active',
1422
+ isDefault: true,
1423
+ priority: 0,
1424
+ createdBy: admin.id,
1425
+ },
1426
+ });
1427
+
1428
+ // Create default OpenAI models
1413
1429
  ${sqlite
1414
- ? ` for (const m of [
1415
- { providerId: provider.id, modelId: 'gpt-4o', name: 'GPT-4o', capabilities: JSON.stringify(['chat', 'vision']), contextWindow: 128000, inputCost: 2.5, outputCost: 10 },
1416
- { providerId: provider.id, modelId: 'gpt-4o-mini', name: 'GPT-4o Mini', capabilities: JSON.stringify(['chat']), contextWindow: 128000, inputCost: 0.15, outputCost: 0.6 },
1417
- { providerId: provider.id, modelId: 'gpt-4-turbo', name: 'GPT-4 Turbo', capabilities: JSON.stringify(['chat', 'vision']), contextWindow: 128000, inputCost: 10, outputCost: 30 },
1418
- { providerId: provider.id, modelId: 'text-embedding-3-small', name: 'Embedding 3 Small', capabilities: JSON.stringify(['embedding']), contextWindow: 8191, inputCost: 0.02, outputCost: 0 },
1419
- { providerId: provider.id, modelId: 'text-embedding-3-large', name: 'Embedding 3 Large', capabilities: JSON.stringify(['embedding']), contextWindow: 8191, inputCost: 0.13, outputCost: 0 },
1420
- ]) {
1421
- try { await prisma.model.create({ data: m }); } catch { /* skip duplicate */ }
1430
+ ? ` for (const m of [
1431
+ { providerId: provider.id, modelId: 'gpt-4o', name: 'GPT-4o', capabilities: JSON.stringify(['chat', 'vision']), contextWindow: 128000, inputCost: 2.5, outputCost: 10 },
1432
+ { providerId: provider.id, modelId: 'gpt-4o-mini', name: 'GPT-4o Mini', capabilities: JSON.stringify(['chat']), contextWindow: 128000, inputCost: 0.15, outputCost: 0.6 },
1433
+ { providerId: provider.id, modelId: 'gpt-4-turbo', name: 'GPT-4 Turbo', capabilities: JSON.stringify(['chat', 'vision']), contextWindow: 128000, inputCost: 10, outputCost: 30 },
1434
+ { providerId: provider.id, modelId: 'text-embedding-3-small', name: 'Embedding 3 Small', capabilities: JSON.stringify(['embedding']), contextWindow: 8191, inputCost: 0.02, outputCost: 0 },
1435
+ { providerId: provider.id, modelId: 'text-embedding-3-large', name: 'Embedding 3 Large', capabilities: JSON.stringify(['embedding']), contextWindow: 8191, inputCost: 0.13, outputCost: 0 },
1436
+ ]) {
1437
+ try { await prisma.model.create({ data: m }); } catch { /* skip duplicate */ }
1422
1438
  }`
1423
- : ` await prisma.model.createMany({
1424
- data: [
1425
- { providerId: provider.id, modelId: 'gpt-4o', name: 'GPT-4o', capabilities: ['chat', 'vision'], contextWindow: 128000, inputCost: 2.5, outputCost: 10 },
1426
- { providerId: provider.id, modelId: 'gpt-4o-mini', name: 'GPT-4o Mini', capabilities: ['chat'], contextWindow: 128000, inputCost: 0.15, outputCost: 0.6 },
1427
- { providerId: provider.id, modelId: 'gpt-4-turbo', name: 'GPT-4 Turbo', capabilities: ['chat', 'vision'], contextWindow: 128000, inputCost: 10, outputCost: 30 },
1428
- { providerId: provider.id, modelId: 'text-embedding-3-small', name: 'Embedding 3 Small', capabilities: ['embedding'], contextWindow: 8191, inputCost: 0.02, outputCost: 0 },
1429
- { providerId: provider.id, modelId: 'text-embedding-3-large', name: 'Embedding 3 Large', capabilities: ['embedding'], contextWindow: 8191, inputCost: 0.13, outputCost: 0 },
1430
- ],
1431
- skipDuplicates: true,
1432
- });`}
1433
-
1434
- console.log('✅ OpenAI provider seeded with models');
1435
- }
1436
-
1437
- if (anthropicKey) {
1438
- const provider = await prisma.provider.upsert({
1439
- where: { id: 'seed-anthropic' },
1440
- update: {},
1441
- create: {
1442
- id: 'seed-anthropic',
1443
- name: 'Anthropic',
1444
- type: 'anthropic',
1445
- description: 'Anthropic Claude API (seeded from .env)',
1446
- baseUrl: 'https://api.anthropic.com/v1',
1447
- apiKey: encrypt(anthropicKey),
1448
- config: ${sqlite ? `JSON.stringify({ defaultModel: 'claude-sonnet-4-20250514' })` : `{ defaultModel: 'claude-sonnet-4-20250514' }`},
1449
- status: 'active',
1450
- isDefault: false,
1451
- priority: 1,
1452
- createdBy: admin.id,
1453
- },
1454
- });
1455
-
1439
+ : ` await prisma.model.createMany({
1440
+ data: [
1441
+ { providerId: provider.id, modelId: 'gpt-4o', name: 'GPT-4o', capabilities: ['chat', 'vision'], contextWindow: 128000, inputCost: 2.5, outputCost: 10 },
1442
+ { providerId: provider.id, modelId: 'gpt-4o-mini', name: 'GPT-4o Mini', capabilities: ['chat'], contextWindow: 128000, inputCost: 0.15, outputCost: 0.6 },
1443
+ { providerId: provider.id, modelId: 'gpt-4-turbo', name: 'GPT-4 Turbo', capabilities: ['chat', 'vision'], contextWindow: 128000, inputCost: 10, outputCost: 30 },
1444
+ { providerId: provider.id, modelId: 'text-embedding-3-small', name: 'Embedding 3 Small', capabilities: ['embedding'], contextWindow: 8191, inputCost: 0.02, outputCost: 0 },
1445
+ { providerId: provider.id, modelId: 'text-embedding-3-large', name: 'Embedding 3 Large', capabilities: ['embedding'], contextWindow: 8191, inputCost: 0.13, outputCost: 0 },
1446
+ ],
1447
+ skipDuplicates: true,
1448
+ });`}
1449
+
1450
+ console.log('✅ OpenAI provider seeded with models');
1451
+ }
1452
+
1453
+ if (anthropicKey) {
1454
+ const provider = await prisma.provider.upsert({
1455
+ where: { id: 'seed-anthropic' },
1456
+ update: {},
1457
+ create: {
1458
+ id: 'seed-anthropic',
1459
+ name: 'Anthropic',
1460
+ type: 'anthropic',
1461
+ description: 'Anthropic Claude API (seeded from .env)',
1462
+ baseUrl: 'https://api.anthropic.com/v1',
1463
+ apiKey: encrypt(anthropicKey),
1464
+ config: ${sqlite ? `JSON.stringify({ defaultModel: 'claude-sonnet-4-20250514' })` : `{ defaultModel: 'claude-sonnet-4-20250514' }`},
1465
+ status: 'active',
1466
+ isDefault: false,
1467
+ priority: 1,
1468
+ createdBy: admin.id,
1469
+ },
1470
+ });
1471
+
1456
1472
  ${sqlite
1457
- ? ` for (const m of [
1458
- { providerId: provider.id, modelId: 'claude-sonnet-4-20250514', name: 'Claude Sonnet 4', capabilities: JSON.stringify(['chat', 'vision']), contextWindow: 200000, inputCost: 3, outputCost: 15 },
1459
- { providerId: provider.id, modelId: 'claude-3-5-sonnet-20241022', name: 'Claude 3.5 Sonnet', capabilities: JSON.stringify(['chat', 'vision']), contextWindow: 200000, inputCost: 3, outputCost: 15 },
1460
- { providerId: provider.id, modelId: 'claude-3-haiku-20240307', name: 'Claude 3 Haiku', capabilities: JSON.stringify(['chat']), contextWindow: 200000, inputCost: 0.25, outputCost: 1.25 },
1461
- ]) {
1462
- try { await prisma.model.create({ data: m }); } catch { /* skip duplicate */ }
1473
+ ? ` for (const m of [
1474
+ { providerId: provider.id, modelId: 'claude-sonnet-4-20250514', name: 'Claude Sonnet 4', capabilities: JSON.stringify(['chat', 'vision']), contextWindow: 200000, inputCost: 3, outputCost: 15 },
1475
+ { providerId: provider.id, modelId: 'claude-3-5-sonnet-20241022', name: 'Claude 3.5 Sonnet', capabilities: JSON.stringify(['chat', 'vision']), contextWindow: 200000, inputCost: 3, outputCost: 15 },
1476
+ { providerId: provider.id, modelId: 'claude-3-haiku-20240307', name: 'Claude 3 Haiku', capabilities: JSON.stringify(['chat']), contextWindow: 200000, inputCost: 0.25, outputCost: 1.25 },
1477
+ ]) {
1478
+ try { await prisma.model.create({ data: m }); } catch { /* skip duplicate */ }
1463
1479
  }`
1464
- : ` await prisma.model.createMany({
1465
- data: [
1466
- { providerId: provider.id, modelId: 'claude-sonnet-4-20250514', name: 'Claude Sonnet 4', capabilities: ['chat', 'vision'], contextWindow: 200000, inputCost: 3, outputCost: 15 },
1467
- { providerId: provider.id, modelId: 'claude-3-5-sonnet-20241022', name: 'Claude 3.5 Sonnet', capabilities: ['chat', 'vision'], contextWindow: 200000, inputCost: 3, outputCost: 15 },
1468
- { providerId: provider.id, modelId: 'claude-3-haiku-20240307', name: 'Claude 3 Haiku', capabilities: ['chat'], contextWindow: 200000, inputCost: 0.25, outputCost: 1.25 },
1469
- ],
1470
- skipDuplicates: true,
1471
- });`}
1472
-
1473
- console.log('✅ Anthropic provider seeded with models');
1474
- }
1475
-
1476
- if (!openaiKey && !anthropicKey) {
1477
- console.log('ℹ️ No API keys in .env — skip provider seeding. Add via Provider Management UI.');
1478
- }
1479
-
1480
- console.log('🎉 Seeding completed!');
1481
- }
1482
-
1483
- main()
1484
- .catch((e) => {
1485
- console.error('❌ Seeding failed:', e);
1486
- process.exit(1);
1487
- })
1488
- .finally(async () => {
1489
- await prisma.$disconnect();
1490
- });
1480
+ : ` await prisma.model.createMany({
1481
+ data: [
1482
+ { providerId: provider.id, modelId: 'claude-sonnet-4-20250514', name: 'Claude Sonnet 4', capabilities: ['chat', 'vision'], contextWindow: 200000, inputCost: 3, outputCost: 15 },
1483
+ { providerId: provider.id, modelId: 'claude-3-5-sonnet-20241022', name: 'Claude 3.5 Sonnet', capabilities: ['chat', 'vision'], contextWindow: 200000, inputCost: 3, outputCost: 15 },
1484
+ { providerId: provider.id, modelId: 'claude-3-haiku-20240307', name: 'Claude 3 Haiku', capabilities: ['chat'], contextWindow: 200000, inputCost: 0.25, outputCost: 1.25 },
1485
+ ],
1486
+ skipDuplicates: true,
1487
+ });`}
1488
+
1489
+ console.log('✅ Anthropic provider seeded with models');
1490
+ }
1491
+
1492
+ if (!openaiKey && !anthropicKey) {
1493
+ console.log('ℹ️ No API keys in .env — skip provider seeding. Add via Provider Management UI.');
1494
+ }
1495
+
1496
+ console.log('🎉 Seeding completed!');
1497
+ }
1498
+
1499
+ main()
1500
+ .catch((e) => {
1501
+ console.error('❌ Seeding failed:', e);
1502
+ process.exit(1);
1503
+ })
1504
+ .finally(async () => {
1505
+ await prisma.$disconnect();
1506
+ });
1491
1507
  `;
1492
1508
  const seedDest = path_1.default.join(targetDir, 'prisma/seed.ts');
1493
1509
  await fs_extra_1.default.ensureDir(path_1.default.dirname(seedDest));
@@ -1500,208 +1516,208 @@ async function createDockerCompose(targetDir) {
1500
1516
  async function createInstallScripts(targetDir, features, sqlite) {
1501
1517
  // Windows install.bat
1502
1518
  const installBat = sqlite
1503
- ? `@echo off
1504
- REM Installation Script for ChimerAI Project (SQLite)
1505
- echo.
1506
- echo ================================================
1507
- echo ChimerAI Project Setup (SQLite - No Docker needed)
1508
- echo ================================================
1509
- echo.
1510
-
1511
- REM Check Node.js
1512
- where node >nul 2>nul
1513
- if %ERRORLEVEL% NEQ 0 (
1514
- echo [ERROR] Node.js is not installed
1515
- pause
1516
- exit /b 1
1517
- )
1518
-
1519
- echo [1/3] Installing dependencies...
1520
- call npm install
1521
- if %ERRORLEVEL% NEQ 0 (
1522
- echo [ERROR] Failed to install dependencies
1523
- pause
1524
- exit /b 1
1525
- )
1526
-
1527
- echo [2/3] Setting up database...
1528
- call npm run db:push
1529
- if %ERRORLEVEL% NEQ 0 (
1530
- echo [ERROR] Failed to setup database
1531
- pause
1532
- exit /b 1
1533
- )
1534
-
1535
- echo [3/3] Seeding database...
1536
- call npm run db:seed
1537
- if %ERRORLEVEL% NEQ 0 (
1538
- echo [WARNING] Seeding failed, but you can continue
1539
- )
1540
-
1541
- echo.
1542
- echo ================================================
1543
- echo Setup completed successfully!
1544
- echo ================================================
1545
- echo.
1546
- echo Next steps:
1547
- echo npm run dev
1548
- echo Open: http://localhost:3001
1549
- echo.
1550
- ${features.includes('auth') ? 'echo Login with:\necho Email: admin@example.com\necho Password: admin123\necho.' : ''}
1551
- pause
1519
+ ? `@echo off
1520
+ REM Installation Script for ChimerAI Project (SQLite)
1521
+ echo.
1522
+ echo ================================================
1523
+ echo ChimerAI Project Setup (SQLite - No Docker needed)
1524
+ echo ================================================
1525
+ echo.
1526
+
1527
+ REM Check Node.js
1528
+ where node >nul 2>nul
1529
+ if %ERRORLEVEL% NEQ 0 (
1530
+ echo [ERROR] Node.js is not installed
1531
+ pause
1532
+ exit /b 1
1533
+ )
1534
+
1535
+ echo [1/3] Installing dependencies...
1536
+ call npm install
1537
+ if %ERRORLEVEL% NEQ 0 (
1538
+ echo [ERROR] Failed to install dependencies
1539
+ pause
1540
+ exit /b 1
1541
+ )
1542
+
1543
+ echo [2/3] Setting up database...
1544
+ call npm run db:push
1545
+ if %ERRORLEVEL% NEQ 0 (
1546
+ echo [ERROR] Failed to setup database
1547
+ pause
1548
+ exit /b 1
1549
+ )
1550
+
1551
+ echo [3/3] Seeding database...
1552
+ call npm run db:seed
1553
+ if %ERRORLEVEL% NEQ 0 (
1554
+ echo [WARNING] Seeding failed, but you can continue
1555
+ )
1556
+
1557
+ echo.
1558
+ echo ================================================
1559
+ echo Setup completed successfully!
1560
+ echo ================================================
1561
+ echo.
1562
+ echo Next steps:
1563
+ echo npm run dev
1564
+ echo Open: http://localhost:3001
1565
+ echo.
1566
+ ${features.includes('auth') ? 'echo Login with:\necho Email: admin@example.com\necho Password: admin123\necho.' : ''}
1567
+ pause
1552
1568
  `
1553
- : `@echo off
1554
- REM Installation Script for ChimerAI Project
1555
- echo.
1556
- echo ================================================
1557
- echo ChimerAI Project Setup
1558
- echo ================================================
1559
- echo.
1560
-
1561
- REM Check Node.js
1562
- where node >nul 2>nul
1563
- if %ERRORLEVEL% NEQ 0 (
1564
- echo [ERROR] Node.js is not installed
1565
- pause
1566
- exit /b 1
1567
- )
1568
-
1569
- REM Check Docker
1570
- where docker >nul 2>nul
1571
- if %ERRORLEVEL% NEQ 0 (
1572
- echo [ERROR] Docker is not installed
1573
- pause
1574
- exit /b 1
1575
- )
1576
-
1577
- echo [1/5] Installing dependencies...
1578
- call npm install
1579
- if %ERRORLEVEL% NEQ 0 (
1580
- echo [ERROR] Failed to install dependencies
1581
- pause
1582
- exit /b 1
1583
- )
1584
-
1585
- echo [2/5] Starting Docker containers...
1586
- call docker-compose up -d
1587
- if %ERRORLEVEL% NEQ 0 (
1588
- echo [ERROR] Failed to start Docker
1589
- pause
1590
- exit /b 1
1591
- )
1592
-
1593
- echo [3/5] Waiting for database...
1594
- timeout /t 5 /nobreak >nul
1595
-
1596
- echo [4/5] Setting up database...
1597
- call npm run db:push
1598
- if %ERRORLEVEL% NEQ 0 (
1599
- echo [ERROR] Failed to setup database
1600
- pause
1601
- exit /b 1
1602
- )
1603
-
1604
- echo [5/5] Seeding database...
1605
- call npm run db:seed
1606
- if %ERRORLEVEL% NEQ 0 (
1607
- echo [WARNING] Seeding failed, but you can continue
1608
- )
1609
-
1610
- echo.
1611
- echo ================================================
1612
- echo Setup completed successfully!
1613
- echo ================================================
1614
- echo.
1615
- echo Next steps:
1616
- echo npm run dev
1617
- echo Open: http://localhost:3001
1618
- echo.
1619
- ${features.includes('auth') ? 'echo Login with:\necho Email: admin@example.com\necho Password: admin123\necho.' : ''}
1620
- pause
1569
+ : `@echo off
1570
+ REM Installation Script for ChimerAI Project
1571
+ echo.
1572
+ echo ================================================
1573
+ echo ChimerAI Project Setup
1574
+ echo ================================================
1575
+ echo.
1576
+
1577
+ REM Check Node.js
1578
+ where node >nul 2>nul
1579
+ if %ERRORLEVEL% NEQ 0 (
1580
+ echo [ERROR] Node.js is not installed
1581
+ pause
1582
+ exit /b 1
1583
+ )
1584
+
1585
+ REM Check Docker
1586
+ where docker >nul 2>nul
1587
+ if %ERRORLEVEL% NEQ 0 (
1588
+ echo [ERROR] Docker is not installed
1589
+ pause
1590
+ exit /b 1
1591
+ )
1592
+
1593
+ echo [1/5] Installing dependencies...
1594
+ call npm install
1595
+ if %ERRORLEVEL% NEQ 0 (
1596
+ echo [ERROR] Failed to install dependencies
1597
+ pause
1598
+ exit /b 1
1599
+ )
1600
+
1601
+ echo [2/5] Starting Docker containers...
1602
+ call docker-compose up -d
1603
+ if %ERRORLEVEL% NEQ 0 (
1604
+ echo [ERROR] Failed to start Docker
1605
+ pause
1606
+ exit /b 1
1607
+ )
1608
+
1609
+ echo [3/5] Waiting for database...
1610
+ timeout /t 5 /nobreak >nul
1611
+
1612
+ echo [4/5] Setting up database...
1613
+ call npm run db:push
1614
+ if %ERRORLEVEL% NEQ 0 (
1615
+ echo [ERROR] Failed to setup database
1616
+ pause
1617
+ exit /b 1
1618
+ )
1619
+
1620
+ echo [5/5] Seeding database...
1621
+ call npm run db:seed
1622
+ if %ERRORLEVEL% NEQ 0 (
1623
+ echo [WARNING] Seeding failed, but you can continue
1624
+ )
1625
+
1626
+ echo.
1627
+ echo ================================================
1628
+ echo Setup completed successfully!
1629
+ echo ================================================
1630
+ echo.
1631
+ echo Next steps:
1632
+ echo npm run dev
1633
+ echo Open: http://localhost:3001
1634
+ echo.
1635
+ ${features.includes('auth') ? 'echo Login with:\necho Email: admin@example.com\necho Password: admin123\necho.' : ''}
1636
+ pause
1621
1637
  `;
1622
1638
  // Linux/macOS install.sh
1623
1639
  const installSh = sqlite
1624
- ? `#!/bin/bash
1625
- set -e
1626
-
1627
- echo ""
1628
- echo "================================================"
1629
- echo " ChimerAI Project Setup (SQLite - No Docker needed)"
1630
- echo "================================================"
1631
- echo ""
1632
-
1633
- # Check Node.js
1634
- if ! command -v node &> /dev/null; then
1635
- echo "[ERROR] Node.js is not installed"
1636
- exit 1
1637
- fi
1638
-
1639
- echo "[1/3] Installing dependencies..."
1640
- npm install
1641
-
1642
- echo "[2/3] Setting up database..."
1643
- npm run db:push
1644
-
1645
- echo "[3/3] Seeding database..."
1646
- npm run db:seed || echo "[WARNING] Seeding failed, but you can continue"
1647
-
1648
- echo ""
1649
- echo "================================================"
1650
- echo " Setup completed successfully!"
1651
- echo "================================================"
1652
- echo ""
1653
- echo "Next steps:"
1654
- echo " npm run dev"
1655
- echo " Open: http://localhost:3001"
1656
- echo ""
1657
- ${features.includes('auth') ? 'echo "Login with:"\necho " Email: admin@example.com"\necho " Password: admin123"\necho ""' : ''}
1640
+ ? `#!/bin/bash
1641
+ set -e
1642
+
1643
+ echo ""
1644
+ echo "================================================"
1645
+ echo " ChimerAI Project Setup (SQLite - No Docker needed)"
1646
+ echo "================================================"
1647
+ echo ""
1648
+
1649
+ # Check Node.js
1650
+ if ! command -v node &> /dev/null; then
1651
+ echo "[ERROR] Node.js is not installed"
1652
+ exit 1
1653
+ fi
1654
+
1655
+ echo "[1/3] Installing dependencies..."
1656
+ npm install
1657
+
1658
+ echo "[2/3] Setting up database..."
1659
+ npm run db:push
1660
+
1661
+ echo "[3/3] Seeding database..."
1662
+ npm run db:seed || echo "[WARNING] Seeding failed, but you can continue"
1663
+
1664
+ echo ""
1665
+ echo "================================================"
1666
+ echo " Setup completed successfully!"
1667
+ echo "================================================"
1668
+ echo ""
1669
+ echo "Next steps:"
1670
+ echo " npm run dev"
1671
+ echo " Open: http://localhost:3001"
1672
+ echo ""
1673
+ ${features.includes('auth') ? 'echo "Login with:"\necho " Email: admin@example.com"\necho " Password: admin123"\necho ""' : ''}
1658
1674
  `
1659
- : `#!/bin/bash
1660
- set -e
1661
-
1662
- echo ""
1663
- echo "================================================"
1664
- echo " ChimerAI Project Setup"
1665
- echo "================================================"
1666
- echo ""
1667
-
1668
- # Check Node.js
1669
- if ! command -v node &> /dev/null; then
1670
- echo "[ERROR] Node.js is not installed"
1671
- exit 1
1672
- fi
1673
-
1674
- # Check Docker
1675
- if ! command -v docker &> /dev/null; then
1676
- echo "[ERROR] Docker is not installed"
1677
- exit 1
1678
- fi
1679
-
1680
- echo "[1/5] Installing dependencies..."
1681
- npm install
1682
-
1683
- echo "[2/5] Starting Docker containers..."
1684
- docker-compose up -d
1685
-
1686
- echo "[3/5] Waiting for database..."
1687
- sleep 5
1688
-
1689
- echo "[4/5] Setting up database..."
1690
- npm run db:push
1691
-
1692
- echo "[5/5] Seeding database..."
1693
- npm run db:seed || echo "[WARNING] Seeding failed, but you can continue"
1694
-
1695
- echo ""
1696
- echo "================================================"
1697
- echo " Setup completed successfully!"
1698
- echo "================================================"
1699
- echo ""
1700
- echo "Next steps:"
1701
- echo " npm run dev"
1702
- echo " Open: http://localhost:3001"
1703
- echo ""
1704
- ${features.includes('auth') ? 'echo "Login with:"\necho " Email: admin@example.com"\necho " Password: admin123"\necho ""' : ''}
1675
+ : `#!/bin/bash
1676
+ set -e
1677
+
1678
+ echo ""
1679
+ echo "================================================"
1680
+ echo " ChimerAI Project Setup"
1681
+ echo "================================================"
1682
+ echo ""
1683
+
1684
+ # Check Node.js
1685
+ if ! command -v node &> /dev/null; then
1686
+ echo "[ERROR] Node.js is not installed"
1687
+ exit 1
1688
+ fi
1689
+
1690
+ # Check Docker
1691
+ if ! command -v docker &> /dev/null; then
1692
+ echo "[ERROR] Docker is not installed"
1693
+ exit 1
1694
+ fi
1695
+
1696
+ echo "[1/5] Installing dependencies..."
1697
+ npm install
1698
+
1699
+ echo "[2/5] Starting Docker containers..."
1700
+ docker-compose up -d
1701
+
1702
+ echo "[3/5] Waiting for database..."
1703
+ sleep 5
1704
+
1705
+ echo "[4/5] Setting up database..."
1706
+ npm run db:push
1707
+
1708
+ echo "[5/5] Seeding database..."
1709
+ npm run db:seed || echo "[WARNING] Seeding failed, but you can continue"
1710
+
1711
+ echo ""
1712
+ echo "================================================"
1713
+ echo " Setup completed successfully!"
1714
+ echo "================================================"
1715
+ echo ""
1716
+ echo "Next steps:"
1717
+ echo " npm run dev"
1718
+ echo " Open: http://localhost:3001"
1719
+ echo ""
1720
+ ${features.includes('auth') ? 'echo "Login with:"\necho " Email: admin@example.com"\necho " Password: admin123"\necho ""' : ''}
1705
1721
  `;
1706
1722
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'install.bat'), installBat);
1707
1723
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'install.sh'), installSh);
@@ -1714,12 +1730,12 @@ ${features.includes('auth') ? 'echo "Login with:"\necho " Email: admin@example.
1714
1730
  }
1715
1731
  }
1716
1732
  async function createReadme(targetDir, projectName, features) {
1717
- let readme = `# ${projectName}
1718
-
1719
- Built with ChimerAI Kickstart
1720
-
1721
- ## Features
1722
-
1733
+ let readme = `# ${projectName}
1734
+
1735
+ Built with ChimerAI Kickstart
1736
+
1737
+ ## Features
1738
+
1723
1739
  `;
1724
1740
  const featureDescriptions = {
1725
1741
  auth: '- 🔐 Authentication with NextAuth',
@@ -1737,95 +1753,95 @@ Built with ChimerAI Kickstart
1737
1753
  readme += featureDescriptions[f] + '\n';
1738
1754
  }
1739
1755
  });
1740
- readme += `
1741
-
1742
- ## Getting Started
1743
-
1744
- ### Prerequisites
1745
-
1746
- - Node.js 20+
1747
- - Docker Desktop
1748
- - npm
1749
-
1750
- ### Quick Start (Recommended)
1751
-
1752
- **Windows:**
1753
- \`\`\`bash
1754
- install.bat
1755
- \`\`\`
1756
-
1757
- **Linux/macOS:**
1758
- \`\`\`bash
1759
- ./install.sh
1760
- \`\`\`
1761
-
1762
- The install script will automatically:
1763
- - Install dependencies
1764
- - Start Docker containers
1765
- - Setup and seed the database
1766
- - Show you the next steps
1767
-
1768
- ### Manual Installation
1769
-
1770
- If you prefer to run commands manually:
1771
-
1772
- \`\`\`bash
1773
- # Install dependencies
1774
- npm install
1775
-
1776
- # Start Docker services
1777
- docker-compose up -d
1778
-
1779
- # Setup database
1780
- npm run db:push
1781
- npm run db:seed
1782
-
1783
- # Start development server
1784
- npm run dev
1785
- \`\`\`
1786
-
1787
- Open [http://localhost:3001](http://localhost:3001) in your browser.
1788
-
1789
- ${features.includes('auth') ? `### Default Admin Credentials\n\n- Email: admin@example.com\n- Password: admin123\n\n⚠️ Change these in production!` : ''}
1790
-
1791
- ## Available Scripts
1792
-
1793
- - \`pnpm dev\` - Start development server
1794
- - \`pnpm build\` - Build for production
1795
- - \`pnpm start\` - Start production server
1796
- - \`pnpm lint\` - Run linter
1797
- - \`pnpm db:push\` - Push database schema
1798
- - \`pnpm db:seed\` - Seed database
1799
- - \`pnpm db:studio\` - Open Prisma Studio
1800
-
1801
- ## Tech Stack
1802
-
1803
- - **Framework**: Next.js 15
1804
- - **Language**: TypeScript
1805
- - **Database**: PostgreSQL + Prisma
1806
- - **Auth**: NextAuth.js
1807
- - **Styling**: Tailwind CSS
1808
- - **UI Components**: Radix UI
1809
-
1810
- ## Project Structure
1811
-
1812
- \`\`\`
1813
- ├── app/ # Next.js app directory
1814
- ├── components/ # React components
1815
- ├── lib/ # Utility functions
1816
- ├── prisma/ # Database schema
1817
- └── public/ # Static assets
1818
- \`\`\`
1819
-
1820
- ## Learn More
1821
-
1822
- - [ChimerAI Documentation](https://chimerai.dev)
1823
- - [Next.js Documentation](https://nextjs.org/docs)
1824
- - [Prisma Documentation](https://www.prisma.io/docs)
1825
-
1826
- ## License
1827
-
1828
- Commercial License - See LICENSE file
1756
+ readme += `
1757
+
1758
+ ## Getting Started
1759
+
1760
+ ### Prerequisites
1761
+
1762
+ - Node.js 20+
1763
+ - Docker Desktop
1764
+ - npm
1765
+
1766
+ ### Quick Start (Recommended)
1767
+
1768
+ **Windows:**
1769
+ \`\`\`bash
1770
+ install.bat
1771
+ \`\`\`
1772
+
1773
+ **Linux/macOS:**
1774
+ \`\`\`bash
1775
+ ./install.sh
1776
+ \`\`\`
1777
+
1778
+ The install script will automatically:
1779
+ - Install dependencies
1780
+ - Start Docker containers
1781
+ - Setup and seed the database
1782
+ - Show you the next steps
1783
+
1784
+ ### Manual Installation
1785
+
1786
+ If you prefer to run commands manually:
1787
+
1788
+ \`\`\`bash
1789
+ # Install dependencies
1790
+ npm install
1791
+
1792
+ # Start Docker services
1793
+ docker-compose up -d
1794
+
1795
+ # Setup database
1796
+ npm run db:push
1797
+ npm run db:seed
1798
+
1799
+ # Start development server
1800
+ npm run dev
1801
+ \`\`\`
1802
+
1803
+ Open [http://localhost:3001](http://localhost:3001) in your browser.
1804
+
1805
+ ${features.includes('auth') ? `### Default Admin Credentials\n\n- Email: admin@example.com\n- Password: admin123\n\n⚠️ Change these in production!` : ''}
1806
+
1807
+ ## Available Scripts
1808
+
1809
+ - \`pnpm dev\` - Start development server
1810
+ - \`pnpm build\` - Build for production
1811
+ - \`pnpm start\` - Start production server
1812
+ - \`pnpm lint\` - Run linter
1813
+ - \`pnpm db:push\` - Push database schema
1814
+ - \`pnpm db:seed\` - Seed database
1815
+ - \`pnpm db:studio\` - Open Prisma Studio
1816
+
1817
+ ## Tech Stack
1818
+
1819
+ - **Framework**: Next.js 15
1820
+ - **Language**: TypeScript
1821
+ - **Database**: PostgreSQL + Prisma
1822
+ - **Auth**: NextAuth.js
1823
+ - **Styling**: Tailwind CSS
1824
+ - **UI Components**: Radix UI
1825
+
1826
+ ## Project Structure
1827
+
1828
+ \`\`\`
1829
+ ├── app/ # Next.js app directory
1830
+ ├── components/ # React components
1831
+ ├── lib/ # Utility functions
1832
+ ├── prisma/ # Database schema
1833
+ └── public/ # Static assets
1834
+ \`\`\`
1835
+
1836
+ ## Learn More
1837
+
1838
+ - [ChimerAI Documentation](https://chimerai.dev)
1839
+ - [Next.js Documentation](https://nextjs.org/docs)
1840
+ - [Prisma Documentation](https://www.prisma.io/docs)
1841
+
1842
+ ## License
1843
+
1844
+ Commercial License - See LICENSE file
1829
1845
  `;
1830
1846
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'README.md'), readme);
1831
1847
  // Create docs directory with inline documentation
@@ -1833,28 +1849,28 @@ Commercial License - See LICENSE file
1833
1849
  const docsDir = path_1.default.join(targetDir, 'docs');
1834
1850
  await fs_extra_1.default.ensureDir(docsDir);
1835
1851
  // Generate inline quickstart guide
1836
- const quickstart = `# Quick Start
1837
-
1838
- ## Prerequisites
1839
- - Node.js 20+
1840
- - Docker Desktop
1841
- - pnpm (recommended)
1842
-
1843
- ## Setup
1844
- 1. \`pnpm install\`
1845
- 2. \`docker-compose up -d\`
1846
- 3. \`pnpm db:push\`
1847
- 4. \`pnpm db:seed\`
1848
- 5. \`pnpm dev\`
1849
-
1850
- ## Default Login
1851
- ${features.includes('auth') ? '- Email: admin@example.com\n- Password: admin123' : '- No auth configured. Run: `chimerai add auth`'}
1852
-
1853
- ## CLI Commands
1854
- - \`chimerai add <component>\` — Add features
1855
- - \`chimerai setup <service>\` — Configure integrations
1856
- - \`chimerai doctor\` — Health check
1857
- - \`chimerai update --diff\` — Check for template updates
1852
+ const quickstart = `# Quick Start
1853
+
1854
+ ## Prerequisites
1855
+ - Node.js 20+
1856
+ - Docker Desktop
1857
+ - pnpm (recommended)
1858
+
1859
+ ## Setup
1860
+ 1. \`pnpm install\`
1861
+ 2. \`docker-compose up -d\`
1862
+ 3. \`pnpm db:push\`
1863
+ 4. \`pnpm db:seed\`
1864
+ 5. \`pnpm dev\`
1865
+
1866
+ ## Default Login
1867
+ ${features.includes('auth') ? '- Email: admin@example.com\n- Password: admin123' : '- No auth configured. Run: `chimerai add auth`'}
1868
+
1869
+ ## CLI Commands
1870
+ - \`chimerai add <component>\` — Add features
1871
+ - \`chimerai setup <service>\` — Configure integrations
1872
+ - \`chimerai doctor\` — Health check
1873
+ - \`chimerai update --diff\` — Check for template updates
1858
1874
  `;
1859
1875
  await fs_extra_1.default.writeFile(path_1.default.join(docsDir, 'QUICKSTART.md'), quickstart);
1860
1876
  console.log(chalk_1.default.green(' ✓ docs/QUICKSTART.md'));