@chimerai/cli 0.2.95 → 0.2.97

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