@chimerai/cli 0.2.90 → 0.2.92

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.
@@ -320,10 +320,10 @@ async function createBaseStructure(targetDir, projectName, features) {
320
320
  'styles',
321
321
  ];
322
322
  // Add feature-specific directories
323
- if (features.includes('admin')) {
323
+ if (features.includes('admin-dashboard')) {
324
324
  dirs.push('app/admin', 'app/admin/settings', 'app/admin/logs');
325
325
  }
326
- if (features.includes('chat')) {
326
+ if (features.includes('ai-chat')) {
327
327
  dirs.push('app/(app)', 'app/(app)/chat', 'components/chat', 'app/api/conversations/[id]');
328
328
  }
329
329
  if (features.includes('auth')) {
@@ -335,7 +335,7 @@ async function createBaseStructure(targetDir, projectName, features) {
335
335
  if (features.includes('model-providers')) {
336
336
  dirs.push('app/dashboard/providers');
337
337
  }
338
- if (features.includes('prompts')) {
338
+ if (features.includes('prompt-management')) {
339
339
  dirs.push('app/dashboard/prompts', 'app/api/prompts', 'app/api/prompts/[id]');
340
340
  }
341
341
  for (const dir of dirs) {
@@ -393,12 +393,12 @@ async function createPackageJson(targetDir, projectName, features) {
393
393
  if (features.includes('model-providers')) {
394
394
  // dependencies['@chimerai/model-providers'] = 'workspace:*'; // Not available standalone yet
395
395
  }
396
- if (features.includes('admin')) {
396
+ if (features.includes('admin-dashboard')) {
397
397
  // dependencies['@chimerai/admin-ui'] = 'workspace:*'; // Not available standalone yet
398
398
  }
399
- if (features.includes('chat')) {
400
- dependencies['react-markdown'] = '^9.0.0';
401
- dependencies['remark-gfm'] = '^4.0.0';
399
+ if (features.includes('ai-chat')) {
400
+ dependencies['react-markdown'] = '^10.1.0';
401
+ dependencies['remark-gfm'] = '^4.0.1';
402
402
  }
403
403
  if (features.includes('billing')) {
404
404
  // dependencies['@chimerai/billing'] = 'workspace:*'; // Not available standalone yet
@@ -453,322 +453,322 @@ async function createTsConfig(targetDir) {
453
453
  }
454
454
  async function createPrismaSchema(targetDir, features, sqlite) {
455
455
  const dbProvider = sqlite ? 'sqlite' : 'postgresql';
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
-
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
+
465
465
  `;
466
466
  // Add User model if auth is included
467
467
  if (features.includes('auth')) {
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[]
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[]
480
480
  `;
481
481
  if (features.includes('rbac')) {
482
- schemaContent += ` roles UserRole[]
482
+ schemaContent += ` roles UserRole[]
483
483
  `;
484
484
  }
485
485
  // Provider relations (ALWAYS included)
486
- schemaContent += ` providers Provider[] @relation("CreatedProviders")
487
- apiUsage ApiUsage[]
488
- apiKeys ApiKey[]
486
+ schemaContent += ` providers Provider[] @relation("CreatedProviders")
487
+ apiUsage ApiUsage[]
488
+ apiKeys ApiKey[]
489
489
  `;
490
490
  if (features.includes('rbac')) {
491
- schemaContent += ` modelAccess ModelAccess[]
492
- auditLogs AuditLog[]
491
+ schemaContent += ` modelAccess ModelAccess[]
492
+ auditLogs AuditLog[]
493
493
  `;
494
494
  }
495
- if (features.includes('chat')) {
496
- schemaContent += ` conversations Conversation[]
495
+ if (features.includes('ai-chat')) {
496
+ schemaContent += ` conversations Conversation[]
497
497
  `;
498
498
  }
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
-
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
+
553
553
  `;
554
554
  }
555
555
  // Add RBAC models
556
556
  if (features.includes('rbac')) {
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
-
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
+
578
578
  `;
579
579
  }
580
580
  // ── PROVIDER MODELS (ALWAYS included — core infrastructure) ──────────
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('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
-
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
+
666
666
  `;
667
667
  // Add ModelAccess when auth + rbac are enabled (for requireModelPermission)
668
668
  if (features.includes('auth') && features.includes('rbac')) {
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
-
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
+
701
701
  `;
702
702
  }
703
703
  // Add Prompt Templates
704
- if (features.includes('prompts')) {
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
-
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
+
727
727
  `;
728
728
  }
729
729
  // Add Conversation & Message models for chat feature
730
- if (features.includes('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
-
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
+
764
764
  `;
765
765
  }
766
766
  // SystemSetting is always included (used by admin settings + app-settings API)
767
- schemaContent += `model SystemSetting {
768
- key String @id
769
- value String @db.Text
770
- updatedAt DateTime @updatedAt
771
- }
767
+ schemaContent += `model SystemSetting {
768
+ key String @id
769
+ value String @db.Text
770
+ updatedAt DateTime @updatedAt
771
+ }
772
772
  `;
773
773
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'prisma/schema.prisma'), schemaContent);
774
774
  // Apply SQLite compatibility transform if needed
@@ -824,55 +824,55 @@ async function createEnvFiles(targetDir, features, sqlite) {
824
824
  // Generate a random NextAuth secret (32 bytes hex = 64 chars)
825
825
  const nextAuthSecret = Array.from({ length: 64 }, () => '0123456789abcdef'.charAt(Math.floor(Math.random() * 16))).join('');
826
826
  const dbUrl = sqlite ? 'file:./dev.db' : 'postgresql://postgres:postgres@localhost:5432/myapp_db';
827
- let envContent = `# Database${sqlite ? ' (SQLite — no Docker needed)' : ''}
828
- DATABASE_URL=${dbUrl}
829
-
827
+ let envContent = `# Database${sqlite ? ' (SQLite — no Docker needed)' : ''}
828
+ DATABASE_URL=${dbUrl}
829
+
830
830
  `;
831
831
  if (features.includes('auth')) {
832
- envContent += `# Authentication
833
- NEXTAUTH_SECRET=${nextAuthSecret}
834
- NEXTAUTH_URL=http://localhost:3000
835
-
832
+ envContent += `# Authentication
833
+ NEXTAUTH_SECRET=${nextAuthSecret}
834
+ NEXTAUTH_URL=http://localhost:3000
835
+
836
836
  `;
837
837
  }
838
838
  // ── Provider Infrastructure (ALWAYS included — core) ──────────
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
-
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
+
854
854
  `;
855
855
  if (features.includes('billing')) {
856
- envContent += `# Stripe
857
- STRIPE_PUBLISHABLE_KEY=
858
- STRIPE_SECRET_KEY=
859
- STRIPE_WEBHOOK_SECRET=
860
-
856
+ envContent += `# Stripe
857
+ STRIPE_PUBLISHABLE_KEY=
858
+ STRIPE_SECRET_KEY=
859
+ STRIPE_WEBHOOK_SECRET=
860
+
861
861
  `;
862
862
  }
863
863
  // Widget / External Integration
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
-
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
+
876
876
  `;
877
877
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, '.env'), envContent);
878
878
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, '.env.example'), envContent
@@ -1020,7 +1020,7 @@ async function copyFeatureFiles(targetDir, features) {
1020
1020
  const noAuthDashLayout = templates.generateDashboardLayoutNoAuth();
1021
1021
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'app/dashboard/layout.tsx'), noAuthDashLayout);
1022
1022
  }
1023
- if (features.includes('admin')) {
1023
+ if (features.includes('admin-dashboard')) {
1024
1024
  // Admin layout with session check + admin role guard
1025
1025
  const adminLayout = templates.generateAdminLayout();
1026
1026
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'app/admin'));
@@ -1075,136 +1075,136 @@ async function copyFeatureFiles(targetDir, features) {
1075
1075
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'lib'));
1076
1076
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'lib/use-app-name.ts'), useAppNameHook);
1077
1077
  // ── RBAC Permission utilities (required by admin API routes) ───
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
- }
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
+ }
1140
1140
  `;
1141
1141
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'lib/permissions.ts'), permissionsLib);
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
- }
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
+ }
1203
1203
  `;
1204
1204
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'lib/auth'));
1205
1205
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'lib/auth/require-permission.ts'), requirePermissionLib);
1206
1206
  }
1207
- if (features.includes('prompts')) {
1207
+ if (features.includes('prompt-management')) {
1208
1208
  const promptsPage = templates.generatePromptManagementPage();
1209
1209
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'app/dashboard/prompts'));
1210
1210
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'app/dashboard/prompts/page.tsx'), promptsPage);
@@ -1216,7 +1216,7 @@ async function getServerSessionWithPermissions() {
1216
1216
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'app/api/prompts/[id]'));
1217
1217
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'app/api/prompts/[id]/route.ts'), promptsIdRoute);
1218
1218
  }
1219
- if (features.includes('chat')) {
1219
+ if (features.includes('ai-chat')) {
1220
1220
  // Modular chat components (useChat hook + individual components)
1221
1221
  await fs_extra_1.default.ensureDir(path_1.default.join(targetDir, 'components/chat'));
1222
1222
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'components/chat/use-chat.ts'), templates.generateUseChatHook());
@@ -1295,215 +1295,215 @@ async function getServerSessionWithPermissions() {
1295
1295
  async function createSeedScript(targetDir, features, sqlite) {
1296
1296
  // Without auth there are no users/providers to seed — write a minimal placeholder
1297
1297
  if (!features.includes('auth')) {
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());
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());
1305
1305
  `;
1306
1306
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'prisma/seed.ts'), minimalSeed);
1307
1307
  return;
1308
1308
  }
1309
1309
  // Generate seed script with feature-specific configuration
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');
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');
1354
1354
  ${features.includes('rbac')
1355
- ? `
1355
+ ? `
1356
1356
  ${sqlite
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
- });
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
+ });
1368
1368
  }`
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
- }
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
+ }
1403
1403
  `
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
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
1429
1429
  ${sqlite
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 */ }
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 */ }
1438
1438
  }`
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
-
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
+
1472
1472
  ${sqlite
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 */ }
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 */ }
1479
1479
  }`
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
- });
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
+ });
1507
1507
  `;
1508
1508
  const seedDest = path_1.default.join(targetDir, 'prisma/seed.ts');
1509
1509
  await fs_extra_1.default.ensureDir(path_1.default.dirname(seedDest));
@@ -1516,208 +1516,208 @@ async function createDockerCompose(targetDir) {
1516
1516
  async function createInstallScripts(targetDir, features, sqlite) {
1517
1517
  // Windows install.bat
1518
1518
  const installBat = sqlite
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
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
1568
1568
  `
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
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
1637
1637
  `;
1638
1638
  // Linux/macOS install.sh
1639
1639
  const installSh = sqlite
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 ""' : ''}
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 ""' : ''}
1674
1674
  `
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 ""' : ''}
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 ""' : ''}
1721
1721
  `;
1722
1722
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'install.bat'), installBat);
1723
1723
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'install.sh'), installSh);
@@ -1730,12 +1730,12 @@ ${features.includes('auth') ? 'echo "Login with:"\necho " Email: admin@example.
1730
1730
  }
1731
1731
  }
1732
1732
  async function createReadme(targetDir, projectName, features) {
1733
- let readme = `# ${projectName}
1734
-
1735
- Built with ChimerAI Kickstart
1736
-
1737
- ## Features
1738
-
1733
+ let readme = `# ${projectName}
1734
+
1735
+ Built with ChimerAI Kickstart
1736
+
1737
+ ## Features
1738
+
1739
1739
  `;
1740
1740
  const featureDescriptions = {
1741
1741
  auth: '- 🔐 Authentication with NextAuth',
@@ -1753,95 +1753,95 @@ Built with ChimerAI Kickstart
1753
1753
  readme += featureDescriptions[f] + '\n';
1754
1754
  }
1755
1755
  });
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
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
1845
1845
  `;
1846
1846
  await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'README.md'), readme);
1847
1847
  // Create docs directory with inline documentation
@@ -1849,28 +1849,28 @@ Commercial License - See LICENSE file
1849
1849
  const docsDir = path_1.default.join(targetDir, 'docs');
1850
1850
  await fs_extra_1.default.ensureDir(docsDir);
1851
1851
  // Generate inline quickstart guide
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
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
1874
1874
  `;
1875
1875
  await fs_extra_1.default.writeFile(path_1.default.join(docsDir, 'QUICKSTART.md'), quickstart);
1876
1876
  console.log(chalk_1.default.green(' ✓ docs/QUICKSTART.md'));