@codebakers/cli 1.6.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/audit.d.ts +19 -0
- package/dist/commands/audit.js +730 -0
- package/dist/commands/config.d.ts +4 -0
- package/dist/commands/config.js +176 -0
- package/dist/commands/doctor.js +59 -4
- package/dist/commands/heal.d.ts +41 -0
- package/dist/commands/heal.js +734 -0
- package/dist/commands/login.js +12 -16
- package/dist/commands/provision.d.ts +55 -3
- package/dist/commands/provision.js +243 -74
- package/dist/commands/scaffold.js +158 -80
- package/dist/commands/setup.js +60 -19
- package/dist/commands/upgrade.d.ts +4 -0
- package/dist/commands/upgrade.js +90 -0
- package/dist/config.d.ts +61 -5
- package/dist/config.js +268 -5
- package/dist/index.js +44 -3
- package/dist/lib/api.d.ts +45 -0
- package/dist/lib/api.js +159 -0
- package/dist/mcp/server.js +146 -0
- package/package.json +1 -1
- package/src/commands/audit.ts +827 -0
- package/src/commands/config.ts +216 -0
- package/src/commands/doctor.ts +69 -4
- package/src/commands/heal.ts +889 -0
- package/src/commands/login.ts +14 -18
- package/src/commands/provision.ts +323 -101
- package/src/commands/scaffold.ts +188 -81
- package/src/commands/setup.ts +65 -20
- package/src/commands/upgrade.ts +110 -0
- package/src/config.ts +320 -11
- package/src/index.ts +48 -3
- package/src/lib/api.ts +183 -0
- package/src/mcp/server.ts +160 -0
|
@@ -47,7 +47,7 @@ const templates = __importStar(require("../templates/nextjs-supabase.js"));
|
|
|
47
47
|
const config_js_1 = require("../config.js");
|
|
48
48
|
const provision_js_1 = require("./provision.js");
|
|
49
49
|
/**
|
|
50
|
-
* Fetch service keys from CodeBakers server
|
|
50
|
+
* Fetch ALL service keys from CodeBakers server
|
|
51
51
|
*/
|
|
52
52
|
async function fetchServerKeys() {
|
|
53
53
|
const apiKey = (0, config_js_1.getApiKey)();
|
|
@@ -61,7 +61,9 @@ async function fetchServerKeys() {
|
|
|
61
61
|
},
|
|
62
62
|
});
|
|
63
63
|
if (response.ok) {
|
|
64
|
-
|
|
64
|
+
const data = await response.json();
|
|
65
|
+
// Handle wrapped response { data: {...} } or direct response
|
|
66
|
+
return data.data || data;
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
69
|
catch {
|
|
@@ -69,17 +71,6 @@ async function fetchServerKeys() {
|
|
|
69
71
|
}
|
|
70
72
|
return null;
|
|
71
73
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Sync server keys to local storage
|
|
74
|
-
*/
|
|
75
|
-
function syncKeysToLocal(keys) {
|
|
76
|
-
if (keys.github)
|
|
77
|
-
(0, config_js_1.setServiceKey)('github', keys.github);
|
|
78
|
-
if (keys.supabase)
|
|
79
|
-
(0, config_js_1.setServiceKey)('supabase', keys.supabase);
|
|
80
|
-
if (keys.vercel)
|
|
81
|
-
(0, config_js_1.setServiceKey)('vercel', keys.vercel);
|
|
82
|
-
}
|
|
83
74
|
// Cursor IDE configuration templates
|
|
84
75
|
const CURSORRULES_TEMPLATE = `# CODEBAKERS CURSOR RULES
|
|
85
76
|
# Zero-friction AI assistance - everything is automatic
|
|
@@ -319,6 +310,49 @@ async function confirm(question) {
|
|
|
319
310
|
const answer = await prompt(`${question} (Y/n): `);
|
|
320
311
|
return answer.toLowerCase() !== 'n';
|
|
321
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Display configured keys summary
|
|
315
|
+
*/
|
|
316
|
+
function displayKeysSummary(_source) {
|
|
317
|
+
const configuredKeys = (0, config_js_1.getConfiguredServiceKeys)();
|
|
318
|
+
if (configuredKeys.length === 0) {
|
|
319
|
+
console.log(chalk_1.default.gray(' No service keys configured\n'));
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
// Group by provisionable vs other
|
|
323
|
+
const provisionable = configuredKeys.filter(k => config_js_1.PROVISIONABLE_KEYS.includes(k));
|
|
324
|
+
const other = configuredKeys.filter(k => !config_js_1.PROVISIONABLE_KEYS.includes(k));
|
|
325
|
+
if (provisionable.length > 0) {
|
|
326
|
+
console.log(chalk_1.default.gray(' Infrastructure (can auto-provision):'));
|
|
327
|
+
for (const key of provisionable) {
|
|
328
|
+
console.log(chalk_1.default.green(` ✓ ${config_js_1.SERVICE_KEY_LABELS[key]}`));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (other.length > 0) {
|
|
332
|
+
console.log(chalk_1.default.gray(' Other services:'));
|
|
333
|
+
for (const key of other) {
|
|
334
|
+
console.log(chalk_1.default.green(` ✓ ${config_js_1.SERVICE_KEY_LABELS[key]}`));
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
console.log('');
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Display sync results
|
|
341
|
+
*/
|
|
342
|
+
function displaySyncResults(result) {
|
|
343
|
+
if (result.added.length > 0) {
|
|
344
|
+
console.log(chalk_1.default.green(` + ${result.added.length} keys synced from server`));
|
|
345
|
+
for (const key of result.added) {
|
|
346
|
+
console.log(chalk_1.default.gray(` + ${config_js_1.SERVICE_KEY_LABELS[key]}`));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (result.updated.length > 0) {
|
|
350
|
+
console.log(chalk_1.default.yellow(` ~ ${result.updated.length} keys updated from server`));
|
|
351
|
+
}
|
|
352
|
+
if (result.added.length === 0 && result.updated.length === 0) {
|
|
353
|
+
console.log(chalk_1.default.gray(' All keys already in sync'));
|
|
354
|
+
}
|
|
355
|
+
}
|
|
322
356
|
/**
|
|
323
357
|
* Scaffold a new project with full structure
|
|
324
358
|
*/
|
|
@@ -472,7 +506,6 @@ async function scaffold() {
|
|
|
472
506
|
console.log(chalk_1.default.gray(' Run `npm install` manually.\n'));
|
|
473
507
|
}
|
|
474
508
|
}
|
|
475
|
-
spinner.succeed('Project structure created!');
|
|
476
509
|
// Auto-install CodeBakers patterns
|
|
477
510
|
console.log(chalk_1.default.white('\n Installing CodeBakers patterns...\n'));
|
|
478
511
|
const apiKey = (0, config_js_1.getApiKey)();
|
|
@@ -551,37 +584,28 @@ async function scaffold() {
|
|
|
551
584
|
if (wantProvision) {
|
|
552
585
|
// Check for saved keys in CodeBakers back office
|
|
553
586
|
const serverKeys = await fetchServerKeys();
|
|
554
|
-
const
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
const localVercel = (0, config_js_1.getServiceKey)('vercel');
|
|
558
|
-
const hasLocalKeys = localGithub || localSupabase || localVercel;
|
|
559
|
-
if (hasServerKeys || hasLocalKeys) {
|
|
587
|
+
const serverKeyCount = serverKeys ? Object.values(serverKeys).filter(v => v).length : 0;
|
|
588
|
+
const localKeyCount = (0, config_js_1.getConfiguredServiceKeys)().length;
|
|
589
|
+
if (serverKeyCount > 0 || localKeyCount > 0) {
|
|
560
590
|
// Show which keys are available
|
|
561
591
|
console.log(chalk_1.default.white('\n Available service keys:\n'));
|
|
562
|
-
if (
|
|
563
|
-
console.log(chalk_1.default.gray(
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
console.log(chalk_1.default.green(' ✓ Vercel'));
|
|
592
|
+
if (serverKeyCount > 0) {
|
|
593
|
+
console.log(chalk_1.default.gray(` From CodeBakers account (${serverKeyCount} keys):`));
|
|
594
|
+
for (const key of config_js_1.SERVICE_KEYS) {
|
|
595
|
+
if (serverKeys && serverKeys[key]) {
|
|
596
|
+
console.log(chalk_1.default.green(` ✓ ${config_js_1.SERVICE_KEY_LABELS[key]}`));
|
|
597
|
+
}
|
|
598
|
+
}
|
|
570
599
|
}
|
|
571
|
-
if (
|
|
572
|
-
console.log(chalk_1.default.gray(
|
|
573
|
-
|
|
574
|
-
console.log(chalk_1.default.green(' ✓ GitHub'));
|
|
575
|
-
if (localSupabase)
|
|
576
|
-
console.log(chalk_1.default.green(' ✓ Supabase'));
|
|
577
|
-
if (localVercel)
|
|
578
|
-
console.log(chalk_1.default.green(' ✓ Vercel'));
|
|
600
|
+
if (localKeyCount > 0) {
|
|
601
|
+
console.log(chalk_1.default.gray(` Stored locally (${localKeyCount} keys):`));
|
|
602
|
+
displayKeysSummary('local');
|
|
579
603
|
}
|
|
580
604
|
console.log('');
|
|
581
605
|
// Ask which keys to use
|
|
582
606
|
console.log(chalk_1.default.white(' Which keys would you like to use?\n'));
|
|
583
607
|
console.log(chalk_1.default.gray(' 1. ') + chalk_1.default.cyan('Use saved keys') + chalk_1.default.gray(' - Use keys from your account/local storage'));
|
|
584
|
-
console.log(chalk_1.default.gray(' 2. ') + chalk_1.default.cyan('
|
|
608
|
+
console.log(chalk_1.default.gray(' 2. ') + chalk_1.default.cyan('Start fresh') + chalk_1.default.gray(' - Clear local keys, enter new ones (for client projects)'));
|
|
585
609
|
console.log(chalk_1.default.gray(' 3. ') + chalk_1.default.cyan('Skip') + chalk_1.default.gray(' - Don\'t provision, I\'ll do it manually\n'));
|
|
586
610
|
let keyChoice = '';
|
|
587
611
|
while (!['1', '2', '3'].includes(keyChoice)) {
|
|
@@ -590,24 +614,43 @@ async function scaffold() {
|
|
|
590
614
|
if (keyChoice === '3') {
|
|
591
615
|
console.log(chalk_1.default.gray('\n Skipping auto-provisioning.\n'));
|
|
592
616
|
}
|
|
617
|
+
else if (keyChoice === '2') {
|
|
618
|
+
// FIX: Actually clear keys for client projects
|
|
619
|
+
console.log(chalk_1.default.yellow('\n Clearing local keys for fresh start...\n'));
|
|
620
|
+
(0, config_js_1.clearAllServiceKeys)();
|
|
621
|
+
console.log(chalk_1.default.green(' ✓ Local keys cleared'));
|
|
622
|
+
console.log(chalk_1.default.gray(' You\'ll be prompted to enter keys for each service.\n'));
|
|
623
|
+
// Initialize git first
|
|
624
|
+
try {
|
|
625
|
+
(0, child_process_1.execSync)('git init', { cwd, stdio: 'pipe' });
|
|
626
|
+
(0, child_process_1.execSync)('git add .', { cwd, stdio: 'pipe' });
|
|
627
|
+
(0, child_process_1.execSync)('git commit -m "Initial commit from CodeBakers scaffold"', { cwd, stdio: 'pipe' });
|
|
628
|
+
}
|
|
629
|
+
catch {
|
|
630
|
+
// Git might already be initialized
|
|
631
|
+
}
|
|
632
|
+
provisionResult = await (0, provision_js_1.provisionAll)(projectName, `${projectName} - Built with CodeBakers`);
|
|
633
|
+
}
|
|
593
634
|
else {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
console.log(chalk_1.default.
|
|
635
|
+
// Option 1: Use saved keys
|
|
636
|
+
if (serverKeyCount > 0 && serverKeys) {
|
|
637
|
+
// Sync server keys to local storage
|
|
638
|
+
console.log(chalk_1.default.white('\n Syncing keys from CodeBakers account...\n'));
|
|
639
|
+
const syncResult = (0, config_js_1.syncServiceKeys)(serverKeys);
|
|
640
|
+
displaySyncResults(syncResult);
|
|
641
|
+
console.log(chalk_1.default.green('\n ✓ Keys synced from CodeBakers account\n'));
|
|
598
642
|
}
|
|
599
|
-
else
|
|
600
|
-
|
|
601
|
-
console.log(chalk_1.default.gray('\n You\'ll be prompted to enter keys for each service.\n'));
|
|
643
|
+
else {
|
|
644
|
+
console.log(chalk_1.default.green('\n ✓ Using locally stored keys\n'));
|
|
602
645
|
}
|
|
603
|
-
// Initialize git first
|
|
646
|
+
// Initialize git first
|
|
604
647
|
try {
|
|
605
648
|
(0, child_process_1.execSync)('git init', { cwd, stdio: 'pipe' });
|
|
606
649
|
(0, child_process_1.execSync)('git add .', { cwd, stdio: 'pipe' });
|
|
607
650
|
(0, child_process_1.execSync)('git commit -m "Initial commit from CodeBakers scaffold"', { cwd, stdio: 'pipe' });
|
|
608
651
|
}
|
|
609
652
|
catch {
|
|
610
|
-
// Git might already be initialized
|
|
653
|
+
// Git might already be initialized
|
|
611
654
|
}
|
|
612
655
|
provisionResult = await (0, provision_js_1.provisionAll)(projectName, `${projectName} - Built with CodeBakers`);
|
|
613
656
|
}
|
|
@@ -616,27 +659,36 @@ async function scaffold() {
|
|
|
616
659
|
// No saved keys - proceed with provisioning (will prompt for keys)
|
|
617
660
|
console.log(chalk_1.default.gray('\n No saved keys found. You\'ll be prompted to enter keys for each service.\n'));
|
|
618
661
|
console.log(chalk_1.default.gray(' Tip: Save keys in your CodeBakers dashboard to auto-provision future projects!\n'));
|
|
619
|
-
// Initialize git first
|
|
662
|
+
// Initialize git first
|
|
620
663
|
try {
|
|
621
664
|
(0, child_process_1.execSync)('git init', { cwd, stdio: 'pipe' });
|
|
622
665
|
(0, child_process_1.execSync)('git add .', { cwd, stdio: 'pipe' });
|
|
623
666
|
(0, child_process_1.execSync)('git commit -m "Initial commit from CodeBakers scaffold"', { cwd, stdio: 'pipe' });
|
|
624
667
|
}
|
|
625
668
|
catch {
|
|
626
|
-
// Git might already be initialized
|
|
669
|
+
// Git might already be initialized
|
|
627
670
|
}
|
|
628
671
|
provisionResult = await (0, provision_js_1.provisionAll)(projectName, `${projectName} - Built with CodeBakers`);
|
|
629
672
|
}
|
|
630
|
-
//
|
|
673
|
+
// Write ALL service keys to .env.local
|
|
674
|
+
const additionalVars = {};
|
|
675
|
+
// Add Supabase project-specific vars if provisioned
|
|
631
676
|
if (provisionResult.supabase) {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
677
|
+
additionalVars['NEXT_PUBLIC_SUPABASE_URL'] = provisionResult.supabase.apiUrl;
|
|
678
|
+
additionalVars['NEXT_PUBLIC_SUPABASE_ANON_KEY'] = provisionResult.supabase.anonKey;
|
|
679
|
+
additionalVars['SUPABASE_PROJECT_ID'] = provisionResult.supabase.projectId;
|
|
680
|
+
}
|
|
681
|
+
// Add Vercel project-specific vars if provisioned
|
|
682
|
+
if (provisionResult.vercel) {
|
|
683
|
+
additionalVars['VERCEL_PROJECT_ID'] = provisionResult.vercel.projectId;
|
|
684
|
+
}
|
|
685
|
+
// Write all keys to .env.local
|
|
686
|
+
const { written } = (0, config_js_1.writeKeysToEnvFile)(cwd, {
|
|
687
|
+
includeEmpty: false,
|
|
688
|
+
additionalVars,
|
|
689
|
+
});
|
|
690
|
+
if (written > 0 || Object.keys(additionalVars).length > 0) {
|
|
691
|
+
console.log(chalk_1.default.green(`\n ✅ Wrote ${written} service keys and ${Object.keys(additionalVars).length} project configs to .env.local\n`));
|
|
640
692
|
}
|
|
641
693
|
}
|
|
642
694
|
// Success message
|
|
@@ -676,32 +728,58 @@ async function scaffold() {
|
|
|
676
728
|
}
|
|
677
729
|
console.log('');
|
|
678
730
|
console.log(chalk_1.default.white(' Next steps:\n'));
|
|
731
|
+
// Show appropriate next steps based on what was provisioned
|
|
732
|
+
let stepNum = 1;
|
|
733
|
+
if (!provisionResult.supabase) {
|
|
734
|
+
if (isBeginnerMode) {
|
|
735
|
+
console.log(chalk_1.default.cyan(` ${stepNum}. `) + chalk_1.default.white('Set up Supabase (free database + login):'));
|
|
736
|
+
console.log(chalk_1.default.gray(' Go to https://supabase.com → Create free account → New Project'));
|
|
737
|
+
console.log('');
|
|
738
|
+
stepNum++;
|
|
739
|
+
console.log(chalk_1.default.cyan(` ${stepNum}. `) + chalk_1.default.white('Connect your project:'));
|
|
740
|
+
console.log(chalk_1.default.gray(' Open .env.local file and paste your Supabase credentials'));
|
|
741
|
+
console.log(chalk_1.default.gray(' (Found in Supabase: Settings → API)'));
|
|
742
|
+
console.log('');
|
|
743
|
+
stepNum++;
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
console.log(chalk_1.default.cyan(` ${stepNum}. `) + chalk_1.default.gray('Create Supabase project at https://supabase.com'));
|
|
747
|
+
stepNum++;
|
|
748
|
+
console.log(chalk_1.default.cyan(` ${stepNum}. `) + chalk_1.default.gray('Update .env.local with Supabase credentials'));
|
|
749
|
+
stepNum++;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
// CRITICAL: Add db:push step
|
|
753
|
+
console.log(chalk_1.default.cyan(` ${stepNum}. `) + chalk_1.default.white('Push database schema:'));
|
|
754
|
+
console.log(chalk_1.default.gray(' npx drizzle-kit db:push'));
|
|
755
|
+
console.log('');
|
|
756
|
+
stepNum++;
|
|
757
|
+
console.log(chalk_1.default.cyan(` ${stepNum}. `) + chalk_1.default.white('Start your app:'));
|
|
758
|
+
console.log(chalk_1.default.gray(' npm run dev'));
|
|
679
759
|
if (isBeginnerMode) {
|
|
680
|
-
console.log(chalk_1.default.cyan(' 1. ') + chalk_1.default.white('Set up Supabase (free database + login):'));
|
|
681
|
-
console.log(chalk_1.default.gray(' Go to https://supabase.com → Create free account → New Project'));
|
|
682
|
-
console.log('');
|
|
683
|
-
console.log(chalk_1.default.cyan(' 2. ') + chalk_1.default.white('Connect your project:'));
|
|
684
|
-
console.log(chalk_1.default.gray(' Open .env.local file and paste your Supabase credentials'));
|
|
685
|
-
console.log(chalk_1.default.gray(' (Found in Supabase: Settings → API)'));
|
|
686
|
-
console.log('');
|
|
687
|
-
console.log(chalk_1.default.cyan(' 3. ') + chalk_1.default.white('Start your app:'));
|
|
688
|
-
console.log(chalk_1.default.gray(' Run: npm run dev'));
|
|
689
760
|
console.log(chalk_1.default.gray(' Open: http://localhost:3000 in your browser'));
|
|
690
|
-
console.log('');
|
|
691
|
-
console.log(chalk_1.default.cyan(' 4. ') + chalk_1.default.white('Start building!'));
|
|
692
|
-
console.log(chalk_1.default.gray(' Tell your AI: "Build me a [feature]"'));
|
|
693
|
-
console.log(chalk_1.default.gray(' The AI already has all the patterns loaded!\n'));
|
|
694
761
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
console.log(chalk_1.default.gray('
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
762
|
+
console.log('');
|
|
763
|
+
stepNum++;
|
|
764
|
+
console.log(chalk_1.default.cyan(` ${stepNum}. `) + chalk_1.default.white('Start building!'));
|
|
765
|
+
console.log(chalk_1.default.gray(' Tell your AI: "Build me a [feature]"'));
|
|
766
|
+
if (isBeginnerMode) {
|
|
767
|
+
console.log(chalk_1.default.gray(' The AI already has all the patterns loaded!'));
|
|
768
|
+
}
|
|
769
|
+
console.log('');
|
|
770
|
+
// Show provisioning summary if any services were created
|
|
771
|
+
if (provisionResult.github || provisionResult.supabase || provisionResult.vercel) {
|
|
772
|
+
console.log(chalk_1.default.white(' Provisioned services:\n'));
|
|
773
|
+
if (provisionResult.github) {
|
|
774
|
+
console.log(chalk_1.default.green(' ✅ GitHub: ') + chalk_1.default.gray(provisionResult.github.repoUrl));
|
|
775
|
+
}
|
|
776
|
+
if (provisionResult.supabase) {
|
|
777
|
+
console.log(chalk_1.default.green(' ✅ Supabase: ') + chalk_1.default.gray(provisionResult.supabase.projectUrl));
|
|
778
|
+
}
|
|
779
|
+
if (provisionResult.vercel) {
|
|
780
|
+
console.log(chalk_1.default.green(' ✅ Vercel: ') + chalk_1.default.gray(provisionResult.vercel.projectUrl));
|
|
781
|
+
}
|
|
782
|
+
console.log('');
|
|
705
783
|
}
|
|
706
784
|
}
|
|
707
785
|
catch (error) {
|
package/dist/commands/setup.js
CHANGED
|
@@ -9,6 +9,7 @@ const ora_1 = __importDefault(require("ora"));
|
|
|
9
9
|
const readline_1 = require("readline");
|
|
10
10
|
const child_process_1 = require("child_process");
|
|
11
11
|
const config_js_1 = require("../config.js");
|
|
12
|
+
const api_js_1 = require("../lib/api.js");
|
|
12
13
|
function prompt(question) {
|
|
13
14
|
const rl = (0, readline_1.createInterface)({
|
|
14
15
|
input: process.stdin,
|
|
@@ -25,6 +26,15 @@ async function setup() {
|
|
|
25
26
|
console.log(chalk_1.default.blue('\n ╔══════════════════════════════════════╗'));
|
|
26
27
|
console.log(chalk_1.default.blue(' ║') + chalk_1.default.white(' CodeBakers One-Time Setup ') + chalk_1.default.blue('║'));
|
|
27
28
|
console.log(chalk_1.default.blue(' ╚══════════════════════════════════════╝\n'));
|
|
29
|
+
// Check CLI version
|
|
30
|
+
const version = (0, api_js_1.getCliVersion)();
|
|
31
|
+
console.log(chalk_1.default.gray(` CLI Version: ${version}\n`));
|
|
32
|
+
// Check for updates
|
|
33
|
+
const updateInfo = await (0, api_js_1.checkForUpdates)();
|
|
34
|
+
if (updateInfo?.updateAvailable) {
|
|
35
|
+
console.log(chalk_1.default.yellow(` ⚠️ Update available: ${updateInfo.currentVersion} → ${updateInfo.latestVersion}`));
|
|
36
|
+
console.log(chalk_1.default.gray(' Run: npm install -g @codebakers/cli@latest\n'));
|
|
37
|
+
}
|
|
28
38
|
// Check if already set up
|
|
29
39
|
const existingKey = (0, config_js_1.getApiKey)();
|
|
30
40
|
if (existingKey) {
|
|
@@ -44,39 +54,70 @@ async function setup() {
|
|
|
44
54
|
console.log(chalk_1.default.red('\n API key is required.\n'));
|
|
45
55
|
process.exit(1);
|
|
46
56
|
}
|
|
47
|
-
// Validate API key
|
|
57
|
+
// Validate API key using shared validation
|
|
48
58
|
const spinner = (0, ora_1.default)('Validating API key...').start();
|
|
49
59
|
try {
|
|
50
|
-
|
|
51
|
-
const response = await fetch(`${apiUrl}/api/patterns`, {
|
|
52
|
-
method: 'GET',
|
|
53
|
-
headers: {
|
|
54
|
-
Authorization: `Bearer ${apiKey}`,
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
if (!response.ok) {
|
|
58
|
-
spinner.fail('Invalid API key');
|
|
59
|
-
const error = await response.json().catch(() => ({}));
|
|
60
|
-
console.log(chalk_1.default.red(`\n ${error.error || 'API key validation failed'}\n`));
|
|
61
|
-
process.exit(1);
|
|
62
|
-
}
|
|
60
|
+
await (0, api_js_1.validateApiKey)(apiKey);
|
|
63
61
|
spinner.succeed('API key validated');
|
|
64
62
|
}
|
|
65
|
-
catch {
|
|
66
|
-
spinner.fail('
|
|
67
|
-
|
|
63
|
+
catch (error) {
|
|
64
|
+
spinner.fail('Invalid API key');
|
|
65
|
+
if (error && typeof error === 'object' && 'recoverySteps' in error) {
|
|
66
|
+
console.log(chalk_1.default.red(`\n ${(0, api_js_1.formatApiError)(error)}\n`));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const message = error instanceof Error ? error.message : 'API key validation failed';
|
|
70
|
+
console.log(chalk_1.default.red(`\n ${message}\n`));
|
|
71
|
+
}
|
|
68
72
|
process.exit(1);
|
|
69
73
|
}
|
|
70
74
|
// Save API key
|
|
71
75
|
(0, config_js_1.setApiKey)(apiKey);
|
|
72
76
|
console.log(chalk_1.default.green(' ✓ API key saved\n'));
|
|
77
|
+
// Step 2: Sync service keys from server
|
|
78
|
+
console.log(chalk_1.default.white(' Step 2: Syncing service keys...\n'));
|
|
79
|
+
const syncSpinner = (0, ora_1.default)('Fetching service keys from your account...').start();
|
|
80
|
+
try {
|
|
81
|
+
const apiUrl = (0, config_js_1.getApiUrl)();
|
|
82
|
+
const response = await fetch(`${apiUrl}/api/cli/service-keys`, {
|
|
83
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
84
|
+
});
|
|
85
|
+
if (response.ok) {
|
|
86
|
+
const data = await response.json();
|
|
87
|
+
const serverKeys = data.data || data;
|
|
88
|
+
const result = (0, config_js_1.syncServiceKeys)(serverKeys);
|
|
89
|
+
const totalSynced = result.added.length + result.updated.length;
|
|
90
|
+
if (totalSynced > 0) {
|
|
91
|
+
syncSpinner.succeed(`Synced ${totalSynced} service keys`);
|
|
92
|
+
// Show which keys were synced
|
|
93
|
+
for (const keyName of [...result.added, ...result.updated]) {
|
|
94
|
+
console.log(chalk_1.default.green(` ✓ ${config_js_1.SERVICE_KEY_LABELS[keyName]}`));
|
|
95
|
+
}
|
|
96
|
+
console.log('');
|
|
97
|
+
}
|
|
98
|
+
else if (result.unchanged.length > 0) {
|
|
99
|
+
syncSpinner.succeed(`${result.unchanged.length} service keys already in sync`);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
syncSpinner.succeed('No service keys configured in your account');
|
|
103
|
+
console.log(chalk_1.default.gray(' Tip: Add keys at https://codebakers.ai/settings\n'));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
syncSpinner.warn('Could not sync service keys');
|
|
108
|
+
console.log(chalk_1.default.gray(' You can add keys later in the scaffold wizard.\n'));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
syncSpinner.warn('Could not sync service keys');
|
|
113
|
+
console.log(chalk_1.default.gray(' You can add keys later in the scaffold wizard.\n'));
|
|
114
|
+
}
|
|
73
115
|
showFinalInstructions();
|
|
74
116
|
}
|
|
75
117
|
function showFinalInstructions() {
|
|
76
118
|
const isWindows = process.platform === 'win32';
|
|
77
|
-
console.log(chalk_1.default.green('\n ✅ API key saved!\n'));
|
|
78
119
|
console.log(chalk_1.default.blue(' ══════════════════════════════════════════════════════════'));
|
|
79
|
-
console.log(chalk_1.default.white.bold('\n STEP
|
|
120
|
+
console.log(chalk_1.default.white.bold('\n STEP 3: Connecting CodeBakers to Claude...\n'));
|
|
80
121
|
console.log(chalk_1.default.blue(' ══════════════════════════════════════════════════════════\n'));
|
|
81
122
|
// Auto-install MCP server
|
|
82
123
|
const mcpCmd = isWindows
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.upgrade = upgrade;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
const path_1 = require("path");
|
|
11
|
+
const config_js_1 = require("../config.js");
|
|
12
|
+
const api_js_1 = require("../lib/api.js");
|
|
13
|
+
/**
|
|
14
|
+
* Upgrade CodeBakers patterns to the latest version
|
|
15
|
+
*/
|
|
16
|
+
async function upgrade() {
|
|
17
|
+
console.log(chalk_1.default.blue('\n CodeBakers Upgrade\n'));
|
|
18
|
+
const cwd = process.cwd();
|
|
19
|
+
const claudeMdPath = (0, path_1.join)(cwd, 'CLAUDE.md');
|
|
20
|
+
const claudeDir = (0, path_1.join)(cwd, '.claude');
|
|
21
|
+
// Check if this is a CodeBakers project
|
|
22
|
+
if (!(0, fs_1.existsSync)(claudeMdPath) && !(0, fs_1.existsSync)(claudeDir)) {
|
|
23
|
+
console.log(chalk_1.default.yellow(' No CodeBakers installation found in this directory.\n'));
|
|
24
|
+
console.log(chalk_1.default.gray(' Run `codebakers install` to set up patterns first.\n'));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Check for CLI updates
|
|
28
|
+
console.log(chalk_1.default.gray(' Checking for CLI updates...\n'));
|
|
29
|
+
const updateInfo = await (0, api_js_1.checkForUpdates)();
|
|
30
|
+
if (updateInfo?.updateAvailable) {
|
|
31
|
+
console.log(chalk_1.default.yellow(` ⚠️ CLI update available: ${updateInfo.currentVersion} → ${updateInfo.latestVersion}`));
|
|
32
|
+
console.log(chalk_1.default.gray(' Run: npm install -g @codebakers/cli@latest\n'));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
console.log(chalk_1.default.green(` ✓ CLI is up to date (v${(0, api_js_1.getCliVersion)()})\n`));
|
|
36
|
+
}
|
|
37
|
+
// Check API key
|
|
38
|
+
const apiKey = (0, config_js_1.getApiKey)();
|
|
39
|
+
if (!apiKey) {
|
|
40
|
+
console.log(chalk_1.default.yellow(' Not logged in. Run `codebakers setup` first.\n'));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// Fetch latest patterns
|
|
44
|
+
const spinner = (0, ora_1.default)('Fetching latest patterns...').start();
|
|
45
|
+
try {
|
|
46
|
+
const apiUrl = (0, config_js_1.getApiUrl)();
|
|
47
|
+
const response = await fetch(`${apiUrl}/api/content`, {
|
|
48
|
+
method: 'GET',
|
|
49
|
+
headers: {
|
|
50
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
const error = await response.json().catch(() => ({}));
|
|
55
|
+
throw new Error(error.error || 'Failed to fetch patterns');
|
|
56
|
+
}
|
|
57
|
+
const content = await response.json();
|
|
58
|
+
spinner.succeed(`Patterns v${content.version} downloaded`);
|
|
59
|
+
// Count what we're updating
|
|
60
|
+
const moduleCount = Object.keys(content.modules).length;
|
|
61
|
+
console.log(chalk_1.default.gray(` Updating ${moduleCount} modules...\n`));
|
|
62
|
+
// Update CLAUDE.md
|
|
63
|
+
if (content.router) {
|
|
64
|
+
(0, fs_1.writeFileSync)(claudeMdPath, content.router);
|
|
65
|
+
console.log(chalk_1.default.green(' ✓ Updated CLAUDE.md'));
|
|
66
|
+
}
|
|
67
|
+
// Update pattern modules
|
|
68
|
+
if (content.modules && Object.keys(content.modules).length > 0) {
|
|
69
|
+
if (!(0, fs_1.existsSync)(claudeDir)) {
|
|
70
|
+
(0, fs_1.mkdirSync)(claudeDir, { recursive: true });
|
|
71
|
+
}
|
|
72
|
+
for (const [name, data] of Object.entries(content.modules)) {
|
|
73
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(claudeDir, name), data);
|
|
74
|
+
}
|
|
75
|
+
console.log(chalk_1.default.green(` ✓ Updated ${moduleCount} modules in .claude/`));
|
|
76
|
+
}
|
|
77
|
+
console.log(chalk_1.default.green(`\n ✅ Upgraded to patterns v${content.version}!\n`));
|
|
78
|
+
// Show what's new if available
|
|
79
|
+
console.log(chalk_1.default.gray(' Changes take effect in your next AI session.\n'));
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
spinner.fail('Upgrade failed');
|
|
83
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
84
|
+
console.log(chalk_1.default.red(`\n Error: ${message}\n`));
|
|
85
|
+
if (message.includes('401') || message.includes('Invalid')) {
|
|
86
|
+
console.log(chalk_1.default.gray(' Your API key may have expired. Run `codebakers setup` to reconfigure.\n'));
|
|
87
|
+
}
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,8 +1,35 @@
|
|
|
1
1
|
export type ExperienceLevel = 'beginner' | 'intermediate' | 'advanced';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Canonical list of ALL supported service keys
|
|
4
|
+
* This must match the server contract at src/lib/contracts/service-keys.ts
|
|
5
|
+
*/
|
|
6
|
+
export declare const SERVICE_KEYS: readonly ["github", "supabase", "vercel", "openai", "anthropic", "stripe", "twilio_sid", "twilio_auth", "resend", "vapi", "sentry", "cloudinary", "pexels", "midjourney"];
|
|
7
|
+
export type ServiceName = typeof SERVICE_KEYS[number];
|
|
8
|
+
/**
|
|
9
|
+
* Map service key names to environment variable names
|
|
10
|
+
*/
|
|
11
|
+
export declare const ENV_VAR_NAMES: Record<ServiceName, string>;
|
|
12
|
+
/**
|
|
13
|
+
* Service key categories for display grouping
|
|
14
|
+
*/
|
|
15
|
+
export declare const SERVICE_KEY_CATEGORIES: Record<string, ServiceName[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Service key labels for display
|
|
18
|
+
*/
|
|
19
|
+
export declare const SERVICE_KEY_LABELS: Record<ServiceName, string>;
|
|
20
|
+
/**
|
|
21
|
+
* Keys that can be used for auto-provisioning
|
|
22
|
+
*/
|
|
23
|
+
export declare const PROVISIONABLE_KEYS: ServiceName[];
|
|
24
|
+
type ServiceKeys = {
|
|
25
|
+
[K in ServiceName]: string | null;
|
|
26
|
+
};
|
|
27
|
+
interface ConfigSchema {
|
|
28
|
+
apiKey: string | null;
|
|
29
|
+
apiUrl: string;
|
|
30
|
+
experienceLevel: ExperienceLevel;
|
|
31
|
+
serviceKeys: ServiceKeys;
|
|
32
|
+
lastKeySync: string | null;
|
|
6
33
|
}
|
|
7
34
|
export declare function getApiKey(): string | null;
|
|
8
35
|
export declare function setApiKey(key: string): void;
|
|
@@ -11,9 +38,38 @@ export declare function getApiUrl(): string;
|
|
|
11
38
|
export declare function setApiUrl(url: string): void;
|
|
12
39
|
export declare function getExperienceLevel(): ExperienceLevel;
|
|
13
40
|
export declare function setExperienceLevel(level: ExperienceLevel): void;
|
|
14
|
-
export type ServiceName = 'github' | 'supabase' | 'vercel';
|
|
15
41
|
export declare function getServiceKey(service: ServiceName): string | null;
|
|
16
42
|
export declare function setServiceKey(service: ServiceName, key: string): void;
|
|
17
43
|
export declare function clearServiceKey(service: ServiceName): void;
|
|
44
|
+
export declare function clearAllServiceKeys(): void;
|
|
18
45
|
export declare function getAllServiceKeys(): ServiceKeys;
|
|
46
|
+
export declare function getConfiguredServiceKeys(): ServiceName[];
|
|
47
|
+
export declare function getLastKeySync(): Date | null;
|
|
48
|
+
export declare function setLastKeySync(date: Date): void;
|
|
49
|
+
export interface SyncResult {
|
|
50
|
+
added: ServiceName[];
|
|
51
|
+
updated: ServiceName[];
|
|
52
|
+
unchanged: ServiceName[];
|
|
53
|
+
total: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Sync keys from server response to local storage
|
|
57
|
+
*/
|
|
58
|
+
export declare function syncServiceKeys(serverKeys: Partial<ServiceKeys>): SyncResult;
|
|
59
|
+
/**
|
|
60
|
+
* Write service keys to a .env.local file
|
|
61
|
+
*/
|
|
62
|
+
export declare function writeKeysToEnvFile(projectPath: string, options?: {
|
|
63
|
+
includeEmpty?: boolean;
|
|
64
|
+
additionalVars?: Record<string, string>;
|
|
65
|
+
}): {
|
|
66
|
+
written: number;
|
|
67
|
+
path: string;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Check if a key value appears valid (basic format check)
|
|
71
|
+
*/
|
|
72
|
+
export declare function validateKeyFormat(name: ServiceName, value: string): boolean;
|
|
73
|
+
export declare function getConfigPath(): string;
|
|
74
|
+
export declare function getConfigStore(): ConfigSchema;
|
|
19
75
|
export {};
|