@tamyla/clodo-framework 3.0.11 ā 3.0.13
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/CHANGELOG.md +16 -0
- package/bin/clodo-service.js +385 -184
- package/dist/orchestration/multi-domain-orchestrator.js +131 -63
- package/dist/service-management/AssessmentCache.js +303 -0
- package/dist/service-management/CapabilityAssessmentEngine.js +902 -0
- package/dist/service-management/ConfirmationEngine.js +4 -4
- package/dist/service-management/InputCollector.js +119 -4
- package/dist/service-management/ServiceAutoDiscovery.js +745 -0
- package/dist/service-management/ServiceCreator.js +1 -4
- package/dist/service-management/ServiceOrchestrator.js +269 -1
- package/dist/service-management/index.js +4 -1
- package/dist/utils/cloudflare/api.js +41 -1
- package/dist/utils/config/unified-config-manager.js +6 -2
- package/dist/utils/deployment/wrangler-config-manager.js +32 -7
- package/package.json +1 -1
- package/templates/generic/src/config/domains.js +3 -3
|
@@ -6,13 +6,10 @@
|
|
|
6
6
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, cpSync, readdirSync, statSync } from 'fs';
|
|
7
7
|
import { join, dirname, resolve } from 'path';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
-
const __dirname = dirname(__filename);
|
|
11
|
-
const TEMPLATES_DIR = join(__dirname, '..', '..', 'templates');
|
|
12
9
|
const SERVICE_TYPES = ['data-service', 'auth-service', 'content-service', 'api-gateway', 'generic'];
|
|
13
10
|
export class ServiceCreator {
|
|
14
11
|
constructor(options = {}) {
|
|
15
|
-
this.templatesDir = options.templatesDir ||
|
|
12
|
+
this.templatesDir = options.templatesDir || join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'templates');
|
|
16
13
|
this.serviceTypes = options.serviceTypes || SERVICE_TYPES;
|
|
17
14
|
}
|
|
18
15
|
|
|
@@ -17,6 +17,7 @@ import { ValidationHandler } from './handlers/ValidationHandler.js';
|
|
|
17
17
|
// Legacy imports for backward compatibility
|
|
18
18
|
import { ServiceCreator } from './ServiceCreator.js';
|
|
19
19
|
import { ErrorTracker } from './ErrorTracker.js';
|
|
20
|
+
import { CapabilityAssessmentEngine } from './CapabilityAssessmentEngine.js';
|
|
20
21
|
import chalk from 'chalk';
|
|
21
22
|
import fs from 'fs/promises';
|
|
22
23
|
import path from 'path';
|
|
@@ -60,11 +61,17 @@ export class ServiceOrchestrator {
|
|
|
60
61
|
// Tier 2: Smart confirmations for 15 derived values
|
|
61
62
|
const confirmedValues = await this.confirmationHandler.generateAndConfirm(coreInputs);
|
|
62
63
|
|
|
64
|
+
// Pre-generation assessment for final validation
|
|
65
|
+
console.log(chalk.yellow('š Pre-Generation Assessment'));
|
|
66
|
+
console.log(chalk.white('Final validation of deployment readiness...\n'));
|
|
67
|
+
const finalAssessment = await this.runFinalAssessment(coreInputs, confirmedValues);
|
|
68
|
+
|
|
63
69
|
// Tier 3: Automated generation of 67 components
|
|
64
70
|
console.log(chalk.yellow('āļø Tier 3: Automated Generation'));
|
|
65
71
|
console.log(chalk.white('Generating 67 configuration files and service components...\n'));
|
|
66
72
|
const generationResult = await this.generationHandler.generateService(coreInputs, confirmedValues, {
|
|
67
|
-
outputPath: this.outputPath
|
|
73
|
+
outputPath: this.outputPath,
|
|
74
|
+
assessment: finalAssessment // Pass assessment for generation optimization
|
|
68
75
|
});
|
|
69
76
|
|
|
70
77
|
// Display results
|
|
@@ -624,10 +631,271 @@ export class ServiceOrchestrator {
|
|
|
624
631
|
return [];
|
|
625
632
|
}
|
|
626
633
|
|
|
634
|
+
/**
|
|
635
|
+
* Run final assessment before generation
|
|
636
|
+
*/
|
|
637
|
+
async runFinalAssessment(coreInputs, confirmedValues) {
|
|
638
|
+
try {
|
|
639
|
+
// Import assessment engine dynamically to avoid circular dependencies
|
|
640
|
+
const {
|
|
641
|
+
CapabilityAssessmentEngine
|
|
642
|
+
} = await import('./CapabilityAssessmentEngine.js');
|
|
643
|
+
|
|
644
|
+
// Combine core inputs and confirmed values for assessment
|
|
645
|
+
const assessmentInputs = {
|
|
646
|
+
...coreInputs,
|
|
647
|
+
...confirmedValues,
|
|
648
|
+
// Ensure we have the right field names
|
|
649
|
+
serviceName: coreInputs.serviceName || coreInputs['service-name'],
|
|
650
|
+
serviceType: coreInputs.serviceType || coreInputs['service-type'] || 'generic',
|
|
651
|
+
domainName: coreInputs.domainName || coreInputs['domain-name'],
|
|
652
|
+
environment: coreInputs.environment || coreInputs['environment'] || 'development',
|
|
653
|
+
cloudflareToken: coreInputs.cloudflareToken || coreInputs['cloudflare-api-token']
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
// Create assessment engine with caching
|
|
657
|
+
const assessmentEngine = new CapabilityAssessmentEngine(this.outputPath, {
|
|
658
|
+
cacheEnabled: true,
|
|
659
|
+
cache: {
|
|
660
|
+
ttl: 5 * 60 * 1000
|
|
661
|
+
} // 5 minutes for final assessment
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
// Run comprehensive assessment
|
|
665
|
+
const assessment = await assessmentEngine.assessCapabilities(assessmentInputs);
|
|
666
|
+
|
|
667
|
+
// Check for deployment blockers
|
|
668
|
+
const blockers = assessment.gapAnalysis.missing.filter(gap => gap.priority === 'blocked');
|
|
669
|
+
if (blockers.length > 0) {
|
|
670
|
+
console.log(chalk.red('\nš« Deployment Blockers Detected:'));
|
|
671
|
+
blockers.forEach(blocker => {
|
|
672
|
+
console.log(chalk.red(` ⢠${blocker.capability}: ${blocker.reason}`));
|
|
673
|
+
});
|
|
674
|
+
if (this.interactive) {
|
|
675
|
+
const proceed = await this.confirmationHandler.promptHandler.confirm('\nContinue with generation despite blockers? (Service may not deploy successfully)', false);
|
|
676
|
+
if (!proceed) {
|
|
677
|
+
throw new Error('Service creation cancelled due to deployment blockers');
|
|
678
|
+
}
|
|
679
|
+
} else {
|
|
680
|
+
console.log(chalk.yellow('ā ļø Proceeding with generation despite blockers (non-interactive mode)'));
|
|
681
|
+
}
|
|
682
|
+
} else {
|
|
683
|
+
console.log(chalk.green('ā
No deployment blockers detected'));
|
|
684
|
+
}
|
|
685
|
+
return assessment;
|
|
686
|
+
} catch (error) {
|
|
687
|
+
console.log(chalk.yellow(`ā ļø Final assessment failed: ${error.message}`));
|
|
688
|
+
console.log(chalk.gray('Continuing with service generation...'));
|
|
689
|
+
return null;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
627
693
|
/**
|
|
628
694
|
* Escape special regex characters for safe replacement
|
|
629
695
|
*/
|
|
630
696
|
escapeRegExp(string) {
|
|
631
697
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
632
698
|
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Run pre-deploy assessment to validate deployment readiness
|
|
702
|
+
*/
|
|
703
|
+
async runPreDeployAssessment(deployConfig) {
|
|
704
|
+
console.log(chalk.yellow('š Running Pre-Deploy Assessment...'));
|
|
705
|
+
try {
|
|
706
|
+
// Create assessment engine
|
|
707
|
+
const assessmentEngine = new CapabilityAssessmentEngine(this.outputPath, {
|
|
708
|
+
cacheEnabled: true,
|
|
709
|
+
cache: {
|
|
710
|
+
ttl: 10 * 60 * 1000
|
|
711
|
+
} // 10 minutes for deploy assessment
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
// Run assessment
|
|
715
|
+
const assessment = await assessmentEngine.assessCapabilities(deployConfig);
|
|
716
|
+
|
|
717
|
+
// Check for critical gaps
|
|
718
|
+
const criticalGaps = assessment.gapAnalysis.missing.filter(gap => gap.priority === 'blocked');
|
|
719
|
+
const warningGaps = assessment.gapAnalysis.missing.filter(gap => gap.priority === 'warning');
|
|
720
|
+
if (criticalGaps.length > 0) {
|
|
721
|
+
console.log(chalk.red('\nš« Critical Deployment Blockers:'));
|
|
722
|
+
criticalGaps.forEach(gap => {
|
|
723
|
+
console.log(chalk.red(` ⢠${gap.capability}: ${gap.reason}`));
|
|
724
|
+
});
|
|
725
|
+
console.log(chalk.red('\nDeployment blocked due to critical gaps.'));
|
|
726
|
+
return {
|
|
727
|
+
...assessment,
|
|
728
|
+
deployable: false,
|
|
729
|
+
reason: 'Critical deployment blockers detected'
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
if (warningGaps.length > 0) {
|
|
733
|
+
console.log(chalk.yellow('\nā ļø Deployment Warnings:'));
|
|
734
|
+
warningGaps.forEach(gap => {
|
|
735
|
+
console.log(chalk.yellow(` ⢠${gap.capability}: ${gap.reason}`));
|
|
736
|
+
});
|
|
737
|
+
console.log(chalk.yellow('Deployment possible but may have issues.'));
|
|
738
|
+
return {
|
|
739
|
+
...assessment,
|
|
740
|
+
deployable: true,
|
|
741
|
+
warnings: warningGaps.length
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
console.log(chalk.green('ā
Deployment assessment passed - no blockers detected'));
|
|
745
|
+
return {
|
|
746
|
+
...assessment,
|
|
747
|
+
deployable: true
|
|
748
|
+
};
|
|
749
|
+
} catch (error) {
|
|
750
|
+
console.log(chalk.red(`ā Pre-deploy assessment failed: ${error.message}`));
|
|
751
|
+
throw error;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Deploy service with assessment integration
|
|
757
|
+
*/
|
|
758
|
+
async deployService(deployOptions) {
|
|
759
|
+
console.log(chalk.cyan('š Deploying Service with Assessment Integration...'));
|
|
760
|
+
const {
|
|
761
|
+
skipAssessment = false,
|
|
762
|
+
forceDeployment = false,
|
|
763
|
+
saveAssessment = false
|
|
764
|
+
} = deployOptions;
|
|
765
|
+
try {
|
|
766
|
+
// Run pre-deploy assessment unless skipped
|
|
767
|
+
if (!skipAssessment) {
|
|
768
|
+
console.log(chalk.yellow('š Running Pre-Deploy Assessment...'));
|
|
769
|
+
const assessmentResult = await this.runPreDeployAssessment(deployOptions);
|
|
770
|
+
|
|
771
|
+
// Check if deployment should be blocked
|
|
772
|
+
if (!assessmentResult.deployable && !forceDeployment) {
|
|
773
|
+
console.log(chalk.red('Deployment blocked by assessment. Use --force to override.'));
|
|
774
|
+
return {
|
|
775
|
+
success: false,
|
|
776
|
+
reason: 'Assessment blocked deployment',
|
|
777
|
+
assessment: assessmentResult
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
if (assessmentResult.warnings && !forceDeployment) {
|
|
781
|
+
const proceed = await this.confirmDeploymentWithAssessment(assessmentResult);
|
|
782
|
+
if (!proceed) {
|
|
783
|
+
console.log(chalk.yellow('Deployment cancelled by user.'));
|
|
784
|
+
return {
|
|
785
|
+
success: false,
|
|
786
|
+
reason: 'User cancelled deployment',
|
|
787
|
+
assessment: assessmentResult
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// Save assessment results if requested
|
|
793
|
+
if (saveAssessment) {
|
|
794
|
+
await this.saveAssessmentResults(assessmentResult, deployOptions.servicePath || this.outputPath);
|
|
795
|
+
}
|
|
796
|
+
deployOptions.assessment = assessmentResult;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// Execute deployment
|
|
800
|
+
console.log(chalk.yellow('āļø Executing Deployment...'));
|
|
801
|
+
const deployResult = await this.executeDeployment(deployOptions);
|
|
802
|
+
console.log(chalk.green('ā
Deployment completed successfully'));
|
|
803
|
+
return {
|
|
804
|
+
success: true,
|
|
805
|
+
deployment: deployResult
|
|
806
|
+
};
|
|
807
|
+
} catch (error) {
|
|
808
|
+
console.log(chalk.red(`ā Deployment failed: ${error.message}`));
|
|
809
|
+
throw error;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Execute the actual deployment process
|
|
815
|
+
*/
|
|
816
|
+
async executeDeployment(deployOptions) {
|
|
817
|
+
// This would integrate with actual deployment logic
|
|
818
|
+
// For now, return a mock successful result
|
|
819
|
+
console.log(chalk.gray(' ⢠Validating deployment configuration...'));
|
|
820
|
+
console.log(chalk.gray(' ⢠Uploading worker script...'));
|
|
821
|
+
console.log(chalk.gray(' ⢠Configuring routes...'));
|
|
822
|
+
console.log(chalk.gray(' ⢠Deployment completed'));
|
|
823
|
+
return {
|
|
824
|
+
workerId: 'mock-worker-id',
|
|
825
|
+
deploymentId: 'mock-deployment-id',
|
|
826
|
+
urls: {
|
|
827
|
+
worker: `https://${deployOptions.workerName || 'worker'}.example.com`,
|
|
828
|
+
api: `https://${deployOptions.serviceName || 'service'}.example.com`
|
|
829
|
+
},
|
|
830
|
+
timestamp: new Date().toISOString()
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Confirm deployment with assessment warnings
|
|
836
|
+
*/
|
|
837
|
+
async confirmDeploymentWithAssessment(assessmentResult) {
|
|
838
|
+
if (!this.interactive) {
|
|
839
|
+
return true; // Auto-confirm in non-interactive mode
|
|
840
|
+
}
|
|
841
|
+
if (!assessmentResult || !assessmentResult.gapAnalysis) {
|
|
842
|
+
return true; // Auto-confirm if no assessment result
|
|
843
|
+
}
|
|
844
|
+
const warnings = (assessmentResult.gapAnalysis.missing || []).filter(gap => gap.priority === 'warning');
|
|
845
|
+
const recommendations = (assessmentResult.recommendations || []).slice(0, 3);
|
|
846
|
+
console.log(chalk.yellow('\nā ļø Assessment detected warnings:'));
|
|
847
|
+
warnings.forEach(warning => {
|
|
848
|
+
console.log(chalk.yellow(` ⢠${warning.capability}: ${warning.reason}`));
|
|
849
|
+
});
|
|
850
|
+
if (recommendations.length > 0) {
|
|
851
|
+
console.log(chalk.blue('\nš” Recommendations:'));
|
|
852
|
+
recommendations.forEach(rec => {
|
|
853
|
+
console.log(chalk.blue(` ⢠${rec.action}`));
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
const proceed = await this.confirmationHandler.promptHandler.confirm('\nContinue with deployment despite warnings?', true);
|
|
857
|
+
return proceed;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Save assessment results for post-deploy reference
|
|
862
|
+
*/
|
|
863
|
+
async saveAssessmentResults(assessment, servicePath) {
|
|
864
|
+
const assessmentFile = path.join(servicePath, '.clodo-assessment.json');
|
|
865
|
+
try {
|
|
866
|
+
await fs.writeFile(assessmentFile, JSON.stringify({
|
|
867
|
+
...assessment,
|
|
868
|
+
savedAt: new Date().toISOString(),
|
|
869
|
+
version: '1.0'
|
|
870
|
+
}, null, 2));
|
|
871
|
+
console.log(chalk.gray(` ⢠Assessment results saved to ${assessmentFile}`));
|
|
872
|
+
} catch (error) {
|
|
873
|
+
console.log(chalk.yellow(`ā ļø Failed to save assessment results: ${error.message}`));
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* Load previous assessment results
|
|
879
|
+
*/
|
|
880
|
+
async loadPreviousAssessment(servicePath) {
|
|
881
|
+
const assessmentFile = path.join(servicePath, '.clodo-assessment.json');
|
|
882
|
+
try {
|
|
883
|
+
const data = await fs.readFile(assessmentFile, 'utf8');
|
|
884
|
+
const assessment = JSON.parse(data);
|
|
885
|
+
console.log(chalk.gray(` ⢠Loaded previous assessment from ${assessmentFile}`));
|
|
886
|
+
return assessment;
|
|
887
|
+
} catch (error) {
|
|
888
|
+
console.log(chalk.yellow(`ā ļø No previous assessment found: ${error.message}`));
|
|
889
|
+
return null;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Create readline interface for interactive prompts
|
|
895
|
+
*/
|
|
896
|
+
createReadlineInterface() {
|
|
897
|
+
// This would create a readline interface for interactive prompts
|
|
898
|
+
// For now, return null as this is handled by the confirmation handler
|
|
899
|
+
return null;
|
|
900
|
+
}
|
|
633
901
|
}
|
|
@@ -4,4 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export { ServiceCreator, createService } from './ServiceCreator.js';
|
|
7
|
-
export { ServiceInitializer, initializeService } from './ServiceInitializer.js';
|
|
7
|
+
export { ServiceInitializer, initializeService } from './ServiceInitializer.js';
|
|
8
|
+
export { AssessmentCache } from './AssessmentCache.js';
|
|
9
|
+
export { CapabilityAssessmentEngine } from './CapabilityAssessmentEngine.js';
|
|
10
|
+
export { ServiceAutoDiscovery } from './ServiceAutoDiscovery.js';
|
|
@@ -34,7 +34,20 @@ export class CloudflareAPI {
|
|
|
34
34
|
const data = await response.json();
|
|
35
35
|
if (!response.ok) {
|
|
36
36
|
const errorMsg = data.errors?.[0]?.message || 'Unknown error';
|
|
37
|
-
|
|
37
|
+
const statusCode = response.status;
|
|
38
|
+
|
|
39
|
+
// Provide specific guidance for common authentication/permission errors
|
|
40
|
+
if (statusCode === 401) {
|
|
41
|
+
throw new Error(`Cloudflare API authentication failed (401). Your API token may be invalid or expired. Please check your token at https://dash.cloudflare.com/profile/api-tokens`);
|
|
42
|
+
}
|
|
43
|
+
if (statusCode === 403) {
|
|
44
|
+
// Check if this is a D1-related endpoint to provide specific guidance
|
|
45
|
+
if (endpoint.includes('/d1/')) {
|
|
46
|
+
throw new Error(`Cloudflare API permission denied (403). Your API token lacks D1 database permissions. Required permissions: 'Cloudflare D1:Edit'. Update your token at https://dash.cloudflare.com/profile/api-tokens`);
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`Cloudflare API permission denied (403). Your API token lacks required permissions for this operation. Please check your token permissions at https://dash.cloudflare.com/profile/api-tokens`);
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`Cloudflare API error: ${errorMsg} (${statusCode})`);
|
|
38
51
|
}
|
|
39
52
|
if (!data.success) {
|
|
40
53
|
const errorMsg = data.errors?.[0]?.message || 'Request failed';
|
|
@@ -65,6 +78,33 @@ export class CloudflareAPI {
|
|
|
65
78
|
}
|
|
66
79
|
}
|
|
67
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Check if API token has D1 database permissions
|
|
83
|
+
* @returns {Promise<Object>} Permission check result
|
|
84
|
+
*/
|
|
85
|
+
async checkD1Permissions() {
|
|
86
|
+
try {
|
|
87
|
+
// Try to list D1 databases - this will fail if no D1 permissions
|
|
88
|
+
// We use a dummy account ID that should fail safely if permissions are missing
|
|
89
|
+
await this.request('/accounts/dummy/d1/database');
|
|
90
|
+
return {
|
|
91
|
+
hasPermission: true
|
|
92
|
+
};
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (error.message.includes('403') || error.message.includes('permission denied')) {
|
|
95
|
+
return {
|
|
96
|
+
hasPermission: false,
|
|
97
|
+
error: 'API token lacks D1 database permissions. Required: Cloudflare D1:Edit'
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// If it's a different error (like invalid account), assume permissions are OK
|
|
101
|
+
// The actual permission check happens during real operations
|
|
102
|
+
return {
|
|
103
|
+
hasPermission: true
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
68
108
|
/**
|
|
69
109
|
* List all zones (domains) accessible with this API token
|
|
70
110
|
* @param {Object} options - Query options
|
|
@@ -15,9 +15,13 @@
|
|
|
15
15
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'fs';
|
|
16
16
|
import { resolve, join } from 'path';
|
|
17
17
|
import { getDirname } from '../esm-helper.js';
|
|
18
|
-
import { createLogger } from '../index.js';
|
|
19
18
|
const __dirname = getDirname(import.meta.url, 'src/utils/config');
|
|
20
|
-
|
|
19
|
+
|
|
20
|
+
// Simple inline logger to avoid circular dependency with index.js
|
|
21
|
+
const logger = {
|
|
22
|
+
info: (message, ...args) => console.log(`[UnifiedConfigManager] ${message}`, ...args),
|
|
23
|
+
error: (message, ...args) => console.error(`[UnifiedConfigManager] ${message}`, ...args)
|
|
24
|
+
};
|
|
21
25
|
|
|
22
26
|
/**
|
|
23
27
|
* UnifiedConfigManager
|
|
@@ -19,11 +19,13 @@ export class WranglerConfigManager {
|
|
|
19
19
|
this.projectRoot = dirname(options);
|
|
20
20
|
this.dryRun = false;
|
|
21
21
|
this.verbose = false;
|
|
22
|
+
this.accountId = null;
|
|
22
23
|
} else {
|
|
23
24
|
this.projectRoot = options.projectRoot || process.cwd();
|
|
24
25
|
this.configPath = options.configPath || join(this.projectRoot, 'wrangler.toml');
|
|
25
26
|
this.dryRun = options.dryRun || false;
|
|
26
27
|
this.verbose = options.verbose || false;
|
|
28
|
+
this.accountId = options.accountId || null;
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
|
|
@@ -52,6 +54,19 @@ export class WranglerConfigManager {
|
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Check if wrangler.toml file exists
|
|
59
|
+
* @returns {Promise<boolean>} True if file exists, false otherwise
|
|
60
|
+
*/
|
|
61
|
+
async exists() {
|
|
62
|
+
try {
|
|
63
|
+
await access(this.configPath, constants.F_OK);
|
|
64
|
+
return true;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
55
70
|
/**
|
|
56
71
|
* Write configuration back to wrangler.toml
|
|
57
72
|
* @param {Object} config - Configuration object to write
|
|
@@ -82,16 +97,26 @@ export class WranglerConfigManager {
|
|
|
82
97
|
}
|
|
83
98
|
|
|
84
99
|
/**
|
|
85
|
-
*
|
|
86
|
-
* @
|
|
100
|
+
* Set account_id in wrangler.toml
|
|
101
|
+
* @param {string} accountId - Cloudflare account ID
|
|
102
|
+
* @returns {Promise<boolean>} True if account_id was set
|
|
87
103
|
*/
|
|
88
|
-
async
|
|
89
|
-
|
|
90
|
-
await access(this.configPath, constants.F_OK);
|
|
91
|
-
return true;
|
|
92
|
-
} catch {
|
|
104
|
+
async setAccountId(accountId) {
|
|
105
|
+
if (!accountId) {
|
|
93
106
|
return false;
|
|
94
107
|
}
|
|
108
|
+
const config = await this.readConfig();
|
|
109
|
+
if (config.account_id === accountId) {
|
|
110
|
+
if (this.verbose) {
|
|
111
|
+
console.log(` ā account_id already set to ${accountId}`);
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
console.log(` š Setting account_id to ${accountId} in wrangler.toml`);
|
|
116
|
+
config.account_id = accountId;
|
|
117
|
+
await this.writeConfig(config);
|
|
118
|
+
console.log(` ā
account_id updated in wrangler.toml`);
|
|
119
|
+
return true;
|
|
95
120
|
}
|
|
96
121
|
|
|
97
122
|
/**
|
package/package.json
CHANGED
|
@@ -18,9 +18,9 @@ export const domains = {
|
|
|
18
18
|
accountId: process.env.CLOUDFLARE_ACCOUNT_ID || '', // Configure in setup
|
|
19
19
|
zoneId: process.env.CLOUDFLARE_ZONE_ID || '', // Configure in setup
|
|
20
20
|
domains: {
|
|
21
|
-
production: process.env.PRODUCTION_DOMAIN || '
|
|
22
|
-
staging: process.env.STAGING_DOMAIN || 'staging
|
|
23
|
-
development: process.env.DEVELOPMENT_DOMAIN || 'dev
|
|
21
|
+
production: process.env.PRODUCTION_DOMAIN || '{{SERVICE_NAME}}.{{DOMAIN_NAME}}',
|
|
22
|
+
staging: process.env.STAGING_DOMAIN || '{{SERVICE_NAME}}-staging.{{DOMAIN_NAME}}',
|
|
23
|
+
development: process.env.DEVELOPMENT_DOMAIN || '{{SERVICE_NAME}}-dev.{{DOMAIN_NAME}}'
|
|
24
24
|
},
|
|
25
25
|
services: [
|
|
26
26
|
'{{SERVICE_NAME}}'
|