@tamyla/clodo-framework 3.0.13 → 3.0.14

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 CHANGED
@@ -1,9 +1,19 @@
1
- ## [3.0.13](https://github.com/tamylaa/clodo-framework/compare/v3.0.12...v3.0.13) (2025-10-16)
1
+ ## [3.0.14](https://github.com/tamylaa/clodo-framework/compare/v3.0.13...v3.0.14) (2025-10-20)
2
2
 
3
3
 
4
4
  ### Bug Fixes
5
5
 
6
- * resolve circular dependency in unified-config-manager ([dff0841](https://github.com/tamylaa/clodo-framework/commit/dff0841c3bfc4ce0200b057c99ab76ea09641c68))
6
+ * add test environment compatibility fixes for ES modules ([71a033d](https://github.com/tamylaa/clodo-framework/commit/71a033d5d959480879c1a4771005553116e8671f))
7
+ * ignore deployment artifacts in deployments/ directory ([768f453](https://github.com/tamylaa/clodo-framework/commit/768f453cbabb984509613a382d86875c1309bcd2))
8
+ * resolve circular dependency in FeatureFlagManager ([d04956f](https://github.com/tamylaa/clodo-framework/commit/d04956f83527d3236fce2fe9750f71cdf0ba8d8e))
9
+ * resolve ESLint errors and update test expectations ([b1188be](https://github.com/tamylaa/clodo-framework/commit/b1188be66d3723bfff62431f6e0eabec685b0111))
10
+ * revert Jest config to working configuration that passes tests ([35a2846](https://github.com/tamylaa/clodo-framework/commit/35a28466c8a48c78011c128f89695399e8500341))
11
+ * update Jest config to properly handle ES modules ([1a951c7](https://github.com/tamylaa/clodo-framework/commit/1a951c7ae147ddb8bef0ec1508c858ac044423b7))
12
+ * update package-lock.json to sync with package.json dependencies ([a3ef884](https://github.com/tamylaa/clodo-framework/commit/a3ef8844cba2b5657a3493b8339c98049a57dd72))
13
+ * update package.json semantic release config to support main branch ([a06b2e8](https://github.com/tamylaa/clodo-framework/commit/a06b2e899ceb7699b67292157991d64c88dedae3))
14
+ * update semantic release config to support main branch ([4102e6c](https://github.com/tamylaa/clodo-framework/commit/4102e6c4cb4f52d065750239c2a0c5a89b733ac0))
15
+ * update version to 3.0.13 to sync with release history ([f9bb4d4](https://github.com/tamylaa/clodo-framework/commit/f9bb4d463a3884b82e972962e5fcd21a272c3acb))
16
+ * use os.tmpdir() for test paths to fix CI permission errors ([022c771](https://github.com/tamylaa/clodo-framework/commit/022c771994635002410c27d8eb7ec8f0614acf6f))
7
17
 
8
18
  ## [3.0.12](https://github.com/tamylaa/clodo-framework/compare/v3.0.11...v3.0.12) (2025-10-14)
9
19
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * Clodo Framework - Unified Three-Tier Service Management CLI
@@ -527,6 +527,86 @@ program
527
527
  }
528
528
  });
529
529
 
530
+ // Professional Edition: assess command (from clodo-orchestration)
531
+ program
532
+ .command('assess [service-path]')
533
+ .description('Run intelligent capability assessment (requires @tamyla/clodo-orchestration)')
534
+ .option('--export <file>', 'Export assessment results to JSON file')
535
+ .option('--domain <domain>', 'Domain name for assessment')
536
+ .option('--service-type <type>', 'Service type for assessment')
537
+ .option('--token <token>', 'Cloudflare API token')
538
+ .action(async (servicePath, options) => {
539
+ try {
540
+ // Try to load professional orchestration package
541
+ let orchestrationModule;
542
+ try {
543
+ orchestrationModule = await import('@tamyla/clodo-orchestration');
544
+ } catch (err) {
545
+ console.error(chalk.red('āŒ clodo-orchestration package not found'));
546
+ console.error(chalk.yellow('šŸ’” Install with: npm install @tamyla/clodo-orchestration'));
547
+ process.exit(1);
548
+ }
549
+
550
+ const {
551
+ CapabilityAssessmentEngine,
552
+ ServiceAutoDiscovery,
553
+ runAssessmentWorkflow
554
+ } = orchestrationModule;
555
+
556
+ const targetPath = servicePath || process.cwd();
557
+ console.log(chalk.cyan('\n🧠 Professional Capability Assessment'));
558
+ console.log(chalk.gray('─'.repeat(60)));
559
+ console.log(chalk.white(`Service Path: ${targetPath}`));
560
+
561
+ if (options.domain) {
562
+ console.log(chalk.white(`Domain: ${options.domain}`));
563
+ }
564
+ if (options.serviceType) {
565
+ console.log(chalk.white(`Service Type: ${options.serviceType}`));
566
+ }
567
+ console.log(chalk.gray('─'.repeat(60)));
568
+
569
+ // Use the assessment workflow
570
+ const assessment = await runAssessmentWorkflow({
571
+ servicePath: targetPath,
572
+ domain: options.domain,
573
+ serviceType: options.serviceType,
574
+ token: options.token || process.env.CLOUDFLARE_API_TOKEN
575
+ });
576
+
577
+ // Display results
578
+ console.log(chalk.cyan('\nāœ… Assessment Results'));
579
+ console.log(chalk.gray('─'.repeat(60)));
580
+ console.log(chalk.white(`Service Type: ${assessment.mergedInputs?.serviceType || assessment.serviceType || 'Not determined'}`));
581
+ console.log(chalk.white(`Confidence: ${assessment.confidence}%`));
582
+
583
+ if (assessment.gapAnalysis?.missing) {
584
+ console.log(chalk.white(`Missing Capabilities: ${assessment.gapAnalysis.missing.length}`));
585
+ if (assessment.gapAnalysis.missing.length > 0) {
586
+ console.log(chalk.yellow('\nāš ļø Missing:'));
587
+ assessment.gapAnalysis.missing.forEach(gap => {
588
+ console.log(chalk.yellow(` • ${gap.capability}: ${gap.reason || 'Not available'}`));
589
+ });
590
+ }
591
+ }
592
+
593
+ console.log(chalk.gray('─'.repeat(60)));
594
+
595
+ // Export results if requested
596
+ if (options.export) {
597
+ require('fs').writeFileSync(options.export, JSON.stringify(assessment, null, 2));
598
+ console.log(chalk.green(`\nšŸ“„ Results exported to: ${options.export}`));
599
+ }
600
+
601
+ } catch (error) {
602
+ console.error(chalk.red(`Assessment failed: ${error.message}`));
603
+ if (process.env.DEBUG) {
604
+ console.error(chalk.gray(error.stack));
605
+ }
606
+ process.exit(1);
607
+ }
608
+ });
609
+
530
610
  // Deploy command - using existing InputCollector + CustomerConfigLoader (reusable pattern)
531
611
  program
532
612
  .command('deploy')
@@ -698,6 +778,19 @@ program
698
778
  // Tier 3: Execute deployment
699
779
  console.log(chalk.cyan('\nāš™ļø Tier 3: Automated Deployment\n'));
700
780
 
781
+ // OPTIONAL: Try to load professional orchestration package if available
782
+ let professionalOrchestration = null;
783
+ try {
784
+ const { runAssessmentWorkflow } = await import('@tamyla/clodo-orchestration');
785
+ professionalOrchestration = { runAssessmentWorkflow };
786
+ console.log(chalk.cyan('šŸ“Š Professional Edition (clodo-orchestration) detected'));
787
+ } catch (err) {
788
+ // clodo-orchestration not installed, continue with standard assessment
789
+ if (process.env.DEBUG) {
790
+ console.log(chalk.gray(` ā„¹ļø clodo-orchestration not available: ${err.message}`));
791
+ }
792
+ }
793
+
701
794
  // INTEGRATION: Add intelligent service discovery and assessment
702
795
  console.log(chalk.cyan('🧠 Performing Intelligent Service Assessment...'));
703
796
  const { CapabilityAssessmentEngine } = await import('../dist/service-management/CapabilityAssessmentEngine.js');
@@ -17,8 +17,13 @@ import { execSync } from 'child_process';
17
17
  import { fileURLToPath } from 'url';
18
18
 
19
19
  const __dirname = (() => {
20
- const filename = fileURLToPath(import.meta.url);
21
- return dirname(filename);
20
+ try {
21
+ const filename = fileURLToPath(import.meta.url);
22
+ return dirname(filename);
23
+ } catch (error) {
24
+ // Fallback for test environments - use current working directory
25
+ return process.cwd();
26
+ }
22
27
  })();
23
28
 
24
29
  const SECRET_CONFIGS = {
@@ -3,10 +3,16 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSy
3
3
  import { resolve, join } from 'path';
4
4
  import toml from '@iarna/toml';
5
5
  import { createDomainConfigSchema, validateDomainConfig, createDomainRegistry } from './domains.js';
6
- import { createLogger } from '../utils/index.js';
7
6
  import { getDirname } from '../utils/esm-helper.js';
8
7
  const __dirname = getDirname(import.meta.url, 'src/config');
9
- const logger = createLogger('CustomerConfig');
8
+
9
+ // Simple inline logger to avoid circular dependency with index.js
10
+ const logger = {
11
+ info: (message, ...args) => console.log(`[CustomerConfig] ${message}`, ...args),
12
+ error: (message, ...args) => console.error(`[CustomerConfig] ${message}`, ...args),
13
+ warn: (message, ...args) => console.warn(`[CustomerConfig] ${message}`, ...args),
14
+ debug: (message, ...args) => console.debug(`[CustomerConfig] ${message}`, ...args)
15
+ };
10
16
 
11
17
  /**
12
18
  * Customer Configuration Manager
@@ -1,5 +1,11 @@
1
- import { deepMerge, validateRequired, createLogger } from '../utils/index.js';
2
- const logger = createLogger('DomainConfig');
1
+ // Simple inline logger to avoid circular dependency with index.js
2
+ import { validateRequired, deepMerge } from '../utils/index.js';
3
+ const logger = {
4
+ info: (message, ...args) => console.log(`[DomainConfig] ${message}`, ...args),
5
+ error: (message, ...args) => console.error(`[DomainConfig] ${message}`, ...args),
6
+ warn: (message, ...args) => console.warn(`[DomainConfig] ${message}`, ...args),
7
+ debug: (message, ...args) => console.debug(`[DomainConfig] ${message}`, ...args)
8
+ };
3
9
 
4
10
  /**
5
11
  * Creates a base domain configuration schema
@@ -1,5 +1,10 @@
1
- import { createLogger } from '../utils/index.js';
2
- const logger = createLogger('FeatureFlags');
1
+ // Simple inline logger to avoid circular dependency with index.js
2
+ const logger = {
3
+ info: (message, ...args) => console.log(`[FeatureFlagManager] ${message}`, ...args),
4
+ error: (message, ...args) => console.error(`[FeatureFlagManager] ${message}`, ...args),
5
+ warn: (message, ...args) => console.warn(`[FeatureFlagManager] ${message}`, ...args),
6
+ debug: (message, ...args) => console.debug(`[FeatureFlagManager] ${message}`, ...args)
7
+ };
3
8
 
4
9
  /**
5
10
  * Feature Flag Manager Class
package/dist/index.js CHANGED
@@ -24,6 +24,14 @@ export { WranglerDeployer } from './deployment/wrangler-deployer.js';
24
24
  // Security components
25
25
  export * from './security/index.js';
26
26
 
27
+ // Service management components
28
+ export { ServiceCreator, createService } from './service-management/ServiceCreator.js';
29
+ export { ServiceOrchestrator } from './service-management/ServiceOrchestrator.js';
30
+ export { InputHandler } from './service-management/handlers/InputHandler.js';
31
+ export { ConfirmationHandler } from './service-management/handlers/ConfirmationHandler.js';
32
+ export { GenerationHandler } from './service-management/handlers/GenerationHandler.js';
33
+ export { ValidationHandler } from './service-management/handlers/ValidationHandler.js';
34
+
27
35
  // Framework version info
28
36
  export const FRAMEWORK_VERSION = '1.0.0';
29
37
  export const FRAMEWORK_NAME = 'Clodo Framework';
@@ -36,8 +36,13 @@ import { fileURLToPath } from 'url';
36
36
  import { dirname, join, relative } from 'path';
37
37
  import { existsSync, mkdirSync, writeFileSync } from 'fs';
38
38
  const __dirname = (() => {
39
- const filename = fileURLToPath(import.meta.url);
40
- return dirname(filename);
39
+ try {
40
+ const filename = fileURLToPath(import.meta.url);
41
+ return dirname(filename);
42
+ } catch (error) {
43
+ // Fallback for test environments - use current working directory
44
+ return process.cwd();
45
+ }
41
46
  })();
42
47
  export class GenerationEngine {
43
48
  constructor(options = {}) {
@@ -148,12 +153,14 @@ export class GenerationEngine {
148
153
  const wranglerConfig = serviceInitializer.generateWranglerConfig(coreInputs.serviceName, {
149
154
  type: coreInputs.serviceType,
150
155
  env: coreInputs.environment
151
- }, [{
152
- domain: coreInputs.domainName,
153
- accountId: coreInputs.cloudflareAccountId,
154
- zoneId: coreInputs.cloudflareZoneId,
155
- name: `${coreInputs.serviceName}-${coreInputs.environment}`
156
- }]);
156
+ }, {
157
+ domains: [{
158
+ domain: coreInputs.domainName,
159
+ accountId: coreInputs.cloudflareAccountId,
160
+ zoneId: coreInputs.cloudflareZoneId,
161
+ name: `${coreInputs.serviceName}-${coreInputs.environment}`
162
+ }]
163
+ });
157
164
 
158
165
  // Write wrangler.toml
159
166
  const wranglerPath = join(servicePath, 'wrangler.toml');
@@ -177,12 +184,14 @@ export class GenerationEngine {
177
184
  const domainsContent = serviceInitializer.generateDomainsConfig(coreInputs.serviceName, {
178
185
  type: coreInputs.serviceType,
179
186
  env: coreInputs.environment
180
- }, [{
181
- domain: coreInputs.domainName,
182
- accountId: coreInputs.cloudflareAccountId,
183
- zoneId: coreInputs.cloudflareZoneId,
184
- name: `${coreInputs.serviceName}-${coreInputs.environment}`
185
- }]);
187
+ }, {
188
+ domains: [{
189
+ domain: coreInputs.domainName,
190
+ accountId: coreInputs.cloudflareAccountId,
191
+ zoneId: coreInputs.cloudflareZoneId,
192
+ name: `${coreInputs.serviceName}-${coreInputs.environment}`
193
+ }]
194
+ });
186
195
  const domainsPath = join(servicePath, 'src', 'config', 'domains.js');
187
196
  writeFileSync(domainsPath, domainsContent, 'utf8');
188
197
  files.push(domainsPath);
@@ -15,7 +15,9 @@ import { createInterface } from 'readline';
15
15
  import chalk from 'chalk';
16
16
  import { validateServiceName, validateDomainName } from '../utils/validation.js';
17
17
  import { uiStructuresLoader } from '../utils/ui-structures-loader.js';
18
- import { CapabilityAssessmentEngine } from './CapabilityAssessmentEngine.js';
18
+
19
+ // Assessment capabilities moved to @tamyla/clodo-orchestration (professional edition)
20
+
19
21
  export class InputCollector {
20
22
  constructor(options = {}) {
21
23
  this.interactive = options.interactive !== false;
@@ -82,10 +84,7 @@ export class InputCollector {
82
84
  required: true
83
85
  };
84
86
 
85
- // Run assessment after API token is collected
86
- if (inputDef.id === 'cloudflare-api-token' && value) {
87
- await this.runDeploymentReadinessAssessment(result.coreInputs);
88
- }
87
+ // Assessment moved to professional edition (@tamyla/clodo-orchestration)
89
88
  }
90
89
  }
91
90
 
@@ -579,98 +578,6 @@ export class InputCollector {
579
578
  });
580
579
  }
581
580
 
582
- /**
583
- * Run deployment readiness assessment after API token collection
584
- */
585
- async runDeploymentReadinessAssessment(coreInputs) {
586
- console.log(chalk.cyan('\nšŸ” Running Deployment Readiness Assessment...'));
587
- console.log(chalk.gray('Analyzing your inputs for deployment feasibility...\n'));
588
- try {
589
- // Convert core inputs to assessment format
590
- const assessmentInputs = {
591
- serviceName: coreInputs['service-name']?.value,
592
- serviceType: coreInputs['service-type']?.value || 'generic',
593
- domainName: coreInputs['domain-name']?.value,
594
- environment: coreInputs['environment']?.value || 'development',
595
- cloudflareToken: coreInputs['cloudflare-api-token']?.value,
596
- customerName: coreInputs['customer-name']?.value
597
- };
598
-
599
- // Create assessment engine with caching enabled
600
- const assessmentEngine = new CapabilityAssessmentEngine(process.cwd(), {
601
- cacheEnabled: true,
602
- cache: {
603
- ttl: 10 * 60 * 1000
604
- } // 10 minutes for interactive sessions
605
- });
606
-
607
- // Run assessment
608
- const assessment = await assessmentEngine.assessCapabilities(assessmentInputs);
609
-
610
- // Display results
611
- this.displayAssessmentResults(assessment);
612
-
613
- // Store assessment in result for later use
614
- return assessment;
615
- } catch (error) {
616
- console.log(chalk.yellow(`āš ļø Assessment failed: ${error.message}`));
617
- console.log(chalk.gray('Continuing with service creation...'));
618
- return null;
619
- }
620
- }
621
-
622
- /**
623
- * Display assessment results in user-friendly format
624
- */
625
- displayAssessmentResults(assessment) {
626
- const {
627
- capabilityManifest,
628
- gapAnalysis,
629
- confidence,
630
- cached
631
- } = assessment;
632
- console.log(chalk.cyan('\nšŸ“Š Assessment Results:'));
633
- console.log(chalk.gray('─'.repeat(40)));
634
-
635
- // Service type and confidence
636
- console.log(chalk.white(`Service Type: ${capabilityManifest.serviceType}`));
637
- console.log(chalk.white(`Confidence: ${confidence.toFixed(1)}%`));
638
- if (cached) {
639
- console.log(chalk.gray('(Results loaded from cache)'));
640
- }
641
-
642
- // Gap analysis summary
643
- const missingCount = gapAnalysis.missing.length;
644
- const blockedCount = gapAnalysis.missing.filter(gap => gap.priority === 'blocked').length;
645
- if (missingCount === 0) {
646
- console.log(chalk.green('āœ… All required capabilities detected'));
647
- } else {
648
- console.log(chalk.yellow(`āš ļø ${missingCount} capability gap(s) found`));
649
- if (blockedCount > 0) {
650
- console.log(chalk.red(`🚫 ${blockedCount} deployment blocker(s) detected`));
651
- }
652
-
653
- // Show top 3 gaps
654
- gapAnalysis.missing.slice(0, 3).forEach(gap => {
655
- const icon = gap.priority === 'blocked' ? '🚫' : 'āš ļø';
656
- console.log(chalk.gray(` ${icon} ${gap.capability}: ${gap.reason}`));
657
- });
658
- if (gapAnalysis.missing.length > 3) {
659
- console.log(chalk.gray(` ... and ${gapAnalysis.missing.length - 3} more`));
660
- }
661
- }
662
-
663
- // Recommendations
664
- if (assessment.recommendations && assessment.recommendations.length > 0) {
665
- console.log(chalk.blue('\nšŸ’” Recommendations:'));
666
- assessment.recommendations.slice(0, 2).forEach(rec => {
667
- console.log(chalk.gray(` • ${rec.message}`));
668
- });
669
- }
670
- console.log(chalk.gray('\n─'.repeat(40)));
671
- console.log(chalk.white('Continue with service creation? (Assessment will run again before generation)'));
672
- }
673
-
674
581
  /**
675
582
  * Close readline interface and clean up
676
583
  */
@@ -9,7 +9,15 @@ import { fileURLToPath } from 'url';
9
9
  const SERVICE_TYPES = ['data-service', 'auth-service', 'content-service', 'api-gateway', 'generic'];
10
10
  export class ServiceCreator {
11
11
  constructor(options = {}) {
12
- this.templatesDir = options.templatesDir || join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'templates');
12
+ const templatesDir = (() => {
13
+ try {
14
+ return join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'templates');
15
+ } catch (error) {
16
+ // Fallback for test environments - use current working directory
17
+ return join(process.cwd(), 'templates');
18
+ }
19
+ })();
20
+ this.templatesDir = options.templatesDir || templatesDir;
13
21
  this.serviceTypes = options.serviceTypes || SERVICE_TYPES;
14
22
  }
15
23
 
@@ -6,11 +6,20 @@
6
6
  import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, readdirSync } from 'fs';
7
7
  import { join, dirname, resolve } from 'path';
8
8
  import { fileURLToPath } from 'url';
9
- const FRAMEWORK_ROOT = (() => {
10
- const filename = fileURLToPath(import.meta.url);
11
- const dirname_ = dirname(filename);
12
- return resolve(dirname_, '..', '..');
13
- })();
9
+
10
+ // Get framework root - handle both ES module and CommonJS environments
11
+ const getFrameworkRoot = () => {
12
+ try {
13
+ // Try ES module approach
14
+ const filename = fileURLToPath(import.meta.url);
15
+ const dirname_ = dirname(filename);
16
+ return resolve(dirname_, '..', '..');
17
+ } catch (error) {
18
+ // Fallback for test environments - use current working directory
19
+ return process.cwd();
20
+ }
21
+ };
22
+ const FRAMEWORK_ROOT = getFrameworkRoot();
14
23
  const TEMPLATES_DIR = join(FRAMEWORK_ROOT, 'templates');
15
24
  const SERVICE_TYPES = ['generic', 'data-service', 'auth-service', 'content-service', 'api-gateway'];
16
25
  export class ServiceInitializer {