@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.
- package/CHANGELOG.md +9 -0
- package/README.md +53 -0
- package/dist/bin/clodo-service.js +47 -15
- package/dist/bin/commands/deploy.js +115 -83
- package/dist/bin/commands/helpers/deployment-ui.js +138 -0
- package/dist/bin/commands/helpers/deployment-verification.js +251 -0
- package/dist/bin/commands/helpers/error-recovery.js +80 -0
- package/dist/bin/commands/helpers/resource-detection.js +113 -0
- package/dist/bin/commands/validate.js +1 -1
- package/dist/bin/security/security-cli.js +1 -1
- package/dist/bin/shared/cache/configuration-cache.js +82 -0
- package/dist/bin/shared/cloudflare/domain-manager.js +1 -1
- package/dist/bin/shared/cloudflare/index.js +1 -1
- package/dist/bin/shared/cloudflare/ops.js +6 -4
- package/dist/bin/shared/config/ConfigurationManager.js +23 -1
- package/dist/bin/shared/config/command-config-manager.js +19 -3
- package/dist/bin/shared/config/index.js +1 -1
- package/dist/bin/shared/deployment/credential-collector.js +30 -7
- package/dist/bin/shared/deployment/index.js +2 -2
- package/dist/bin/shared/deployment/rollback-manager.js +4 -520
- package/dist/bin/shared/deployment/utilities/d1-error-recovery.js +177 -0
- package/dist/bin/shared/deployment/validator.js +40 -10
- package/dist/bin/shared/deployment/workflows/deployment-summary.js +214 -0
- package/dist/bin/shared/deployment/workflows/interactive-confirmation.js +188 -0
- package/dist/bin/shared/deployment/workflows/interactive-database-workflow.js +234 -0
- package/dist/bin/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
- package/dist/bin/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
- package/dist/bin/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
- package/dist/bin/shared/deployment/workflows/interactive-validation.js +218 -0
- package/dist/bin/shared/error-handling/error-classifier.js +46 -0
- package/dist/bin/shared/monitoring/health-checker.js +129 -1
- package/dist/bin/shared/monitoring/memory-manager.js +17 -6
- package/dist/bin/shared/routing/domain-router.js +1 -1
- package/dist/bin/shared/utils/deployment-validator.js +97 -0
- package/dist/bin/shared/utils/formatters.js +10 -0
- package/dist/bin/shared/utils/index.js +13 -1
- package/dist/bin/shared/utils/interactive-prompts.js +34 -18
- package/dist/bin/shared/utils/progress-manager.js +2 -2
- package/dist/bin/shared/utils/progress-spinner.js +53 -0
- package/dist/bin/shared/utils/sensitive-redactor.js +91 -0
- package/dist/bin/shared/validation/ValidationRegistry.js +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/patterns/insecure-patterns.js +1 -1
- package/dist/utils/constants.js +102 -0
- package/dist/utils/deployment/wrangler-config-manager.js +215 -48
- package/dist/utils/framework-config.js +2 -2
- package/dist/utils/interactive-prompts.js +10 -59
- package/package.json +16 -8
- package/dist/bin/clodo-service-old.js +0 -868
- package/dist/bin/clodo-service-test.js +0 -10
- package/dist/bin/commands/assess.js +0 -91
- package/dist/bin/commands/create.js +0 -77
- package/dist/bin/commands/diagnose.js +0 -83
- package/dist/bin/commands/helpers.js +0 -138
- package/dist/bin/commands/update.js +0 -75
- package/dist/bin/database/deployment-db-manager.js +0 -423
- package/dist/bin/database/enterprise-db-manager.js +0 -457
- package/dist/bin/database/wrangler-d1-manager.js +0 -685
- package/dist/bin/deployment/enterprise-deploy.js +0 -877
- package/dist/bin/deployment/master-deploy.js +0 -1376
- package/dist/bin/deployment/modular-enterprise-deploy.js +0 -466
- package/dist/bin/deployment/modules/DeploymentConfiguration.js +0 -395
- package/dist/bin/deployment/modules/DeploymentOrchestrator.js +0 -492
- package/dist/bin/deployment/modules/EnvironmentManager.js +0 -517
- package/dist/bin/deployment/modules/MonitoringIntegration.js +0 -560
- package/dist/bin/deployment/modules/ValidationManager.js +0 -342
- package/dist/bin/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
- package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/bin/deployment/orchestration/PortfolioOrchestrator.js +0 -273
- package/dist/bin/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
- package/dist/bin/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
- package/dist/bin/deployment/test-interactive-utils.js +0 -66
- package/dist/bin/portfolio/portfolio-manager.js +0 -487
- package/dist/bin/service-management/create-service.js +0 -122
- package/dist/bin/service-management/init-service.js +0 -79
- package/dist/config/customers.js +0 -623
- package/dist/config/domains.js +0 -186
- package/dist/config/index.js +0 -6
- package/dist/database/database-orchestrator.js +0 -795
- package/dist/database/index.js +0 -4
- package/dist/deployment/index.js +0 -11
- package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
- package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/deployment/orchestration/PortfolioOrchestrator.js +0 -273
- package/dist/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
- package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
- package/dist/deployment/orchestration/index.js +0 -17
- package/dist/deployment/rollback-manager.js +0 -36
- package/dist/deployment/wrangler-deployer.js +0 -640
- package/dist/handlers/GenericRouteHandler.js +0 -532
- package/dist/migration/MigrationAdapters.js +0 -562
- package/dist/modules/ModuleManager.js +0 -668
- package/dist/modules/security.js +0 -96
- package/dist/orchestration/cross-domain-coordinator.js +0 -1083
- package/dist/orchestration/index.js +0 -5
- package/dist/orchestration/modules/DeploymentCoordinator.js +0 -368
- package/dist/orchestration/modules/DomainResolver.js +0 -198
- package/dist/orchestration/modules/StateManager.js +0 -332
- package/dist/orchestration/multi-domain-orchestrator.js +0 -724
- package/dist/routing/EnhancedRouter.js +0 -158
- package/dist/schema/SchemaManager.js +0 -778
- package/dist/service-management/ConfirmationEngine.js +0 -412
- package/dist/service-management/ErrorTracker.js +0 -299
- package/dist/service-management/GenerationEngine.js +0 -447
- package/dist/service-management/InputCollector.js +0 -619
- package/dist/service-management/ServiceCreator.js +0 -265
- package/dist/service-management/ServiceInitializer.js +0 -453
- package/dist/service-management/ServiceOrchestrator.js +0 -633
- package/dist/service-management/generators/BaseGenerator.js +0 -233
- package/dist/service-management/generators/GeneratorRegistry.js +0 -254
- package/dist/service-management/generators/cicd/CiWorkflowGenerator.js +0 -87
- package/dist/service-management/generators/cicd/DeployWorkflowGenerator.js +0 -106
- package/dist/service-management/generators/code/ServiceHandlersGenerator.js +0 -235
- package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +0 -116
- package/dist/service-management/generators/code/ServiceUtilsGenerator.js +0 -246
- package/dist/service-management/generators/code/WorkerIndexGenerator.js +0 -143
- package/dist/service-management/generators/config/DevelopmentEnvGenerator.js +0 -101
- package/dist/service-management/generators/config/DomainsConfigGenerator.js +0 -175
- package/dist/service-management/generators/config/EnvExampleGenerator.js +0 -178
- package/dist/service-management/generators/config/ProductionEnvGenerator.js +0 -97
- package/dist/service-management/generators/config/StagingEnvGenerator.js +0 -97
- package/dist/service-management/generators/config/WranglerTomlGenerator.js +0 -238
- package/dist/service-management/generators/core/PackageJsonGenerator.js +0 -243
- package/dist/service-management/generators/core/SiteConfigGenerator.js +0 -115
- package/dist/service-management/generators/documentation/ApiDocsGenerator.js +0 -331
- package/dist/service-management/generators/documentation/ConfigurationDocsGenerator.js +0 -294
- package/dist/service-management/generators/documentation/DeploymentDocsGenerator.js +0 -244
- package/dist/service-management/generators/documentation/ReadmeGenerator.js +0 -196
- package/dist/service-management/generators/schemas/ServiceSchemaGenerator.js +0 -190
- package/dist/service-management/generators/scripts/DeployScriptGenerator.js +0 -123
- package/dist/service-management/generators/scripts/HealthCheckScriptGenerator.js +0 -101
- package/dist/service-management/generators/scripts/SetupScriptGenerator.js +0 -88
- package/dist/service-management/generators/service-types/StaticSiteGenerator.js +0 -342
- package/dist/service-management/generators/testing/EslintConfigGenerator.js +0 -85
- package/dist/service-management/generators/testing/IntegrationTestsGenerator.js +0 -237
- package/dist/service-management/generators/testing/JestConfigGenerator.js +0 -72
- package/dist/service-management/generators/testing/UnitTestsGenerator.js +0 -277
- package/dist/service-management/generators/tooling/DockerComposeGenerator.js +0 -71
- package/dist/service-management/generators/tooling/GitignoreGenerator.js +0 -143
- package/dist/service-management/generators/utils/FileWriter.js +0 -179
- package/dist/service-management/generators/utils/PathResolver.js +0 -157
- package/dist/service-management/generators/utils/ServiceManifestGenerator.js +0 -111
- package/dist/service-management/generators/utils/TemplateEngine.js +0 -185
- package/dist/service-management/generators/utils/index.js +0 -18
- package/dist/service-management/handlers/ConfirmationHandler.js +0 -71
- package/dist/service-management/handlers/GenerationHandler.js +0 -80
- package/dist/service-management/handlers/InputHandler.js +0 -59
- package/dist/service-management/handlers/ValidationHandler.js +0 -203
- package/dist/service-management/index.js +0 -14
- package/dist/service-management/routing/DomainRouteMapper.js +0 -311
- package/dist/service-management/routing/RouteGenerator.js +0 -266
- package/dist/service-management/routing/WranglerRoutesBuilder.js +0 -273
- package/dist/service-management/routing/index.js +0 -14
- package/dist/service-management/services/DirectoryStructureService.js +0 -56
- package/dist/service-management/services/GenerationCoordinator.js +0 -208
- package/dist/service-management/services/GeneratorRegistry.js +0 -174
- package/dist/services/GenericDataService.js +0 -501
- package/dist/ui-structures/concepts/second-order-acquisition-strategy.md +0 -286
- package/dist/ui-structures/concepts/service-lifecycle-management.md +0 -150
- package/dist/ui-structures/concepts/service-manifest-guide.md +0 -309
- package/dist/ui-structures/concepts/three-tier-categorization-strategy.md +0 -231
- package/dist/ui-structures/creation/automated-generation-ui.json +0 -246
- package/dist/ui-structures/creation/core-inputs-ui.json +0 -217
- package/dist/ui-structures/creation/smart-confirmable-ui.json +0 -451
- package/dist/ui-structures/reference/absolutely-required-inputs.json +0 -315
- package/dist/ui-structures/reference/service-manifest-template.json +0 -342
- package/dist/version/VersionDetector.js +0 -723
- package/dist/worker/index.js +0 -4
- 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();
|