@tamyla/clodo-framework 3.1.21 → 3.1.23

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.
Files changed (150) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +283 -1
  3. package/dist/{bin → cli}/clodo-service.js +47 -15
  4. package/dist/cli/commands/assess.js +183 -0
  5. package/dist/{bin → cli}/commands/create.js +5 -5
  6. package/dist/{bin → cli}/commands/deploy.js +122 -90
  7. package/dist/{bin → cli}/commands/diagnose.js +5 -5
  8. package/dist/cli/commands/helpers/deployment-ui.js +138 -0
  9. package/dist/cli/commands/helpers/deployment-verification.js +250 -0
  10. package/dist/cli/commands/helpers/error-recovery.js +80 -0
  11. package/dist/cli/commands/helpers/resource-detection.js +113 -0
  12. package/dist/{bin → cli}/commands/helpers.js +0 -28
  13. package/dist/cli/commands/init-config.js +57 -0
  14. package/dist/{bin → cli}/commands/update.js +5 -5
  15. package/dist/{bin → cli}/commands/validate.js +5 -5
  16. package/dist/cli/security-cli.js +118 -0
  17. package/dist/config/FeatureManager.js +6 -0
  18. package/dist/config/clodo-create.example.json +26 -0
  19. package/dist/config/clodo-deploy.example.json +41 -0
  20. package/dist/config/clodo-update.example.json +46 -0
  21. package/dist/config/clodo-validate.example.json +41 -0
  22. package/dist/config/customers/template/development.env.template +37 -0
  23. package/dist/config/customers/template/production.env.template +39 -0
  24. package/dist/config/customers/template/staging.env.template +37 -0
  25. package/dist/config/customers.js +28 -26
  26. package/dist/config/domain-examples/README.md +464 -0
  27. package/dist/config/domain-examples/environment-mapped.json +168 -0
  28. package/dist/config/domain-examples/multi-domain.json +144 -0
  29. package/dist/config/domain-examples/single-domain.json +50 -0
  30. package/dist/config/examples +12 -0
  31. package/dist/config/features.js +61 -0
  32. package/dist/config/staging-deployment.json +60 -0
  33. package/dist/config/validation-config.json +347 -0
  34. package/dist/deployment/wrangler-deployer.js +1 -1
  35. package/dist/{bin → lib}/deployment/modules/DeploymentOrchestrator.js +2 -2
  36. package/dist/{bin → lib}/deployment/modules/EnvironmentManager.js +2 -2
  37. package/dist/lib/deployment/orchestration/EnterpriseOrchestrator.js +21 -0
  38. package/dist/lib/shared/cache/configuration-cache.js +82 -0
  39. package/dist/{bin → lib}/shared/cloudflare/domain-discovery.js +1 -1
  40. package/dist/{bin → lib}/shared/cloudflare/domain-manager.js +1 -1
  41. package/dist/{bin → lib}/shared/cloudflare/index.js +1 -1
  42. package/dist/{bin → lib}/shared/cloudflare/ops.js +10 -8
  43. package/dist/{bin → lib}/shared/config/ConfigurationManager.js +23 -1
  44. package/dist/{bin → lib}/shared/config/command-config-manager.js +19 -3
  45. package/dist/{bin → lib}/shared/config/index.js +1 -1
  46. package/dist/{bin → lib}/shared/deployment/credential-collector.js +30 -7
  47. package/dist/lib/shared/deployment/index.js +10 -0
  48. package/dist/lib/shared/deployment/rollback-manager.js +7 -0
  49. package/dist/lib/shared/deployment/utilities/d1-error-recovery.js +177 -0
  50. package/dist/{bin → lib}/shared/deployment/validator.js +40 -10
  51. package/dist/lib/shared/deployment/workflows/deployment-summary.js +214 -0
  52. package/dist/lib/shared/deployment/workflows/interactive-confirmation.js +188 -0
  53. package/dist/lib/shared/deployment/workflows/interactive-database-workflow.js +234 -0
  54. package/dist/lib/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
  55. package/dist/lib/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
  56. package/dist/lib/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
  57. package/dist/lib/shared/deployment/workflows/interactive-validation.js +218 -0
  58. package/dist/lib/shared/error-handling/error-classifier.js +46 -0
  59. package/dist/{bin → lib}/shared/monitoring/health-checker.js +129 -1
  60. package/dist/{bin → lib}/shared/monitoring/memory-manager.js +17 -6
  61. package/dist/{bin → lib}/shared/routing/domain-router.js +1 -1
  62. package/dist/lib/shared/utils/deployment-validator.js +97 -0
  63. package/dist/{bin → lib}/shared/utils/formatters.js +10 -0
  64. package/dist/{bin → lib}/shared/utils/index.js +13 -1
  65. package/dist/{bin → lib}/shared/utils/interactive-prompts.js +34 -18
  66. package/dist/{bin → lib}/shared/utils/progress-manager.js +2 -2
  67. package/dist/lib/shared/utils/progress-spinner.js +53 -0
  68. package/dist/lib/shared/utils/sensitive-redactor.js +91 -0
  69. package/dist/{bin → lib}/shared/validation/ValidationRegistry.js +1 -1
  70. package/dist/migration/MigrationAdapters.js +50 -4
  71. package/dist/orchestration/cross-domain-coordinator.js +5 -5
  72. package/dist/orchestration/multi-domain-orchestrator.js +63 -22
  73. package/dist/security/index.js +2 -2
  74. package/dist/security/patterns/insecure-patterns.js +1 -1
  75. package/dist/service-management/ConfirmationEngine.js +1 -1
  76. package/dist/service-management/ErrorTracker.js +1 -1
  77. package/dist/service-management/InputCollector.js +1 -1
  78. package/dist/service-management/ServiceCreator.js +11 -255
  79. package/dist/service-management/ServiceOrchestrator.js +0 -2
  80. package/dist/service-management/generators/testing/UnitTestsGenerator.js +4 -4
  81. package/dist/service-management/index.js +1 -1
  82. package/dist/utils/cloudflare/ops.js +1 -1
  83. package/dist/utils/constants.js +102 -0
  84. package/dist/utils/deployment/wrangler-config-manager.js +215 -48
  85. package/dist/utils/file-manager.js +1 -1
  86. package/dist/utils/formatters.js +1 -1
  87. package/dist/utils/framework-config.js +2 -2
  88. package/dist/utils/interactive-prompts.js +10 -59
  89. package/dist/utils/logger.js +1 -1
  90. package/dist/version/VersionDetector.js +99 -9
  91. package/dist/worker/integration.js +1 -1
  92. package/package.json +10 -10
  93. package/dist/bin/clodo-service-old.js +0 -868
  94. package/dist/bin/clodo-service-test.js +0 -10
  95. package/dist/bin/commands/assess.js +0 -91
  96. package/dist/bin/database/enterprise-db-manager.js +0 -457
  97. package/dist/bin/deployment/enterprise-deploy.js +0 -877
  98. package/dist/bin/deployment/master-deploy.js +0 -1376
  99. package/dist/bin/deployment/modular-enterprise-deploy.js +0 -466
  100. package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
  101. package/dist/bin/deployment/test-interactive-utils.js +0 -66
  102. package/dist/bin/portfolio/portfolio-manager.js +0 -487
  103. package/dist/bin/security/security-cli.js +0 -108
  104. package/dist/bin/service-management/create-service.js +0 -122
  105. package/dist/bin/service-management/init-service.js +0 -79
  106. package/dist/bin/shared/deployment/index.js +0 -10
  107. package/dist/bin/shared/deployment/rollback-manager.js +0 -523
  108. package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
  109. package/dist/service-management/ServiceInitializer.js +0 -453
  110. /package/dist/{bin → lib}/database/deployment-db-manager.js +0 -0
  111. /package/dist/{bin → lib}/database/wrangler-d1-manager.js +0 -0
  112. /package/dist/{bin → lib}/deployment/modules/DeploymentConfiguration.js +0 -0
  113. /package/dist/{bin → lib}/deployment/modules/MonitoringIntegration.js +0 -0
  114. /package/dist/{bin → lib}/deployment/modules/ValidationManager.js +0 -0
  115. /package/dist/{bin → lib}/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -0
  116. /package/dist/{bin → lib}/deployment/orchestration/PortfolioOrchestrator.js +0 -0
  117. /package/dist/{bin → lib}/deployment/orchestration/SingleServiceOrchestrator.js +0 -0
  118. /package/dist/{bin → lib}/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -0
  119. /package/dist/{bin → lib}/shared/config/cache.js +0 -0
  120. /package/dist/{bin → lib}/shared/config/cloudflare-service-validator.js +0 -0
  121. /package/dist/{bin → lib}/shared/config/manager.js +0 -0
  122. /package/dist/{bin → lib}/shared/config/manifest-loader.js +0 -0
  123. /package/dist/{bin → lib}/shared/database/connection-manager.js +0 -0
  124. /package/dist/{bin → lib}/shared/database/index.js +0 -0
  125. /package/dist/{bin → lib}/shared/database/orchestrator.js +0 -0
  126. /package/dist/{bin → lib}/shared/deployment/auditor.js +0 -0
  127. /package/dist/{bin → lib}/shared/index.js +0 -0
  128. /package/dist/{bin → lib}/shared/logging/Logger.js +0 -0
  129. /package/dist/{bin → lib}/shared/monitoring/index.js +0 -0
  130. /package/dist/{bin → lib}/shared/monitoring/production-monitor.js +0 -0
  131. /package/dist/{bin → lib}/shared/production-tester/api-tester.js +0 -0
  132. /package/dist/{bin → lib}/shared/production-tester/auth-tester.js +0 -0
  133. /package/dist/{bin → lib}/shared/production-tester/core.js +0 -0
  134. /package/dist/{bin → lib}/shared/production-tester/database-tester.js +0 -0
  135. /package/dist/{bin → lib}/shared/production-tester/index.js +0 -0
  136. /package/dist/{bin → lib}/shared/production-tester/load-tester.js +0 -0
  137. /package/dist/{bin → lib}/shared/production-tester/performance-tester.js +0 -0
  138. /package/dist/{bin → lib}/shared/security/api-token-manager.js +0 -0
  139. /package/dist/{bin → lib}/shared/security/index.js +0 -0
  140. /package/dist/{bin → lib}/shared/security/secret-generator.js +0 -0
  141. /package/dist/{bin → lib}/shared/security/secure-token-manager.js +0 -0
  142. /package/dist/{bin → lib}/shared/utils/ErrorHandler.js +0 -0
  143. /package/dist/{bin → lib}/shared/utils/cli-options.js +0 -0
  144. /package/dist/{bin → lib}/shared/utils/config-loader.js +0 -0
  145. /package/dist/{bin → lib}/shared/utils/error-recovery.js +0 -0
  146. /package/dist/{bin → lib}/shared/utils/file-manager.js +0 -0
  147. /package/dist/{bin → lib}/shared/utils/graceful-shutdown-manager.js +0 -0
  148. /package/dist/{bin → lib}/shared/utils/interactive-utils.js +0 -0
  149. /package/dist/{bin → lib}/shared/utils/output-formatter.js +0 -0
  150. /package/dist/{bin → lib}/shared/utils/rate-limiter.js +0 -0
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Deployment Verification Helpers (UI Wrapper)
3
+ * Provides UI-specific deployment verification by delegating to shared infrastructure
4
+ */
5
+
6
+ import { verifyWorkerDeployment, healthCheckWithBackoff, checkHealth } from '../../lib/shared/monitoring/health-checker.js';
7
+ import chalk from 'chalk';
8
+ import readline from 'readline';
9
+
10
+ /**
11
+ * Run comprehensive post-deployment verification
12
+ * @param {string} serviceName - Name of the service
13
+ * @param {Object} deploymentResult - Result from deployment
14
+ * @param {Object} credentials - Cloudflare credentials
15
+ * @param {Object} options - Verification options
16
+ * @returns {Promise<Object>} Verification result
17
+ */
18
+ export async function runPostDeploymentVerification(serviceName, deploymentResult, credentials, options = {}) {
19
+ console.log('\n🔍 Post-deployment Verification');
20
+
21
+ // Skip health checks in dry-run mode with clear explanation
22
+ if (options.dryRun) {
23
+ console.log(chalk.blue(' ℹ️ Health checks skipped in dry-run mode'));
24
+ console.log(chalk.gray(' 💡 Run without --dry-run to deploy and verify the worker'));
25
+ return {
26
+ success: true,
27
+ healthCheckPassed: null,
28
+ reason: 'dry-run-skipped'
29
+ };
30
+ }
31
+ try {
32
+ // Wait for deployment to propagate
33
+ console.log(' ⏳ Waiting for deployment to propagate...');
34
+ await new Promise(resolve => setTimeout(resolve, 5000));
35
+
36
+ // Run health checks with enhanced URL discovery
37
+ console.log(' 🏥 Running health checks...');
38
+
39
+ // Try multiple sources for worker URL (in priority order)
40
+ const workerUrl = await discoverWorkerUrl(deploymentResult, credentials, options);
41
+ if (workerUrl) {
42
+ console.log(chalk.gray(` 🔗 Testing: ${workerUrl}`));
43
+ const health = await checkHealth(workerUrl).catch(err => {
44
+ console.log(chalk.yellow(` ⚠️ Health check failed: ${err.message}`));
45
+ return {
46
+ status: 'unknown',
47
+ error: err.message
48
+ };
49
+ });
50
+ if (health && health.status === 'ok') {
51
+ console.log(` ✅ Deployment verified: ${health.framework?.models?.length || 0} models active`);
52
+ return {
53
+ success: true,
54
+ healthCheckPassed: true,
55
+ healthData: health,
56
+ url: workerUrl
57
+ };
58
+ } else {
59
+ console.log(` ⚠️ Health check returned: ${health?.status || 'unknown'}`);
60
+ if (health.error) {
61
+ console.log(chalk.gray(` Error: ${health.error}`));
62
+ }
63
+ // Don't fail deployment for health check issues
64
+ return {
65
+ success: true,
66
+ healthCheckPassed: false,
67
+ healthData: health,
68
+ url: workerUrl
69
+ };
70
+ }
71
+ } else {
72
+ console.log(chalk.yellow(' ⚠️ No worker URL could be determined'));
73
+ console.log(chalk.gray(' 💡 To fix: Add route in wrangler.toml or check Cloudflare dashboard'));
74
+ return {
75
+ success: true,
76
+ healthCheckPassed: null,
77
+ reason: 'no-url-discovered'
78
+ };
79
+ }
80
+ } catch (error) {
81
+ console.log(chalk.yellow(` ⚠️ Post-deployment verification warning: ${error.message}`));
82
+ // Don't fail overall deployment for verification issues
83
+ return {
84
+ success: true,
85
+ healthCheckPassed: false,
86
+ error: error.message
87
+ };
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Discover worker URL from multiple sources
93
+ * Priority: deploymentResult.url > workers.dev convention > Cloudflare API discovery
94
+ * @param {Object} deploymentResult - Result from deployment
95
+ * @param {Object} credentials - Cloudflare credentials
96
+ * @param {Object} options - Discovery options
97
+ * @returns {Promise<string|null>} Discovered worker URL or null
98
+ */
99
+ async function discoverWorkerUrl(deploymentResult, credentials, options = {}) {
100
+ // Source 1: Direct URL from deployment result
101
+ if (deploymentResult?.url) {
102
+ return deploymentResult.url;
103
+ }
104
+
105
+ // Source 2: Construct workers.dev URL from credentials
106
+ if (credentials?.cloudflareSettings?.accountId && options.serviceName) {
107
+ const accountHash = credentials.cloudflareSettings.accountId;
108
+ const scriptName = options.serviceName || 'my-worker';
109
+ const workersDevUrl = `https://${scriptName}.${accountHash}.workers.dev`;
110
+
111
+ // Verify this URL exists by checking if we can reach it
112
+ const urlExists = await verifyUrlExists(workersDevUrl);
113
+ if (urlExists) {
114
+ return workersDevUrl;
115
+ }
116
+ }
117
+
118
+ // Source 3: Query Cloudflare API for worker routes
119
+ if (credentials?.cloudflareSettings) {
120
+ const discoveredUrl = await discoverUrlFromCloudflare(credentials.cloudflareSettings, options);
121
+ if (discoveredUrl) {
122
+ return discoveredUrl;
123
+ }
124
+ }
125
+
126
+ // Source 4: Custom domain from options
127
+ if (options.customDomain) {
128
+ return `https://${options.customDomain}`;
129
+ }
130
+ return null;
131
+ }
132
+
133
+ /**
134
+ * Verify if a URL exists (can be reached)
135
+ * @param {string} url - URL to verify
136
+ * @returns {Promise<boolean>} True if URL responds
137
+ */
138
+ async function verifyUrlExists(url) {
139
+ try {
140
+ const response = await fetch(url, {
141
+ method: 'HEAD',
142
+ signal: AbortSignal.timeout(5000)
143
+ });
144
+ return response.ok || response.status === 404; // 404 means route exists but no handler
145
+ } catch {
146
+ return false;
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Discover worker URL from Cloudflare API
152
+ * @param {Object} cloudflareSettings - Cloudflare credentials
153
+ * @param {Object} options - Discovery options
154
+ * @returns {Promise<string|null>} Discovered URL or null
155
+ */
156
+ async function discoverUrlFromCloudflare(cloudflareSettings, options = {}) {
157
+ try {
158
+ const {
159
+ CloudflareAPI
160
+ } = await import('../../utils/cloudflare/api.js');
161
+ const api = new CloudflareAPI(cloudflareSettings.token);
162
+
163
+ // Get worker routes for the zone
164
+ if (cloudflareSettings.zoneId) {
165
+ const response = await api.request(`/zones/${cloudflareSettings.zoneId}/workers/routes`);
166
+ const routes = response.result || [];
167
+ if (routes.length > 0) {
168
+ // Find route for this worker
169
+ const scriptName = options.serviceName || options.workerName;
170
+ const matchingRoute = routes.find(r => r.script && (r.script === scriptName || r.script.includes(scriptName)));
171
+ if (matchingRoute && matchingRoute.pattern) {
172
+ // Convert route pattern to URL (basic conversion)
173
+ const url = matchingRoute.pattern.replace('*', '').replace('//', '//');
174
+ return url.startsWith('http') ? url : `https://${url}`;
175
+ }
176
+ }
177
+ }
178
+
179
+ // Fallback: Get worker info from scripts API
180
+ const scriptsResponse = await api.request(`/accounts/${cloudflareSettings.accountId}/workers/scripts`);
181
+ const scripts = scriptsResponse.result || [];
182
+ const scriptName = options.serviceName || options.workerName;
183
+ const script = scripts.find(s => s.id === scriptName || s.id.includes(scriptName));
184
+ if (script && script.url) {
185
+ return script.url;
186
+ }
187
+ } catch (error) {
188
+ // API discovery failed - not critical
189
+ if (options.verbose) {
190
+ console.log(chalk.gray(` Could not discover URL from Cloudflare API: ${error.message}`));
191
+ }
192
+ }
193
+ return null;
194
+ }
195
+
196
+ /**
197
+ * Verify deployment via Cloudflare API (UI wrapper)
198
+ * @param {string} workerName - Name of the worker to verify
199
+ * @param {Object} credentials - Cloudflare credentials
200
+ * @param {Object} options - Verification options
201
+ * @returns {Promise<Object>} Verification result
202
+ */
203
+ export async function verifyDeployment(workerName, credentials, options = {}) {
204
+ // Delegate to shared infrastructure
205
+ return await verifyWorkerDeployment(workerName, credentials, options);
206
+ }
207
+
208
+ /**
209
+ * Perform health check with user permission prompt
210
+ * @param {string} baseUrl - Base URL to check
211
+ * @param {Object} options - Health check options
212
+ * @returns {Promise<Object>} Health check result
213
+ */
214
+ export async function performHealthCheckWithPermission(baseUrl, options = {}) {
215
+ const {
216
+ skipPrompt = false,
217
+ ...healthOptions
218
+ } = options;
219
+ if (!skipPrompt) {
220
+ const permission = await promptForHealthCheck();
221
+ if (!permission) {
222
+ console.log(chalk.yellow('\n⏭️ Health check skipped by user'));
223
+ return {
224
+ healthy: null,
225
+ skipped: true
226
+ };
227
+ }
228
+ }
229
+ console.log(chalk.gray('\n🏥 Running health checks with exponential backoff...\n'));
230
+
231
+ // Delegate to shared infrastructure
232
+ return await healthCheckWithBackoff(baseUrl, healthOptions);
233
+ }
234
+
235
+ /**
236
+ * Prompt user for health check permission
237
+ * @returns {Promise<boolean>} Whether to proceed with health check
238
+ */
239
+ async function promptForHealthCheck() {
240
+ const rl = readline.createInterface({
241
+ input: process.stdin,
242
+ output: process.stdout
243
+ });
244
+ return new Promise(resolve => {
245
+ rl.question(chalk.cyan('\n❓ Run health checks to verify deployment? (Y/n): '), answer => {
246
+ rl.close();
247
+ resolve(!answer || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
248
+ });
249
+ });
250
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Error Recovery Helpers (UI Wrapper)
3
+ * Provides interactive error recovery by delegating to shared error classification
4
+ */
5
+
6
+ import { classifyError, getRecoverySuggestions } from '../../lib/shared/error-handling/error-classifier.js';
7
+ import chalk from 'chalk';
8
+ import readline from 'readline';
9
+
10
+ /**
11
+ * Handle deployment error with contextual recovery options
12
+ * @param {Error} error - The deployment error
13
+ * @param {Object} context - Deployment context
14
+ * @returns {Promise<string>} Recovery action: 'retry', 'dry-run', or 'abort'
15
+ */
16
+ export async function handleDeploymentError(error, context = {}) {
17
+ // Classify error using shared infrastructure
18
+ const errorType = classifyError(error);
19
+ const suggestions = getRecoverySuggestions(errorType);
20
+
21
+ // Display error with contextual information
22
+ console.error(chalk.red(`\n❌ Deployment failed: ${error.message}\n`));
23
+ console.log(chalk.yellow(`Error Type: ${errorType.toUpperCase()}\n`));
24
+
25
+ // Display recovery suggestions
26
+ console.log(chalk.cyan('💡 Suggestions:'));
27
+ suggestions.forEach(suggestion => {
28
+ console.log(chalk.gray(` • ${suggestion}`));
29
+ });
30
+
31
+ // Prompt for recovery action
32
+ return await promptForRecoveryAction();
33
+ }
34
+
35
+ /**
36
+ * Prompt user for recovery action
37
+ * @returns {Promise<string>} Recovery action
38
+ */
39
+ async function promptForRecoveryAction() {
40
+ const rl = readline.createInterface({
41
+ input: process.stdin,
42
+ output: process.stdout
43
+ });
44
+ console.log(chalk.cyan('\nRecovery Options:'));
45
+ console.log(chalk.gray(' 1) Retry deployment'));
46
+ console.log(chalk.gray(' 2) Run in dry-run mode to diagnose'));
47
+ console.log(chalk.gray(' 3) Abort'));
48
+ return new Promise(resolve => {
49
+ rl.question(chalk.cyan('\nSelect option (1-3): '), answer => {
50
+ rl.close();
51
+ switch (answer.trim()) {
52
+ case '1':
53
+ resolve('retry');
54
+ break;
55
+ case '2':
56
+ resolve('dry-run');
57
+ break;
58
+ case '3':
59
+ default:
60
+ resolve('abort');
61
+ break;
62
+ }
63
+ });
64
+ });
65
+ }
66
+
67
+ /**
68
+ * Display error summary for non-interactive mode
69
+ * @param {Error} error - The error
70
+ */
71
+ export function displayErrorSummary(error) {
72
+ const errorType = classifyError(error);
73
+ const suggestions = getRecoverySuggestions(errorType);
74
+ console.error(chalk.red(`\n❌ ${error.message}`));
75
+ console.log(chalk.yellow(`\nError Type: ${errorType.toUpperCase()}`));
76
+ console.log(chalk.cyan('\nSuggestions:'));
77
+ suggestions.forEach(suggestion => {
78
+ console.log(chalk.gray(` • ${suggestion}`));
79
+ });
80
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Resource Detection Helpers
3
+ * Detects existing Cloudflare resources (workers, databases) for change tracking
4
+ */
5
+
6
+ import chalk from 'chalk';
7
+
8
+ /**
9
+ * Detect existing resources to show what will change
10
+ * @param {string} serviceName - Name of the service
11
+ * @param {Object} manifest - Service manifest
12
+ * @param {Object} credentials - Cloudflare credentials
13
+ * @param {Object} output - Output formatter
14
+ * @param {boolean} verbose - Verbose logging flag
15
+ * @returns {Promise<Object>} Detected resources
16
+ */
17
+ export async function detectExistingResources(serviceName, manifest, credentials, output, verbose = false) {
18
+ let existingWorker = null;
19
+ let existingDatabases = [];
20
+ let resourceDetectionFailed = false;
21
+ try {
22
+ // Import CloudflareAPI to check existing resources
23
+ const {
24
+ CloudflareAPI
25
+ } = await import('../../utils/cloudflare/api.js');
26
+ const cfApi = new CloudflareAPI(credentials.token);
27
+
28
+ // Check if worker already exists
29
+ const workers = await cfApi.listWorkers(credentials.accountId);
30
+ existingWorker = workers.find(w => w.name === serviceName);
31
+
32
+ // Check existing databases
33
+ existingDatabases = await cfApi.listD1Databases(credentials.accountId);
34
+ } catch (error) {
35
+ resourceDetectionFailed = true;
36
+ if (verbose && output) {
37
+ output.warn(`Could not detect existing resources: ${error.message}`);
38
+ }
39
+ }
40
+ return {
41
+ existingWorker,
42
+ existingDatabases,
43
+ resourceDetectionFailed
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Display deployment plan with change detection badges
49
+ * @param {Object} planData - Deployment plan data
50
+ */
51
+ export function displayDeploymentPlan(planData) {
52
+ const {
53
+ serviceName,
54
+ serviceType,
55
+ selectedDomain,
56
+ environment,
57
+ credentials,
58
+ dryRun,
59
+ existingWorker,
60
+ existingDatabases,
61
+ resourceDetectionFailed,
62
+ manifest
63
+ } = planData;
64
+ console.log(chalk.cyan('\n📋 Deployment Plan:'));
65
+ console.log(chalk.gray('─'.repeat(60)));
66
+ console.log(chalk.white(`Service: ${serviceName}`));
67
+ console.log(chalk.white(`Type: ${serviceType}`));
68
+ console.log(chalk.white(`Domain: ${selectedDomain}`));
69
+ console.log(chalk.white(`Environment: ${environment || 'production'}`));
70
+ console.log(chalk.white(`Account: ${credentials.accountId.substring(0, 8)}...`));
71
+ console.log(chalk.white(`Zone: ${credentials.zoneId.substring(0, 8)}...`));
72
+ console.log(chalk.white(`Deployment Mode: ${dryRun ? 'DRY RUN' : 'LIVE'}`));
73
+ console.log(chalk.gray('─'.repeat(60)));
74
+
75
+ // Show what will be deployed with change badges
76
+ console.log(chalk.cyan('\n📦 Resources:'));
77
+
78
+ // Worker status
79
+ if (existingWorker) {
80
+ const lastModified = existingWorker.modifiedOn ? new Date(existingWorker.modifiedOn).toLocaleString() : 'unknown';
81
+ console.log(chalk.yellow(` [CHANGE] Worker: ${serviceName}`));
82
+ console.log(chalk.gray(` Last deployed: ${lastModified}`));
83
+ } else if (!resourceDetectionFailed) {
84
+ console.log(chalk.green(` [NEW] Worker: ${serviceName}`));
85
+ } else {
86
+ console.log(chalk.white(` [DEPLOY] Worker: ${serviceName}`));
87
+ }
88
+
89
+ // Database status (if manifest has database config)
90
+ if (manifest.database && manifest.database.name) {
91
+ const dbName = manifest.database.name;
92
+ const existingDb = existingDatabases.find(db => db.name === dbName || db.name === `${serviceName}-${dbName}`);
93
+ if (existingDb) {
94
+ console.log(chalk.yellow(` [CHANGE] Database: ${dbName} (binding update)`));
95
+ } else if (!resourceDetectionFailed) {
96
+ console.log(chalk.green(` [NEW] Database: ${dbName}`));
97
+ } else {
98
+ console.log(chalk.white(` [DEPLOY] Database: ${dbName}`));
99
+ }
100
+ }
101
+
102
+ // Routes
103
+ const workersDomain = `${serviceName}.${credentials.accountId}.workers.dev`;
104
+ console.log(chalk.green(` [NEW] Route: https://${workersDomain}`));
105
+ if (selectedDomain && selectedDomain !== 'workers.cloudflare.com') {
106
+ console.log(chalk.yellow(` [MANUAL] Custom domain: ${selectedDomain}`));
107
+ console.log(chalk.gray(` ⚠️ Requires DNS configuration (see post-deployment steps)`));
108
+ }
109
+ console.log(chalk.gray('─'.repeat(60)));
110
+ if (dryRun) {
111
+ console.log(chalk.yellow('\n🔍 DRY RUN MODE - No changes will be made\n'));
112
+ }
113
+ }
@@ -6,34 +6,6 @@ import chalk from 'chalk';
6
6
  import { existsSync, readFileSync } from 'fs';
7
7
  import { join, resolve } from 'path';
8
8
 
9
- /**
10
- * Load JSON configuration file
11
- */
12
- export function loadJsonConfig(configPath) {
13
- try {
14
- const fullPath = resolve(configPath);
15
- if (!existsSync(fullPath)) {
16
- throw new Error(`Configuration file not found: ${fullPath}`);
17
- }
18
- const content = readFileSync(fullPath, 'utf8');
19
- const config = JSON.parse(content);
20
-
21
- // Validate required fields
22
- const required = ['customer', 'environment', 'domainName', 'cloudflareToken'];
23
- const missing = required.filter(field => !config[field]);
24
- if (missing.length > 0) {
25
- throw new Error(`Missing required configuration fields: ${missing.join(', ')}`);
26
- }
27
- console.log(chalk.green(`✅ Loaded configuration from: ${fullPath}`));
28
- return config;
29
- } catch (error) {
30
- if (error instanceof SyntaxError) {
31
- throw new Error(`Invalid JSON in configuration file: ${error.message}`);
32
- }
33
- throw error;
34
- }
35
- }
36
-
37
9
  /**
38
10
  * Show progress indicator for deployment steps
39
11
  */
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Initialize Configuration Command
5
+ * Copies the framework's validation-config.json to the service directory for customization
6
+ */
7
+ import { copyFile, access } from 'fs/promises';
8
+ import { join, dirname } from 'path';
9
+ import { fileURLToPath } from 'url';
10
+ import chalk from 'chalk';
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+
14
+ // Path to framework's bundled config
15
+ const FRAMEWORK_CONFIG_PATH = join(__dirname, '../config/validation-config.json');
16
+
17
+ /**
18
+ * Register the init-config command with the CLI
19
+ */
20
+ export function registerInitConfigCommand(program) {
21
+ program.command('init-config').description('Initialize validation-config.json in your service directory').option('-f, --force', 'Overwrite existing validation-config.json', false).action(handler);
22
+ }
23
+ async function handler(options) {
24
+ const targetPath = join(process.cwd(), 'validation-config.json');
25
+ try {
26
+ // Check if file already exists
27
+ try {
28
+ await access(targetPath);
29
+ if (!options.force) {
30
+ console.log(chalk.yellow('⚠️ validation-config.json already exists.'));
31
+ console.log(chalk.gray(' Use --force to overwrite.'));
32
+ process.exit(0);
33
+ } else {
34
+ console.log(chalk.yellow('🔄 Overwriting existing validation-config.json...'));
35
+ }
36
+ } catch {
37
+ // File doesn't exist, proceed
38
+ }
39
+
40
+ // Copy framework config to service directory
41
+ await copyFile(FRAMEWORK_CONFIG_PATH, targetPath);
42
+ console.log(chalk.green('✅ Successfully initialized validation-config.json'));
43
+ console.log(chalk.gray('\n📝 Configuration file details:'));
44
+ console.log(chalk.gray(` Location: ${targetPath}`));
45
+ console.log(chalk.gray('\n💡 Usage:'));
46
+ console.log(chalk.gray(' • Customize timing values (timeouts, intervals)'));
47
+ console.log(chalk.gray(' • Add service-specific endpoints for validation'));
48
+ console.log(chalk.gray(' • Configure platform-specific commands'));
49
+ console.log(chalk.gray(' • Set environment-specific requirements'));
50
+ console.log(chalk.gray('\n⚠️ Note: Most services don\'t need this file.'));
51
+ console.log(chalk.gray(' The framework provides sensible defaults.'));
52
+ } catch (error) {
53
+ console.error(chalk.red('❌ Failed to initialize configuration:'));
54
+ console.error(chalk.red(` ${error.message}`));
55
+ process.exit(1);
56
+ }
57
+ }
@@ -3,16 +3,16 @@
3
3
  */
4
4
 
5
5
  import chalk from 'chalk';
6
- import { ServiceOrchestrator } from "../../service-management/ServiceOrchestrator.js";
7
- import { StandardOptions } from '../shared/utils/cli-options.js';
8
- import { ConfigLoader } from '../shared/utils/config-loader.js';
6
+ import { ServiceOrchestrator } from '../service-management/ServiceOrchestrator.js';
7
+ import { StandardOptions } from '../lib/shared/utils/cli-options.js';
8
+ import { ConfigLoader } from '../lib/shared/utils/config-loader.js';
9
9
  export function registerUpdateCommand(program) {
10
10
  const command = program.command('update [service-path]').description('Update an existing service configuration').option('-i, --interactive', 'Run in interactive mode to select what to update').option('--domain-name <domain>', 'Update domain name').option('--cloudflare-token <token>', 'Update Cloudflare API token').option('--cloudflare-account-id <id>', 'Update Cloudflare account ID').option('--cloudflare-zone-id <id>', 'Update Cloudflare zone ID').option('--environment <env>', 'Update target environment: development, staging, production').option('--add-feature <feature>', 'Add a feature flag').option('--remove-feature <feature>', 'Remove a feature flag').option('--regenerate-configs', 'Regenerate all configuration files').option('--fix-errors', 'Attempt to fix common configuration errors').option('--preview', 'Show what would be changed without applying').option('--force', 'Skip confirmation prompts');
11
11
 
12
12
  // Add standard options (--verbose, --quiet, --json, --no-color, --config-file)
13
13
  StandardOptions.define(command).action(async (servicePath, options) => {
14
14
  try {
15
- const output = new (await import('../shared/utils/output-formatter.js')).OutputFormatter(options);
15
+ const output = new (await import('../lib/shared/utils/output-formatter.js')).OutputFormatter(options);
16
16
  const configLoader = new ConfigLoader({
17
17
  verbose: options.verbose,
18
18
  quiet: options.quiet,
@@ -60,7 +60,7 @@ export function registerUpdateCommand(program) {
60
60
  }
61
61
  output.success('Service update completed successfully!');
62
62
  } catch (error) {
63
- const output = new (await import('../shared/utils/output-formatter.js')).OutputFormatter(options || {});
63
+ const output = new (await import('../lib/shared/utils/output-formatter.js')).OutputFormatter(options || {});
64
64
  output.error(`Service update failed: ${error.message}`);
65
65
  if (error.details) {
66
66
  output.warning(`Details: ${error.details}`);
@@ -3,16 +3,16 @@
3
3
  */
4
4
 
5
5
  import chalk from 'chalk';
6
- import { ServiceOrchestrator } from "../../service-management/ServiceOrchestrator.js";
7
- import { StandardOptions } from '../shared/utils/cli-options.js';
8
- import { ConfigLoader } from '../shared/utils/config-loader.js';
6
+ import { ServiceOrchestrator } from '../service-management/ServiceOrchestrator.js';
7
+ import { StandardOptions } from '../lib/shared/utils/cli-options.js';
8
+ import { ConfigLoader } from '../lib/shared/utils/config-loader.js';
9
9
  export function registerValidateCommand(program) {
10
10
  const command = program.command('validate <service-path>').description('Validate an existing service configuration').option('--deep-scan', 'Run comprehensive validation checks').option('--export-report <file>', 'Export validation report to JSON file');
11
11
 
12
12
  // Add standard options (--verbose, --quiet, --json, --no-color, --config-file)
13
13
  StandardOptions.define(command).action(async (servicePath, options) => {
14
14
  try {
15
- const output = new (await import('../shared/utils/output-formatter.js')).OutputFormatter(options);
15
+ const output = new (await import('../lib/shared/utils/output-formatter.js')).OutputFormatter(options);
16
16
  const configLoader = new ConfigLoader({
17
17
  verbose: options.verbose,
18
18
  quiet: options.quiet,
@@ -43,7 +43,7 @@ export function registerValidateCommand(program) {
43
43
  process.exit(1);
44
44
  }
45
45
  } catch (error) {
46
- const output = new (await import('../shared/utils/output-formatter.js')).OutputFormatter(options || {});
46
+ const output = new (await import('../lib/shared/utils/output-formatter.js')).OutputFormatter(options || {});
47
47
  output.error(`Validation failed: ${error.message}`);
48
48
  process.exit(1);
49
49
  }