@tamyla/clodo-framework 3.1.21 → 3.1.22

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 (169) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +53 -0
  3. package/dist/bin/clodo-service.js +47 -15
  4. package/dist/bin/commands/deploy.js +115 -83
  5. package/dist/bin/commands/helpers/deployment-ui.js +138 -0
  6. package/dist/bin/commands/helpers/deployment-verification.js +251 -0
  7. package/dist/bin/commands/helpers/error-recovery.js +80 -0
  8. package/dist/bin/commands/helpers/resource-detection.js +113 -0
  9. package/dist/bin/commands/validate.js +1 -1
  10. package/dist/bin/security/security-cli.js +1 -1
  11. package/dist/bin/shared/cache/configuration-cache.js +82 -0
  12. package/dist/bin/shared/cloudflare/domain-manager.js +1 -1
  13. package/dist/bin/shared/cloudflare/index.js +1 -1
  14. package/dist/bin/shared/cloudflare/ops.js +6 -4
  15. package/dist/bin/shared/config/ConfigurationManager.js +23 -1
  16. package/dist/bin/shared/config/command-config-manager.js +19 -3
  17. package/dist/bin/shared/config/index.js +1 -1
  18. package/dist/bin/shared/deployment/credential-collector.js +30 -7
  19. package/dist/bin/shared/deployment/index.js +2 -2
  20. package/dist/bin/shared/deployment/rollback-manager.js +4 -520
  21. package/dist/bin/shared/deployment/utilities/d1-error-recovery.js +177 -0
  22. package/dist/bin/shared/deployment/validator.js +40 -10
  23. package/dist/bin/shared/deployment/workflows/deployment-summary.js +214 -0
  24. package/dist/bin/shared/deployment/workflows/interactive-confirmation.js +188 -0
  25. package/dist/bin/shared/deployment/workflows/interactive-database-workflow.js +234 -0
  26. package/dist/bin/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
  27. package/dist/bin/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
  28. package/dist/bin/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
  29. package/dist/bin/shared/deployment/workflows/interactive-validation.js +218 -0
  30. package/dist/bin/shared/error-handling/error-classifier.js +46 -0
  31. package/dist/bin/shared/monitoring/health-checker.js +129 -1
  32. package/dist/bin/shared/monitoring/memory-manager.js +17 -6
  33. package/dist/bin/shared/routing/domain-router.js +1 -1
  34. package/dist/bin/shared/utils/deployment-validator.js +97 -0
  35. package/dist/bin/shared/utils/formatters.js +10 -0
  36. package/dist/bin/shared/utils/index.js +13 -1
  37. package/dist/bin/shared/utils/interactive-prompts.js +34 -18
  38. package/dist/bin/shared/utils/progress-manager.js +2 -2
  39. package/dist/bin/shared/utils/progress-spinner.js +53 -0
  40. package/dist/bin/shared/utils/sensitive-redactor.js +91 -0
  41. package/dist/bin/shared/validation/ValidationRegistry.js +1 -1
  42. package/dist/security/index.js +1 -1
  43. package/dist/security/patterns/insecure-patterns.js +1 -1
  44. package/dist/utils/constants.js +102 -0
  45. package/dist/utils/deployment/wrangler-config-manager.js +215 -48
  46. package/dist/utils/framework-config.js +2 -2
  47. package/dist/utils/interactive-prompts.js +10 -59
  48. package/package.json +16 -8
  49. package/dist/bin/clodo-service-old.js +0 -868
  50. package/dist/bin/clodo-service-test.js +0 -10
  51. package/dist/bin/commands/assess.js +0 -91
  52. package/dist/bin/commands/create.js +0 -77
  53. package/dist/bin/commands/diagnose.js +0 -83
  54. package/dist/bin/commands/helpers.js +0 -138
  55. package/dist/bin/commands/update.js +0 -75
  56. package/dist/bin/database/deployment-db-manager.js +0 -423
  57. package/dist/bin/database/enterprise-db-manager.js +0 -457
  58. package/dist/bin/database/wrangler-d1-manager.js +0 -685
  59. package/dist/bin/deployment/enterprise-deploy.js +0 -877
  60. package/dist/bin/deployment/master-deploy.js +0 -1376
  61. package/dist/bin/deployment/modular-enterprise-deploy.js +0 -466
  62. package/dist/bin/deployment/modules/DeploymentConfiguration.js +0 -395
  63. package/dist/bin/deployment/modules/DeploymentOrchestrator.js +0 -492
  64. package/dist/bin/deployment/modules/EnvironmentManager.js +0 -517
  65. package/dist/bin/deployment/modules/MonitoringIntegration.js +0 -560
  66. package/dist/bin/deployment/modules/ValidationManager.js +0 -342
  67. package/dist/bin/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
  68. package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
  69. package/dist/bin/deployment/orchestration/PortfolioOrchestrator.js +0 -273
  70. package/dist/bin/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
  71. package/dist/bin/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
  72. package/dist/bin/deployment/test-interactive-utils.js +0 -66
  73. package/dist/bin/portfolio/portfolio-manager.js +0 -487
  74. package/dist/bin/service-management/create-service.js +0 -122
  75. package/dist/bin/service-management/init-service.js +0 -79
  76. package/dist/config/customers.js +0 -623
  77. package/dist/config/domains.js +0 -186
  78. package/dist/config/index.js +0 -6
  79. package/dist/database/database-orchestrator.js +0 -795
  80. package/dist/database/index.js +0 -4
  81. package/dist/deployment/index.js +0 -11
  82. package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
  83. package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
  84. package/dist/deployment/orchestration/PortfolioOrchestrator.js +0 -273
  85. package/dist/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
  86. package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
  87. package/dist/deployment/orchestration/index.js +0 -17
  88. package/dist/deployment/rollback-manager.js +0 -36
  89. package/dist/deployment/wrangler-deployer.js +0 -640
  90. package/dist/handlers/GenericRouteHandler.js +0 -532
  91. package/dist/migration/MigrationAdapters.js +0 -562
  92. package/dist/modules/ModuleManager.js +0 -668
  93. package/dist/modules/security.js +0 -96
  94. package/dist/orchestration/cross-domain-coordinator.js +0 -1083
  95. package/dist/orchestration/index.js +0 -5
  96. package/dist/orchestration/modules/DeploymentCoordinator.js +0 -368
  97. package/dist/orchestration/modules/DomainResolver.js +0 -198
  98. package/dist/orchestration/modules/StateManager.js +0 -332
  99. package/dist/orchestration/multi-domain-orchestrator.js +0 -724
  100. package/dist/routing/EnhancedRouter.js +0 -158
  101. package/dist/schema/SchemaManager.js +0 -778
  102. package/dist/service-management/ConfirmationEngine.js +0 -412
  103. package/dist/service-management/ErrorTracker.js +0 -299
  104. package/dist/service-management/GenerationEngine.js +0 -447
  105. package/dist/service-management/InputCollector.js +0 -619
  106. package/dist/service-management/ServiceCreator.js +0 -265
  107. package/dist/service-management/ServiceInitializer.js +0 -453
  108. package/dist/service-management/ServiceOrchestrator.js +0 -633
  109. package/dist/service-management/generators/BaseGenerator.js +0 -233
  110. package/dist/service-management/generators/GeneratorRegistry.js +0 -254
  111. package/dist/service-management/generators/cicd/CiWorkflowGenerator.js +0 -87
  112. package/dist/service-management/generators/cicd/DeployWorkflowGenerator.js +0 -106
  113. package/dist/service-management/generators/code/ServiceHandlersGenerator.js +0 -235
  114. package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +0 -116
  115. package/dist/service-management/generators/code/ServiceUtilsGenerator.js +0 -246
  116. package/dist/service-management/generators/code/WorkerIndexGenerator.js +0 -143
  117. package/dist/service-management/generators/config/DevelopmentEnvGenerator.js +0 -101
  118. package/dist/service-management/generators/config/DomainsConfigGenerator.js +0 -175
  119. package/dist/service-management/generators/config/EnvExampleGenerator.js +0 -178
  120. package/dist/service-management/generators/config/ProductionEnvGenerator.js +0 -97
  121. package/dist/service-management/generators/config/StagingEnvGenerator.js +0 -97
  122. package/dist/service-management/generators/config/WranglerTomlGenerator.js +0 -238
  123. package/dist/service-management/generators/core/PackageJsonGenerator.js +0 -243
  124. package/dist/service-management/generators/core/SiteConfigGenerator.js +0 -115
  125. package/dist/service-management/generators/documentation/ApiDocsGenerator.js +0 -331
  126. package/dist/service-management/generators/documentation/ConfigurationDocsGenerator.js +0 -294
  127. package/dist/service-management/generators/documentation/DeploymentDocsGenerator.js +0 -244
  128. package/dist/service-management/generators/documentation/ReadmeGenerator.js +0 -196
  129. package/dist/service-management/generators/schemas/ServiceSchemaGenerator.js +0 -190
  130. package/dist/service-management/generators/scripts/DeployScriptGenerator.js +0 -123
  131. package/dist/service-management/generators/scripts/HealthCheckScriptGenerator.js +0 -101
  132. package/dist/service-management/generators/scripts/SetupScriptGenerator.js +0 -88
  133. package/dist/service-management/generators/service-types/StaticSiteGenerator.js +0 -342
  134. package/dist/service-management/generators/testing/EslintConfigGenerator.js +0 -85
  135. package/dist/service-management/generators/testing/IntegrationTestsGenerator.js +0 -237
  136. package/dist/service-management/generators/testing/JestConfigGenerator.js +0 -72
  137. package/dist/service-management/generators/testing/UnitTestsGenerator.js +0 -277
  138. package/dist/service-management/generators/tooling/DockerComposeGenerator.js +0 -71
  139. package/dist/service-management/generators/tooling/GitignoreGenerator.js +0 -143
  140. package/dist/service-management/generators/utils/FileWriter.js +0 -179
  141. package/dist/service-management/generators/utils/PathResolver.js +0 -157
  142. package/dist/service-management/generators/utils/ServiceManifestGenerator.js +0 -111
  143. package/dist/service-management/generators/utils/TemplateEngine.js +0 -185
  144. package/dist/service-management/generators/utils/index.js +0 -18
  145. package/dist/service-management/handlers/ConfirmationHandler.js +0 -71
  146. package/dist/service-management/handlers/GenerationHandler.js +0 -80
  147. package/dist/service-management/handlers/InputHandler.js +0 -59
  148. package/dist/service-management/handlers/ValidationHandler.js +0 -203
  149. package/dist/service-management/index.js +0 -14
  150. package/dist/service-management/routing/DomainRouteMapper.js +0 -311
  151. package/dist/service-management/routing/RouteGenerator.js +0 -266
  152. package/dist/service-management/routing/WranglerRoutesBuilder.js +0 -273
  153. package/dist/service-management/routing/index.js +0 -14
  154. package/dist/service-management/services/DirectoryStructureService.js +0 -56
  155. package/dist/service-management/services/GenerationCoordinator.js +0 -208
  156. package/dist/service-management/services/GeneratorRegistry.js +0 -174
  157. package/dist/services/GenericDataService.js +0 -501
  158. package/dist/ui-structures/concepts/second-order-acquisition-strategy.md +0 -286
  159. package/dist/ui-structures/concepts/service-lifecycle-management.md +0 -150
  160. package/dist/ui-structures/concepts/service-manifest-guide.md +0 -309
  161. package/dist/ui-structures/concepts/three-tier-categorization-strategy.md +0 -231
  162. package/dist/ui-structures/creation/automated-generation-ui.json +0 -246
  163. package/dist/ui-structures/creation/core-inputs-ui.json +0 -217
  164. package/dist/ui-structures/creation/smart-confirmable-ui.json +0 -451
  165. package/dist/ui-structures/reference/absolutely-required-inputs.json +0 -315
  166. package/dist/ui-structures/reference/service-manifest-template.json +0 -342
  167. package/dist/version/VersionDetector.js +0 -723
  168. package/dist/worker/index.js +0 -4
  169. package/dist/worker/integration.js +0 -351
@@ -1,868 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Clodo Framework - Unified Three-Tier Service Management CLI
5
- *
6
- * This tool provides a conversational interface for complete service lifecycle management
7
- * combining service creation, initialization, and deployment preparation.
8
- *
9
- * Three-Tier Architecture:
10
- * 1. Core Input Collection (6 required inputs)
11
- * 2. Smart Confirmations (15 derived values)
12
- * 3. Automated Generation (67 configurations + service manifest)
13
- */
14
- import { Command } from 'commander';
15
- import { createInterface } from 'readline';
16
- import chalk from 'chalk';
17
- import { join } from 'path';
18
- import { ServiceOrchestrator } from "../service-management/ServiceOrchestrator.js";
19
- import { InputCollector } from "../service-management/InputCollector.js";
20
- import { readFileSync, existsSync } from 'fs';
21
- import { resolve } from 'path';
22
- const program = new Command();
23
- program.name('clodo-service').description('Unified conversational CLI for Clodo Framework service lifecycle management').version('1.0.0');
24
-
25
- // Helper function to load JSON configuration file
26
- function loadJsonConfig(configPath) {
27
- try {
28
- const fullPath = resolve(configPath);
29
- if (!existsSync(fullPath)) {
30
- throw new Error(`Configuration file not found: ${fullPath}`);
31
- }
32
- const content = readFileSync(fullPath, 'utf8');
33
- const config = JSON.parse(content);
34
-
35
- // Validate required fields
36
- const required = ['customer', 'environment', 'domainName', 'cloudflareToken'];
37
- const missing = required.filter(field => !config[field]);
38
- if (missing.length > 0) {
39
- throw new Error(`Missing required configuration fields: ${missing.join(', ')}`);
40
- }
41
- console.log(chalk.green(`✅ Loaded configuration from: ${fullPath}`));
42
- return config;
43
- } catch (error) {
44
- if (error instanceof SyntaxError) {
45
- throw new Error(`Invalid JSON in configuration file: ${error.message}`);
46
- }
47
- throw error;
48
- }
49
- }
50
-
51
- // Simple progress indicator for deployment steps
52
- function showProgress(message, duration = 2000) {
53
- return new Promise(resolve => {
54
- process.stdout.write(chalk.cyan(`⏳ ${message}...`));
55
- const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
56
- let i = 0;
57
- const interval = setInterval(() => {
58
- process.stdout.write(`\r${chalk.cyan(spinner[i])} ${message}...`);
59
- i = (i + 1) % spinner.length;
60
- }, 100);
61
- setTimeout(() => {
62
- clearInterval(interval);
63
- process.stdout.write(`\r${chalk.green('✅')} ${message}... Done!\n`);
64
- resolve();
65
- }, duration);
66
- });
67
- }
68
-
69
- // Early validation function to check prerequisites before deployment
70
- async function validateDeploymentPrerequisites(coreInputs, options) {
71
- const issues = [];
72
- console.log(chalk.cyan('\n🔍 Pre-deployment Validation'));
73
- console.log(chalk.gray('─'.repeat(40)));
74
-
75
- // Check required fields
76
- if (!coreInputs.customer) {
77
- issues.push('Customer name is required');
78
- }
79
- if (!coreInputs.environment) {
80
- issues.push('Environment is required');
81
- }
82
- if (!coreInputs.domainName) {
83
- issues.push('Domain name is required');
84
- }
85
- if (!coreInputs.cloudflareToken) {
86
- issues.push('Cloudflare API token is required');
87
- }
88
-
89
- // Check Cloudflare token format (basic validation)
90
- if (coreInputs.cloudflareToken && !coreInputs.cloudflareToken.startsWith('CLOUDFLARE_API_TOKEN=')) {
91
- if (coreInputs.cloudflareToken.length < 40) {
92
- issues.push('Cloudflare API token appears to be invalid (too short)');
93
- }
94
- }
95
-
96
- // Check if service path exists
97
- if (options.servicePath && options.servicePath !== '.') {
98
- const {
99
- existsSync
100
- } = await import('fs');
101
- if (!existsSync(options.servicePath)) {
102
- issues.push(`Service path does not exist: ${options.servicePath}`);
103
- }
104
- }
105
-
106
- // Check for wrangler.toml if not dry run
107
- if (!options.dryRun) {
108
- const {
109
- existsSync
110
- } = await import('fs');
111
- const {
112
- join
113
- } = await import('path');
114
- const wranglerPath = join(options.servicePath || '.', 'wrangler.toml');
115
- if (!existsSync(wranglerPath)) {
116
- issues.push('wrangler.toml not found in service directory');
117
- }
118
- }
119
-
120
- // Report issues
121
- if (issues.length > 0) {
122
- console.log(chalk.red('\n❌ Validation Failed:'));
123
- issues.forEach(issue => {
124
- console.log(chalk.red(` • ${issue}`));
125
- });
126
- console.log(chalk.gray('\n─'.repeat(40)));
127
- return false;
128
- }
129
- console.log(chalk.green('✅ All prerequisites validated'));
130
- console.log(chalk.gray('─'.repeat(40)));
131
- return true;
132
- }
133
-
134
- // Main interactive command
135
- program.command('create').description('Create a new Clodo service with conversational setup').option('-n, --non-interactive', 'Run in non-interactive mode with all required parameters').option('--service-name <name>', 'Service name (required in non-interactive mode)').option('--service-type <type>', 'Service type: data-service, auth-service, content-service, api-gateway, generic', 'generic').option('--domain-name <domain>', 'Domain name (required in non-interactive mode)').option('--cloudflare-token <token>', 'Cloudflare API token (required in non-interactive mode)').option('--cloudflare-account-id <id>', 'Cloudflare account ID (required in non-interactive mode)').option('--cloudflare-zone-id <id>', 'Cloudflare zone ID (required in non-interactive mode)').option('--environment <env>', 'Target environment: development, staging, production', 'development').option('--output-path <path>', 'Output directory for generated service', '.').option('--template-path <path>', 'Path to service templates', './templates').action(async options => {
136
- try {
137
- const orchestrator = new ServiceOrchestrator({
138
- interactive: !options.nonInteractive,
139
- outputPath: options.outputPath,
140
- templatePath: options.templatePath
141
- });
142
- if (options.nonInteractive) {
143
- // Validate required parameters for non-interactive mode
144
- const required = ['serviceName', 'domainName', 'cloudflareToken', 'cloudflareAccountId', 'cloudflareZoneId'];
145
- const missing = required.filter(key => !options[key]);
146
- if (missing.length > 0) {
147
- console.error(chalk.red(`Missing required parameters: ${missing.join(', ')}`));
148
- console.error(chalk.yellow('Use --help for parameter details'));
149
- process.exit(1);
150
- }
151
-
152
- // Convert CLI options to core inputs
153
- const coreInputs = {
154
- serviceName: options.serviceName,
155
- serviceType: options.serviceType,
156
- domainName: options.domainName,
157
- cloudflareToken: options.cloudflareToken,
158
- cloudflareAccountId: options.cloudflareAccountId,
159
- cloudflareZoneId: options.cloudflareZoneId,
160
- environment: options.environment
161
- };
162
- await orchestrator.runNonInteractive(coreInputs);
163
- } else {
164
- await orchestrator.runInteractive();
165
- }
166
- console.log(chalk.green('\n✓ Service creation completed successfully!'));
167
- console.log(chalk.cyan('Next steps:'));
168
- console.log(chalk.white(' 1. cd into your new service directory'));
169
- console.log(chalk.white(' 2. Run npm install'));
170
- console.log(chalk.white(' 3. Configure additional settings in src/config/domains.js'));
171
- console.log(chalk.white(' 4. Run npm run deploy to deploy to Cloudflare'));
172
- } catch (error) {
173
- console.error(chalk.red(`\n✗ Service creation failed: ${error.message}`));
174
- if (error.details) {
175
- console.error(chalk.yellow(`Details: ${error.details}`));
176
- }
177
- process.exit(1);
178
- }
179
- });
180
-
181
- // Legacy command aliases for backward compatibility
182
- program.command('create-service').description('Legacy alias for create command').action(async options => {
183
- console.log(chalk.yellow('This command is deprecated. Use "clodo-service create" instead.'));
184
- const createCommand = program.commands.find(cmd => cmd.name() === 'create');
185
- if (createCommand && createCommand._actionHandler) {
186
- await createCommand._actionHandler(options || {});
187
- }
188
- });
189
- program.command('init-service').description('Legacy alias for create command').action(async options => {
190
- console.log(chalk.yellow('This command is deprecated. Use "clodo-service create" instead.'));
191
- const createCommand = program.commands.find(cmd => cmd.name() === 'create');
192
- if (createCommand && createCommand._actionHandler) {
193
- await createCommand._actionHandler(options || {});
194
- }
195
- });
196
-
197
- // List available service types
198
- program.command('list-types').description('List available service types and their features').action(() => {
199
- console.log(chalk.cyan('Available Clodo Framework Service Types:'));
200
- console.log('');
201
- const types = {
202
- 'data-service': ['Authentication', 'Authorization', 'File Storage', 'Search', 'Filtering', 'Pagination'],
203
- 'auth-service': ['Authentication', 'Authorization', 'User Profiles', 'Email Notifications', 'Magic Link Auth'],
204
- 'content-service': ['File Storage', 'Search', 'Filtering', 'Pagination', 'Caching'],
205
- 'api-gateway': ['Authentication', 'Authorization', 'Rate Limiting', 'Caching', 'Monitoring'],
206
- 'generic': ['Logging', 'Monitoring', 'Error Reporting']
207
- };
208
- Object.entries(types).forEach(([type, features]) => {
209
- console.log(chalk.green(` ${type}`));
210
- features.forEach(feature => {
211
- console.log(chalk.white(` • ${feature}`));
212
- });
213
- console.log('');
214
- });
215
- });
216
-
217
- // Show usage statistics and plan information
218
- /*
219
- program
220
- .command('usage')
221
- .description('Show usage statistics and subscription information')
222
- .action(() => {
223
- const stats = usageTracker.getUsageStats();
224
- const license = usageTracker.getLicense();
225
- const plan = usageTracker.isPaidUser() ? 'Paid' : 'Free';
226
-
227
- console.log(chalk.cyan('\n📊 Clodo Framework Usage Statistics'));
228
- console.log('='.repeat(50));
229
-
230
- if (license && (license.plan === 'founder' || license.plan === 'admin')) {
231
- console.log(chalk.magenta(`👑 Plan: ${license.plan.charAt(0).toUpperCase() + license.plan.slice(1)} (Unlimited)`));
232
- console.log(chalk.magenta(`👤 User: ${license.userName} <${license.userEmail}>`));
233
- } else {
234
- console.log(chalk.white(`Plan: ${plan}`));
235
- }
236
-
237
- console.log(chalk.white(`Services Created: ${stats.currentUsage}${stats.limit !== -1 ? `/${stats.limit}` : ''}`));
238
- console.log(chalk.white(`Environments: ${stats.environments.join(', ')}`));
239
- console.log(chalk.white(`Premium Features: ${stats.premiumFeatures ? '✅' : '❌'}`));
240
-
241
- if (stats.daysUntilExpiry && stats.daysUntilExpiry < 36500) { // Not showing expiry for founder licenses
242
- console.log(chalk.white(`Days Until Expiry: ${stats.daysUntilExpiry}`));
243
- } else if (license && (license.plan === 'founder' || license.plan === 'admin')) {
244
- console.log(chalk.magenta(`⏰ Status: Never Expires`));
245
- }
246
-
247
- if (!usageTracker.isPaidUser()) {
248
- console.log(chalk.yellow('\n🚀 Upgrade to unlock unlimited usage!'));
249
- console.log(chalk.white('Visit: https://clodo-framework.com/pricing'));
250
- } else if (license && (license.plan === 'founder' || license.plan === 'admin')) {
251
- console.log(chalk.magenta('\n🎉 You have unlimited founder access!'));
252
- console.log(chalk.white('Thank you for building Clodo Framework!'));
253
- }
254
-
255
- console.log('');
256
- });
257
- */
258
-
259
- // Upgrade to paid plan (simulated for now)
260
- /*
261
- program
262
- .command('upgrade')
263
- .description('Upgrade to a paid plan')
264
- .option('--plan <plan>', 'Plan type: monthly, annual, lifetime', 'monthly')
265
- .option('--simulate', 'Simulate upgrade without actual payment')
266
- .action((options) => {
267
- if (options.simulate) {
268
- // Simulate license activation
269
- const license = usageTracker.activateLicense(options.plan, 'simulated');
270
- console.log(chalk.green('\n🎉 Successfully upgraded to Clodo Framework!'));
271
- console.log('='.repeat(50));
272
- console.log(chalk.white(`Plan: ${options.plan.charAt(0).toUpperCase() + options.plan.slice(1)}`));
273
- console.log(chalk.white(`License ID: ${license.id}`));
274
- console.log(chalk.white(`Expires: ${new Date(license.expiry).toLocaleDateString()}`));
275
- console.log(chalk.green('\n✅ You now have unlimited access to all features!'));
276
- console.log(chalk.cyan('Run "clodo-service usage" to see your new limits.'));
277
- } else {
278
- console.log(chalk.cyan('\n🚀 Ready to upgrade to Clodo Framework?'));
279
- console.log('='.repeat(50));
280
- console.log(chalk.white('Choose your plan:'));
281
- console.log(chalk.white('• Monthly: $19/month'));
282
- console.log(chalk.white('• Annual: $189/year (save 17%)'));
283
- console.log(chalk.white('• Lifetime: $999 (one-time payment)'));
284
- console.log('');
285
- console.log(chalk.yellow('For testing, use: clodo-service upgrade --simulate --plan annual'));
286
- console.log(chalk.cyan('Real payments coming soon at: https://clodo-framework.com/pricing'));
287
- }
288
- console.log('');
289
- });
290
- */
291
-
292
- // Generate founder license (for framework builder and selected team)
293
- /*
294
- program
295
- .command('generate-founder-license')
296
- .description('Generate unlimited founder license (admin only)')
297
- .option('--email <email>', 'User email address', 'founder@clodo-framework.com')
298
- .option('--name <name>', 'User display name', 'Framework Builder')
299
- .option('--admin', 'Generate admin license instead of founder')
300
- .action((options) => {
301
- try {
302
- let license;
303
- if (options.admin) {
304
- license = usageTracker.generateAdminLicense(options.email, options.name);
305
- console.log(chalk.green('\n🔑 Admin License Generated Successfully!'));
306
- console.log('='.repeat(50));
307
- console.log(chalk.white(`License Type: Admin (Unlimited Access)`));
308
- } else {
309
- license = usageTracker.generateFounderLicense(options.email, options.name);
310
- console.log(chalk.green('\n👑 Founder License Generated Successfully!'));
311
- console.log('='.repeat(50));
312
- console.log(chalk.white(`License Type: Founder (Unlimited Access)`));
313
- }
314
-
315
- console.log(chalk.white(`License ID: ${license.id}`));
316
- console.log(chalk.white(`User: ${license.userName} <${license.userEmail}>`));
317
- console.log(chalk.white(`Generated: ${new Date(license.generated).toLocaleString()}`));
318
- console.log(chalk.green('\n✅ Unlimited access granted - never expires!'));
319
- console.log(chalk.cyan('Run "clodo-service usage" to verify your access.'));
320
-
321
- } catch (error) {
322
- console.error(chalk.red(`\n❌ Error generating license: ${error.message}`));
323
- process.exit(1);
324
- }
325
- console.log('');
326
- });
327
- */
328
-
329
- // Validate service configuration
330
- program.command('validate <service-path>').description('Validate an existing service configuration').action(async servicePath => {
331
- try {
332
- const orchestrator = new ServiceOrchestrator();
333
- const result = await orchestrator.validateService(servicePath);
334
- if (result.valid) {
335
- console.log(chalk.green('✓ Service configuration is valid'));
336
- } else {
337
- console.log(chalk.red('✗ Service configuration has issues:'));
338
- result.issues.forEach(issue => {
339
- console.log(chalk.yellow(` • ${issue}`));
340
- });
341
- process.exit(1);
342
- }
343
- } catch (error) {
344
- console.error(chalk.red(`Validation failed: ${error.message}`));
345
- process.exit(1);
346
- }
347
- });
348
-
349
- // Update existing service
350
- 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').action(async (servicePath, options) => {
351
- try {
352
- const orchestrator = new ServiceOrchestrator();
353
-
354
- // Auto-detect service path if not provided
355
- if (!servicePath) {
356
- servicePath = await orchestrator.detectServicePath();
357
- if (!servicePath) {
358
- console.error(chalk.red('No service path provided and could not auto-detect service directory'));
359
- console.log(chalk.white('Please run this command from within a service directory or specify the path'));
360
- process.exit(1);
361
- }
362
- console.log(chalk.cyan(`Auto-detected service at: ${servicePath}`));
363
- }
364
-
365
- // Validate it's a service directory
366
- const isValid = await orchestrator.validateService(servicePath);
367
- if (!isValid.valid) {
368
- console.log(chalk.yellow('⚠️ Service has configuration issues. Use --fix-errors to attempt automatic fixes.'));
369
- if (!options.fixErrors) {
370
- console.log(chalk.white('Issues found:'));
371
- isValid.issues.forEach(issue => {
372
- console.log(chalk.yellow(` • ${issue}`));
373
- });
374
- process.exit(1);
375
- }
376
- }
377
- if (options.interactive) {
378
- await orchestrator.runInteractiveUpdate(servicePath);
379
- } else {
380
- await orchestrator.runNonInteractiveUpdate(servicePath, options);
381
- }
382
- console.log(chalk.green('\n✓ Service update completed successfully!'));
383
- } catch (error) {
384
- console.error(chalk.red(`\n✗ Service update failed: ${error.message}`));
385
- if (error.details) {
386
- console.error(chalk.yellow(`Details: ${error.details}`));
387
- }
388
- if (error.recovery) {
389
- console.log(chalk.cyan('\n💡 Recovery suggestions:'));
390
- error.recovery.forEach(suggestion => {
391
- console.log(chalk.white(` • ${suggestion}`));
392
- });
393
- }
394
- process.exit(1);
395
- }
396
- });
397
-
398
- // Diagnose service issues
399
- program.command('diagnose [service-path]').description('Diagnose and report issues with an existing service').option('--deep-scan', 'Perform deep analysis including dependencies and deployment readiness').option('--export-report <file>', 'Export diagnostic report to file').action(async (servicePath, options) => {
400
- try {
401
- const orchestrator = new ServiceOrchestrator();
402
-
403
- // Auto-detect service path if not provided
404
- if (!servicePath) {
405
- servicePath = await orchestrator.detectServicePath();
406
- if (!servicePath) {
407
- console.error(chalk.red('No service path provided and could not auto-detect service directory'));
408
- process.exit(1);
409
- }
410
- }
411
- console.log(chalk.cyan('🔍 Diagnosing service...'));
412
- const diagnosis = await orchestrator.diagnoseService(servicePath, options);
413
-
414
- // Display results
415
- console.log(chalk.cyan('\n📋 Diagnostic Report'));
416
- console.log(chalk.white(`Service: ${diagnosis.serviceName || 'Unknown'}`));
417
- console.log(chalk.white(`Path: ${servicePath}`));
418
- if (diagnosis.errors.length > 0) {
419
- console.log(chalk.red('\n❌ Critical Errors:'));
420
- diagnosis.errors.forEach(error => {
421
- console.log(chalk.red(` • ${error.message}`));
422
- if (error.location) {
423
- console.log(chalk.gray(` Location: ${error.location}`));
424
- }
425
- if (error.suggestion) {
426
- console.log(chalk.cyan(` 💡 ${error.suggestion}`));
427
- }
428
- });
429
- }
430
- if (diagnosis.warnings.length > 0) {
431
- console.log(chalk.yellow('\n⚠️ Warnings:'));
432
- diagnosis.warnings.forEach(warning => {
433
- console.log(chalk.yellow(` • ${warning.message}`));
434
- if (warning.suggestion) {
435
- console.log(chalk.cyan(` 💡 ${warning.suggestion}`));
436
- }
437
- });
438
- }
439
- if (diagnosis.recommendations.length > 0) {
440
- console.log(chalk.cyan('\n💡 Recommendations:'));
441
- diagnosis.recommendations.forEach(rec => {
442
- console.log(chalk.white(` • ${rec}`));
443
- });
444
- }
445
-
446
- // Export report if requested
447
- if (options.exportReport) {
448
- await orchestrator.exportDiagnosticReport(diagnosis, options.exportReport);
449
- console.log(chalk.green(`\n📄 Report exported to: ${options.exportReport}`));
450
- }
451
-
452
- // Exit with error code if critical issues found
453
- if (diagnosis.errors.length > 0) {
454
- process.exit(1);
455
- }
456
- } catch (error) {
457
- console.error(chalk.red(`Diagnosis failed: ${error.message}`));
458
- process.exit(1);
459
- }
460
- });
461
-
462
- // Professional Edition: assess command (from clodo-orchestration)
463
- program.command('assess [service-path]').description('Run intelligent capability assessment (requires @tamyla/clodo-orchestration)').option('--export <file>', 'Export assessment results to JSON file').option('--domain <domain>', 'Domain name for assessment').option('--service-type <type>', 'Service type for assessment').option('--token <token>', 'Cloudflare API token').action(async (servicePath, options) => {
464
- try {
465
- // Try to load professional orchestration package
466
- let orchestrationModule;
467
- try {
468
- orchestrationModule = await import('@tamyla/clodo-orchestration');
469
- } catch (err) {
470
- console.error(chalk.red('❌ clodo-orchestration package not found'));
471
- console.error(chalk.yellow('💡 Install with: npm install @tamyla/clodo-orchestration'));
472
- process.exit(1);
473
- }
474
- const {
475
- CapabilityAssessmentEngine,
476
- ServiceAutoDiscovery,
477
- runAssessmentWorkflow
478
- } = orchestrationModule;
479
- const targetPath = servicePath || process.cwd();
480
- console.log(chalk.cyan('\n🧠 Professional Capability Assessment'));
481
- console.log(chalk.gray('─'.repeat(60)));
482
- console.log(chalk.white(`Service Path: ${targetPath}`));
483
- if (options.domain) {
484
- console.log(chalk.white(`Domain: ${options.domain}`));
485
- }
486
- if (options.serviceType) {
487
- console.log(chalk.white(`Service Type: ${options.serviceType}`));
488
- }
489
- console.log(chalk.gray('─'.repeat(60)));
490
-
491
- // Use the assessment workflow
492
- const assessment = await runAssessmentWorkflow({
493
- servicePath: targetPath,
494
- domain: options.domain,
495
- serviceType: options.serviceType,
496
- token: options.token || process.env.CLOUDFLARE_API_TOKEN
497
- });
498
-
499
- // Display results
500
- console.log(chalk.cyan('\n✅ Assessment Results'));
501
- console.log(chalk.gray('─'.repeat(60)));
502
- console.log(chalk.white(`Service Type: ${assessment.mergedInputs?.serviceType || assessment.serviceType || 'Not determined'}`));
503
- console.log(chalk.white(`Confidence: ${assessment.confidence}%`));
504
- if (assessment.gapAnalysis?.missing) {
505
- console.log(chalk.white(`Missing Capabilities: ${assessment.gapAnalysis.missing.length}`));
506
- if (assessment.gapAnalysis.missing.length > 0) {
507
- console.log(chalk.yellow('\n⚠️ Missing:'));
508
- assessment.gapAnalysis.missing.forEach(gap => {
509
- console.log(chalk.yellow(` • ${gap.capability}: ${gap.reason || 'Not available'}`));
510
- });
511
- }
512
- }
513
- console.log(chalk.gray('─'.repeat(60)));
514
-
515
- // Export results if requested
516
- if (options.export) {
517
- require('fs').writeFileSync(options.export, JSON.stringify(assessment, null, 2));
518
- console.log(chalk.green(`\n📄 Results exported to: ${options.export}`));
519
- }
520
- } catch (error) {
521
- console.error(chalk.red(`Assessment failed: ${error.message}`));
522
- if (process.env.DEBUG) {
523
- console.error(chalk.gray(error.stack));
524
- }
525
- process.exit(1);
526
- }
527
- });
528
-
529
- // Deploy command - using existing InputCollector + CustomerConfigLoader (reusable pattern)
530
- program.command('deploy').description('Deploy service using three-tier input architecture').option('-c, --customer <name>', 'Customer name').option('-e, --env <environment>', 'Target environment (development, staging, production)').option('-i, --interactive', 'Interactive mode (review confirmations)', true).option('--non-interactive', 'Non-interactive mode (use stored config)').option('--config-file <file>', 'Load configuration from JSON file').option('--defaults', 'Use default values where possible (non-interactive)').option('--quiet', 'Quiet mode - minimal output for CI/CD').option('--json-output', 'Output results as JSON for scripting').option('--dry-run', 'Simulate deployment without making changes').option('--domain <domain>', 'Domain name (overrides stored config)').option('--service-path <path>', 'Path to service directory', '.').action(async options => {
531
- try {
532
- // Use existing reusable components
533
- const {
534
- InputCollector
535
- } = await import('../dist/service-management/InputCollector.js');
536
- const {
537
- UnifiedConfigManager
538
- } = await import('../dist/utils/config/unified-config-manager.js');
539
- const {
540
- ConfirmationHandler
541
- } = await import('../dist/service-management/handlers/ConfirmationHandler.js');
542
- const {
543
- MultiDomainOrchestrator
544
- } = await import('../dist/orchestration/multi-domain-orchestrator.js');
545
- console.log(chalk.cyan('\n🚀 Clodo Framework Deployment'));
546
- console.log(chalk.white('Using Three-Tier Input Architecture\n'));
547
- const isInteractive = options.interactive && !options.nonInteractive && !options.configFile && !options.defaults;
548
- const isQuiet = options.quiet;
549
- const jsonOutput = options.jsonOutput;
550
- const configManager = new UnifiedConfigManager({
551
- configDir: join(process.cwd(), 'config', 'customers')
552
- });
553
- const inputCollector = new InputCollector({
554
- interactive: isInteractive
555
- });
556
- const confirmationHandler = new ConfirmationHandler({
557
- interactive: isInteractive
558
- });
559
- let coreInputs = {};
560
- let source = 'interactive';
561
-
562
- // Interactive mode: run full three-tier input collection
563
- if (isInteractive) {
564
- const collectionResult = await inputCollector.collect();
565
- coreInputs = collectionResult.flatInputs;
566
-
567
- // The three-tier collection already included confirmations
568
- // Skip the separate confirmation step since it was done in collect()
569
- } else {
570
- // Non-interactive mode: load from config file or stored config
571
-
572
- // Load from JSON config file if specified
573
- if (options.configFile) {
574
- coreInputs = loadJsonConfig(options.configFile);
575
- source = 'config-file';
576
- } else if (options.customer && options.env) {
577
- // Check UnifiedConfigManager for existing config
578
- if (configManager.configExists(options.customer, options.env)) {
579
- console.log(chalk.green(`✅ Found existing configuration for ${options.customer}/${options.env}\n`));
580
- configManager.displayCustomerConfig(options.customer, options.env);
581
-
582
- // Non-interactive: auto-load the config
583
- const storedConfig = configManager.loadCustomerConfig(options.customer, options.env);
584
- if (storedConfig) {
585
- coreInputs = storedConfig;
586
- source = 'stored-config';
587
- }
588
- } else {
589
- console.log(chalk.yellow(`⚠️ No configuration found for ${options.customer}/${options.env}`));
590
- console.log(chalk.white('Collecting inputs interactively...\n'));
591
- }
592
- } else {
593
- console.log(chalk.yellow('⚠️ Customer and environment not specified'));
594
- console.log(chalk.white('Use --customer and --env options, or run in interactive mode'));
595
- process.exit(1);
596
- }
597
-
598
- // Collect inputs if we don't have them from stored config
599
- if (!coreInputs.cloudflareAccountId) {
600
- console.log(chalk.cyan('📊 Tier 1: Core Input Collection\n'));
601
-
602
- // Collect basic info with smart customer selection
603
- let customer = options.customer;
604
- if (!customer) {
605
- const customers = configManager.listCustomers();
606
- if (customers.length > 0) {
607
- const selection = await inputCollector.question('Select customer (enter number or name): ');
608
-
609
- // Try to parse as number first
610
- const num = parseInt(selection);
611
- if (!isNaN(num) && num >= 1 && num <= customers.length) {
612
- customer = customers[num - 1];
613
- console.log(chalk.green(`✓ Selected: ${customer}\n`));
614
- } else if (customers.includes(selection)) {
615
- customer = selection;
616
- console.log(chalk.green(`✓ Selected: ${customer}\n`));
617
- } else {
618
- // New customer name
619
- customer = selection;
620
- console.log(chalk.yellow(`⚠️ Creating new customer: ${customer}\n`));
621
- }
622
- } else {
623
- customer = await inputCollector.question('Customer name: ');
624
- }
625
- }
626
- const environment = options.env || (await inputCollector.collectEnvironment());
627
- const serviceName = await inputCollector.collectServiceName();
628
- const serviceType = await inputCollector.collectServiceType();
629
-
630
- // Collect Cloudflare token
631
- const cloudflareToken = process.env.CLOUDFLARE_API_TOKEN || (await inputCollector.collectCloudflareToken());
632
-
633
- // Use CloudflareAPI for automatic domain discovery
634
- console.log(chalk.cyan('⏳ Fetching Cloudflare configuration...'));
635
- const cloudflareConfig = await inputCollector.collectCloudflareConfigWithDiscovery(cloudflareToken, options.domain);
636
-
637
- // Combine all inputs
638
- coreInputs = {
639
- customer,
640
- environment,
641
- serviceName,
642
- serviceType,
643
- domainName: cloudflareConfig.domainName,
644
- cloudflareToken,
645
- cloudflareAccountId: cloudflareConfig.accountId,
646
- cloudflareZoneId: cloudflareConfig.zoneId
647
- };
648
- source = 'interactive';
649
- }
650
-
651
- // Allow domain override
652
- if (options.domain) {
653
- coreInputs.domainName = options.domain;
654
- }
655
-
656
- // Early validation before proceeding
657
- const isValid = await validateDeploymentPrerequisites(coreInputs, options);
658
- if (!isValid) {
659
- console.log(chalk.red('\n❌ Deployment cancelled due to validation errors.'));
660
- console.log(chalk.cyan('💡 Fix the issues above and try again.'));
661
- process.exit(1);
662
- }
663
-
664
- // Tier 2: Generate smart confirmations using existing ConfirmationHandler
665
- // Skip if interactive mode (confirmations already done in three-tier collection)
666
- const confirmations = isInteractive ? {} // Confirmations already collected in interactive mode
667
- : await confirmationHandler.generateAndConfirm(coreInputs);
668
-
669
- // Show deployment summary
670
- console.log(chalk.cyan('\n📊 Deployment Summary'));
671
- console.log(chalk.gray('─'.repeat(60)));
672
- console.log(chalk.white(`Source: ${source}`));
673
- console.log(chalk.white(`Customer: ${coreInputs.customer}`));
674
- console.log(chalk.white(`Environment: ${coreInputs.environment}`));
675
- console.log(chalk.white(`Domain: ${coreInputs.domainName}`));
676
- console.log(chalk.white(`Account ID: ${coreInputs.cloudflareAccountId ? coreInputs.cloudflareAccountId.substring(0, 8) + '...' : 'N/A'}`));
677
- console.log(chalk.white(`Zone ID: ${coreInputs.cloudflareZoneId ? coreInputs.cloudflareZoneId.substring(0, 8) + '...' : 'N/A'}`));
678
- if (confirmations.workerName) {
679
- console.log(chalk.white(`Worker: ${confirmations.workerName}`));
680
- }
681
- if (confirmations.databaseName) {
682
- console.log(chalk.white(`Database: ${confirmations.databaseName}`));
683
- }
684
- console.log(chalk.gray('─'.repeat(60)));
685
- if (options.dryRun) {
686
- console.log(chalk.yellow('\n🔍 DRY RUN MODE - No changes will be made\n'));
687
- }
688
-
689
- // Tier 3: Execute deployment
690
- console.log(chalk.cyan('\n⚙️ Tier 3: Automated Deployment\n'));
691
-
692
- // OPTIONAL: Try to load professional orchestration package if available
693
- let professionalOrchestration = null;
694
- try {
695
- const {
696
- runAssessmentWorkflow
697
- } = await import('@tamyla/clodo-orchestration');
698
- professionalOrchestration = {
699
- runAssessmentWorkflow
700
- };
701
- console.log(chalk.cyan('📊 Professional Edition (clodo-orchestration) detected'));
702
- } catch (err) {
703
- // clodo-orchestration not installed, continue with standard assessment
704
- if (process.env.DEBUG) {
705
- console.log(chalk.gray(` ℹ️ clodo-orchestration not available: ${err.message}`));
706
- }
707
- }
708
-
709
- // INTEGRATION: Add intelligent service discovery and assessment
710
- console.log(chalk.cyan('🧠 Performing Intelligent Service Assessment...'));
711
- const {
712
- CapabilityAssessmentEngine
713
- } = await import('../dist/service-management/CapabilityAssessmentEngine.js');
714
- const assessor = new CapabilityAssessmentEngine(options.servicePath || process.cwd());
715
- try {
716
- const assessment = await assessor.assessCapabilities({
717
- serviceName: coreInputs.serviceName,
718
- serviceType: coreInputs.serviceType,
719
- environment: coreInputs.environment,
720
- domainName: coreInputs.domainName
721
- });
722
-
723
- // Display assessment results
724
- console.log(chalk.green('✅ Assessment Complete'));
725
- console.log(chalk.white(` Service Type: ${assessment.mergedInputs.serviceType || 'Not determined'}`));
726
- console.log(chalk.white(` Confidence: ${assessment.confidence}%`));
727
- console.log(chalk.white(` Missing Capabilities: ${assessment.gapAnalysis.missing.length}`));
728
-
729
- // Show any blocking issues
730
- const blockingIssues = assessment.gapAnalysis.missing.filter(gap => gap.priority === 'blocked');
731
- if (blockingIssues.length > 0) {
732
- console.log(chalk.yellow('\n⚠️ Permission-limited capabilities detected:'));
733
- blockingIssues.forEach(issue => {
734
- console.log(chalk.yellow(` - ${issue.capability}: ${issue.reason}`));
735
- });
736
- }
737
- console.log('');
738
- } catch (assessmentError) {
739
- console.log(chalk.yellow('⚠️ Assessment failed, proceeding with deployment:'), assessmentError.message);
740
- console.log('');
741
- }
742
- const orchestrator = new MultiDomainOrchestrator({
743
- domains: [coreInputs.domainName],
744
- environment: coreInputs.environment,
745
- dryRun: options.dryRun,
746
- servicePath: options.servicePath,
747
- cloudflareToken: coreInputs.cloudflareToken,
748
- cloudflareAccountId: coreInputs.cloudflareAccountId
749
- });
750
-
751
- // Show progress for initialization
752
- await showProgress('Initializing deployment orchestrator', 1500);
753
- await orchestrator.initialize();
754
- console.log(chalk.cyan('🚀 Starting deployment...\n'));
755
-
756
- // Show progress during deployment
757
- const deployPromise = orchestrator.deploySingleDomain(coreInputs.domainName, {
758
- ...coreInputs,
759
- ...confirmations,
760
- servicePath: options.servicePath
761
- });
762
-
763
- // Show deployment progress
764
- const progressMessages = ['Preparing deployment configuration', 'Setting up Cloudflare resources', 'Deploying worker script', 'Configuring database connections', 'Setting up domain routing', 'Running final health checks'];
765
- let progressIndex = 0;
766
- const progressInterval = setInterval(() => {
767
- if (progressIndex < progressMessages.length) {
768
- console.log(chalk.gray(` ${progressMessages[progressIndex]}`));
769
- progressIndex++;
770
- }
771
- }, 2000);
772
- const result = await deployPromise;
773
- clearInterval(progressInterval);
774
-
775
- // Fill in remaining progress messages quickly
776
- while (progressIndex < progressMessages.length) {
777
- console.log(chalk.gray(` ${progressMessages[progressIndex]}`));
778
- progressIndex++;
779
- }
780
-
781
- // Display results with cleaner formatting
782
- console.log(chalk.green('\n✅ Deployment Completed Successfully!'));
783
- console.log(chalk.gray('─'.repeat(60)));
784
-
785
- // Show key information prominently
786
- if (result.url || confirmations.deploymentUrl) {
787
- console.log(chalk.white(`🌐 Service URL: ${chalk.bold(result.url || confirmations.deploymentUrl)}`));
788
- }
789
- console.log(chalk.white(`👤 Customer: ${coreInputs.customer}`));
790
- console.log(chalk.white(`🏭 Environment: ${coreInputs.environment}`));
791
- console.log(chalk.white(`🔧 Worker: ${confirmations.workerName || 'N/A'}`));
792
- if (result.health) {
793
- const healthStatus = result.health.toLowerCase().includes('ok') || result.health.toLowerCase().includes('healthy') ? chalk.green('✅ Healthy') : chalk.yellow('⚠️ Check required');
794
- console.log(chalk.white(`💚 Health: ${healthStatus}`));
795
- }
796
- console.log(chalk.gray('─'.repeat(60)));
797
-
798
- // Save deployment configuration for future reuse
799
- try {
800
- console.log(chalk.cyan('\n💾 Saving deployment configuration...'));
801
- const configFile = await configManager.saveCustomerConfig(coreInputs.customer, coreInputs.environment, {
802
- coreInputs,
803
- confirmations,
804
- result
805
- });
806
- console.log(chalk.green(' ✅ Configuration saved for future deployments'));
807
- console.log(chalk.gray(` 📄 File: ${configFile}`));
808
- } catch (saveError) {
809
- console.log(chalk.yellow(` ⚠️ Could not save configuration: ${saveError.message}`));
810
- console.log(chalk.gray(' Deployment succeeded, but you may need to re-enter values next time.'));
811
- }
812
- console.log(chalk.cyan('\n📋 Next Steps:'));
813
- console.log(chalk.white(` • Test deployment: curl ${result.url || confirmations.deploymentUrl}/health`));
814
- console.log(chalk.white(` • Monitor logs: wrangler tail ${confirmations.workerName}`));
815
- console.log(chalk.white(` • View dashboard: https://dash.cloudflare.com`));
816
- }
817
- } catch (error) {
818
- console.error(chalk.red(`\n❌ Deployment failed: ${error.message}`));
819
-
820
- // Show helpful context based on error type
821
- if (error.message.includes('timeout')) {
822
- console.log(chalk.yellow('\n💡 Troubleshooting Tips:'));
823
- console.log(chalk.white(' • Use non-interactive mode: npx clodo-service deploy --customer=NAME --env=ENV --non-interactive'));
824
- console.log(chalk.white(' • Set DEBUG=1 for detailed logs: DEBUG=1 npx clodo-service deploy'));
825
- console.log(chalk.white(' • Check your terminal supports readline'));
826
- } else if (error.message.includes('domain')) {
827
- console.log(chalk.yellow('\n💡 Domain Issues:'));
828
- console.log(chalk.white(' • Verify domain exists in Cloudflare dashboard'));
829
- console.log(chalk.white(' • Check API token has zone:read permissions'));
830
- console.log(chalk.white(' • Try specifying domain: --domain=example.com'));
831
- } else if (error.message.includes('readline')) {
832
- console.log(chalk.yellow('\n💡 Terminal Issues:'));
833
- console.log(chalk.white(' • Try a different terminal (cmd, bash, powershell)'));
834
- console.log(chalk.white(' • Use --non-interactive with config file'));
835
- }
836
- if (process.env.DEBUG) {
837
- console.error(chalk.gray('\nFull Stack Trace:'));
838
- console.error(chalk.gray(error.stack));
839
- } else {
840
- console.log(chalk.gray('\nRun with DEBUG=1 for full stack trace'));
841
- }
842
- process.exit(1);
843
- }
844
- });
845
-
846
- // Utility function to redact sensitive information from logs
847
- function redactSensitiveInfo(text) {
848
- if (typeof text !== 'string') return text;
849
-
850
- // Patterns to redact
851
- const patterns = [
852
- // Cloudflare API tokens
853
- [/(CLOUDFLARE_API_TOKEN=?)(\w{20,})/gi, '$1[REDACTED]'],
854
- // Generic API tokens/keys
855
- [/(api[_-]?token|api[_-]?key|auth[_-]?token)["']?[:=]\s*["']?([a-zA-Z0-9_-]{20,})["']?/gi, '$1: [REDACTED]'],
856
- // Passwords
857
- [/(password|passwd|pwd)["']?[:=]\s*["']?([^"'\s]{3,})["']?/gi, '$1: [REDACTED]'],
858
- // Secrets
859
- [/(secret|key)["']?[:=]\s*["']?([a-zA-Z0-9_-]{10,})["']?/gi, '$1: [REDACTED]'],
860
- // Account IDs (partial redaction)
861
- [/(account[_-]?id|zone[_-]?id)["']?[:=]\s*["']?([a-zA-Z0-9]{8})([a-zA-Z0-9]{24,})["']?/gi, '$1: $2[REDACTED]']];
862
- let redacted = text;
863
- patterns.forEach(([pattern, replacement]) => {
864
- redacted = redacted.replace(pattern, replacement);
865
- });
866
- return redacted;
867
- }
868
- program.parse();