@tamyla/clodo-framework 4.0.7 → 4.0.10
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 +14 -0
- package/dist/cli/clodo-service.js +13 -7
- package/dist/cli/commands/init-config.js +39 -4
- package/dist/config/customers.js +0 -2
- package/dist/lib/shared/deployment/validator.js +0 -3
- package/dist/lib/shared/deployment/workflows/interactive-deployment-coordinator.js +4 -0
- package/dist/orchestration/multi-domain-orchestrator.js +87 -17
- package/dist/simple-api.js +3 -0
- package/dist/utils/deployment/wrangler-config-manager.js +29 -8
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [4.0.10](https://github.com/tamylaa/clodo-framework/compare/v4.0.9...v4.0.10) (2025-12-10)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* clean up test artifacts and temporary files ([0eb6cda](https://github.com/tamylaa/clodo-framework/commit/0eb6cdac68bce6d07e567f4b7810f4d94752b7c0))
|
|
7
|
+
|
|
8
|
+
## [4.0.8](https://github.com/tamylaa/clodo-framework/compare/v4.0.7...v4.0.8) (2025-12-09)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* respect user-provided worker and database names in deployment ([04ffc38](https://github.com/tamylaa/clodo-framework/commit/04ffc382ab910d4d260ba5c3f2e421763587948a))
|
|
14
|
+
|
|
1
15
|
## [4.0.7](https://github.com/tamylaa/clodo-framework/compare/v4.0.6...v4.0.7) (2025-12-09)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -16,7 +16,11 @@
|
|
|
16
16
|
* - list-types List available service types and their features
|
|
17
17
|
*/
|
|
18
18
|
import { Command } from 'commander';
|
|
19
|
+
import { join, dirname } from 'path';
|
|
20
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
19
21
|
import chalk from 'chalk';
|
|
22
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
23
|
+
const __dirname = dirname(__filename);
|
|
20
24
|
|
|
21
25
|
// Create program instance
|
|
22
26
|
const program = new Command();
|
|
@@ -25,33 +29,35 @@ program.name('clodo-service').description('Unified conversational CLI for Clodo
|
|
|
25
29
|
// Dynamically load available command modules
|
|
26
30
|
// This makes the CLI resilient if some commands are excluded from the package
|
|
27
31
|
async function registerAvailableCommands() {
|
|
32
|
+
// Use absolute paths to ensure commands are found regardless of working directory
|
|
33
|
+
const commandsDir = join(__dirname, 'commands');
|
|
28
34
|
const commands = [{
|
|
29
35
|
name: 'create',
|
|
30
|
-
path: '
|
|
36
|
+
path: pathToFileURL(join(commandsDir, 'create.js')).href,
|
|
31
37
|
register: 'registerCreateCommand'
|
|
32
38
|
}, {
|
|
33
39
|
name: 'deploy',
|
|
34
|
-
path: '
|
|
40
|
+
path: pathToFileURL(join(commandsDir, 'deploy.js')).href,
|
|
35
41
|
register: 'registerDeployCommand'
|
|
36
42
|
}, {
|
|
37
43
|
name: 'validate',
|
|
38
|
-
path: '
|
|
44
|
+
path: pathToFileURL(join(commandsDir, 'validate.js')).href,
|
|
39
45
|
register: 'registerValidateCommand'
|
|
40
46
|
}, {
|
|
41
47
|
name: 'update',
|
|
42
|
-
path: '
|
|
48
|
+
path: pathToFileURL(join(commandsDir, 'update.js')).href,
|
|
43
49
|
register: 'registerUpdateCommand'
|
|
44
50
|
}, {
|
|
45
51
|
name: 'diagnose',
|
|
46
|
-
path: '
|
|
52
|
+
path: pathToFileURL(join(commandsDir, 'diagnose.js')).href,
|
|
47
53
|
register: 'registerDiagnoseCommand'
|
|
48
54
|
}, {
|
|
49
55
|
name: 'assess',
|
|
50
|
-
path: '
|
|
56
|
+
path: pathToFileURL(join(commandsDir, 'assess.js')).href,
|
|
51
57
|
register: 'registerAssessCommand'
|
|
52
58
|
}, {
|
|
53
59
|
name: 'init-config',
|
|
54
|
-
path: '
|
|
60
|
+
path: pathToFileURL(join(commandsDir, 'init-config.js')).href,
|
|
55
61
|
register: 'registerInitConfigCommand'
|
|
56
62
|
}];
|
|
57
63
|
for (const cmd of commands) {
|
|
@@ -5,14 +5,46 @@
|
|
|
5
5
|
* Copies the framework's validation-config.json to the service directory for customization
|
|
6
6
|
*/
|
|
7
7
|
import { copyFile, access } from 'fs/promises';
|
|
8
|
-
import { join, dirname } from 'path';
|
|
8
|
+
import { join, dirname, resolve } from 'path';
|
|
9
9
|
import { fileURLToPath } from 'url';
|
|
10
10
|
import chalk from 'chalk';
|
|
11
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
12
|
const __dirname = dirname(__filename);
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Find the framework's config directory by walking up from the current working directory
|
|
16
|
+
* or from the command file location
|
|
17
|
+
*/
|
|
18
|
+
export async function findFrameworkConfig() {
|
|
19
|
+
// First try relative to this command file
|
|
20
|
+
const relativeToCommand = join(__dirname, '../../config/validation-config.json');
|
|
21
|
+
try {
|
|
22
|
+
// Check if the file exists at the expected location
|
|
23
|
+
await access(relativeToCommand);
|
|
24
|
+
return relativeToCommand;
|
|
25
|
+
} catch {
|
|
26
|
+
// If not found, try to find it by walking up from current working directory
|
|
27
|
+
let currentDir = process.cwd();
|
|
28
|
+
const maxDepth = 10;
|
|
29
|
+
for (let i = 0; i < maxDepth; i++) {
|
|
30
|
+
const candidatePath = join(currentDir, 'config', 'validation-config.json');
|
|
31
|
+
try {
|
|
32
|
+
await access(candidatePath);
|
|
33
|
+
return candidatePath;
|
|
34
|
+
} catch {
|
|
35
|
+
const parentDir = dirname(currentDir);
|
|
36
|
+
if (parentDir === currentDir) {
|
|
37
|
+
// Reached root directory
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
currentDir = parentDir;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// As fallback, try absolute path from command location
|
|
45
|
+
return resolve(__dirname, '../../config/validation-config.json');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
16
48
|
|
|
17
49
|
/**
|
|
18
50
|
* Register the init-config command with the CLI
|
|
@@ -23,6 +55,9 @@ export function registerInitConfigCommand(program) {
|
|
|
23
55
|
async function handler(options) {
|
|
24
56
|
const targetPath = join(process.cwd(), 'validation-config.json');
|
|
25
57
|
try {
|
|
58
|
+
// Find the framework config file
|
|
59
|
+
const frameworkConfigPath = await findFrameworkConfig();
|
|
60
|
+
|
|
26
61
|
// Check if file already exists
|
|
27
62
|
try {
|
|
28
63
|
await access(targetPath);
|
|
@@ -38,7 +73,7 @@ async function handler(options) {
|
|
|
38
73
|
}
|
|
39
74
|
|
|
40
75
|
// Copy framework config to service directory
|
|
41
|
-
await copyFile(
|
|
76
|
+
await copyFile(frameworkConfigPath, targetPath);
|
|
42
77
|
console.log(chalk.green('✅ Successfully initialized validation-config.json'));
|
|
43
78
|
console.log(chalk.gray('\n📝 Configuration file details:'));
|
|
44
79
|
console.log(chalk.gray(` Location: ${targetPath}`));
|
package/dist/config/customers.js
CHANGED
|
@@ -38,9 +38,7 @@ export class CustomerConfigurationManager {
|
|
|
38
38
|
const customerDir = resolve(this.configDir, 'customers', customerName);
|
|
39
39
|
|
|
40
40
|
// Create customer directory structure
|
|
41
|
-
console.log(`DEBUG: Checking if directory exists: ${customerDir}`);
|
|
42
41
|
if (!fs.existsSync(customerDir)) {
|
|
43
|
-
console.log(`DEBUG: Creating directory: ${customerDir}`);
|
|
44
42
|
fs.mkdirSync(customerDir, {
|
|
45
43
|
recursive: true
|
|
46
44
|
});
|
|
@@ -598,9 +598,6 @@ export class DeploymentValidator {
|
|
|
598
598
|
}
|
|
599
599
|
async validateDomainEndpoints(domain) {
|
|
600
600
|
console.log(` Validating endpoints for ${domain}...`);
|
|
601
|
-
console.log(` 🔧 DEBUG: skipEndpointCheck = ${this.options?.skipEndpointCheck}`);
|
|
602
|
-
console.log(` 🔧 DEBUG: deploymentType = ${this.options?.deploymentType}`);
|
|
603
|
-
console.log(` 🔧 DEBUG: options = ${JSON.stringify(this.options)}`);
|
|
604
601
|
|
|
605
602
|
// Skip endpoint validation for new deployments
|
|
606
603
|
if (this.options?.skipEndpointCheck) {
|
|
@@ -214,6 +214,10 @@ export class InteractiveDeploymentCoordinator {
|
|
|
214
214
|
environment: this.deploymentState.config.environment,
|
|
215
215
|
domain: this.deploymentState.config.credentials.zoneName,
|
|
216
216
|
serviceName: this.options.serviceName,
|
|
217
|
+
workerName: this.deploymentState.config.worker?.name,
|
|
218
|
+
// Pass the collected worker name
|
|
219
|
+
databaseName: this.deploymentState.resources.database?.name,
|
|
220
|
+
// Pass the collected database name
|
|
217
221
|
dryRun: this.deploymentState.config.dryRun,
|
|
218
222
|
credentials: this.deploymentState.config.credentials
|
|
219
223
|
});
|
|
@@ -36,6 +36,8 @@ export class MultiDomainOrchestrator {
|
|
|
36
36
|
this.parallelDeployments = options.parallelDeployments || 3;
|
|
37
37
|
this.servicePath = options.servicePath || process.cwd();
|
|
38
38
|
this.serviceName = options.serviceName || 'data-service'; // Service name for custom domain (e.g., 'data-service', 'auth-service')
|
|
39
|
+
this.workerName = options.workerName; // Specific worker name to use (optional)
|
|
40
|
+
this.databaseName = options.databaseName; // Specific database name to use (optional)
|
|
39
41
|
|
|
40
42
|
// Wrangler config path - allows using customer-specific wrangler.toml files
|
|
41
43
|
// If not specified, wrangler uses the default wrangler.toml in servicePath
|
|
@@ -293,13 +295,21 @@ export class MultiDomainOrchestrator {
|
|
|
293
295
|
}
|
|
294
296
|
|
|
295
297
|
/**
|
|
296
|
-
*
|
|
298
|
+
* Get base service name from worker name (remove environment suffixes)
|
|
299
|
+
* @returns {string} Base service name for URL generation
|
|
297
300
|
*/
|
|
301
|
+
getBaseServiceName() {
|
|
302
|
+
if (this.workerName) {
|
|
303
|
+
// Remove environment suffixes from worker name to get base service name
|
|
304
|
+
return this.workerName.replace(/-development$|-staging$|-production$/, '');
|
|
305
|
+
}
|
|
306
|
+
return this.serviceName || 'data-service';
|
|
307
|
+
}
|
|
298
308
|
async setupDomainDatabase(domain) {
|
|
299
309
|
console.log(` 🗄️ Setting up database for ${domain}`);
|
|
300
310
|
if (this.dryRun) {
|
|
301
311
|
console.log(` � DRY RUN: Would create database for ${domain}`);
|
|
302
|
-
const databaseName = `${domain.replace(/\./g, '-')}-${this.environment}-db`;
|
|
312
|
+
const databaseName = this.databaseName || `${domain.replace(/\./g, '-')}-${this.environment}-db`;
|
|
303
313
|
return {
|
|
304
314
|
databaseName,
|
|
305
315
|
databaseId: 'dry-run-id',
|
|
@@ -308,7 +318,8 @@ export class MultiDomainOrchestrator {
|
|
|
308
318
|
}
|
|
309
319
|
try {
|
|
310
320
|
// Create D1 database using Cloudflare ops
|
|
311
|
-
|
|
321
|
+
// Use provided database name, or generate one based on domain/environment
|
|
322
|
+
const databaseName = this.databaseName || `${domain.replace(/\./g, '-')}-${this.environment}-db`;
|
|
312
323
|
|
|
313
324
|
// Check if database already exists
|
|
314
325
|
console.log(` � Checking if database exists: ${databaseName}`);
|
|
@@ -526,7 +537,7 @@ export class MultiDomainOrchestrator {
|
|
|
526
537
|
if (this.dryRun) {
|
|
527
538
|
console.log(` 🔍 DRY RUN: Would deploy worker for ${domain}`);
|
|
528
539
|
// Use centralized domain template from validation-config.json
|
|
529
|
-
const customUrl = buildCustomDomain(this.
|
|
540
|
+
const customUrl = buildCustomDomain(this.getBaseServiceName(), domain, this.environment);
|
|
530
541
|
return {
|
|
531
542
|
url: customUrl,
|
|
532
543
|
deployed: false,
|
|
@@ -544,7 +555,8 @@ export class MultiDomainOrchestrator {
|
|
|
544
555
|
// Generate or update customer config with current deployment parameters
|
|
545
556
|
const customerConfigPath = await this.wranglerConfigManager.generateCustomerConfig(this.cloudflareZoneName, {
|
|
546
557
|
accountId: this.cloudflareAccountId,
|
|
547
|
-
environment: this.environment
|
|
558
|
+
environment: this.environment,
|
|
559
|
+
workerName: this.workerName // Pass specific worker name if provided
|
|
548
560
|
});
|
|
549
561
|
|
|
550
562
|
// Copy customer config to root (ephemeral working copy for this deployment)
|
|
@@ -609,7 +621,7 @@ export class MultiDomainOrchestrator {
|
|
|
609
621
|
|
|
610
622
|
// Construct custom domain URL using centralized template from validation-config.json
|
|
611
623
|
// Handles all environment patterns: production, staging, development
|
|
612
|
-
const customUrl = buildCustomDomain(this.
|
|
624
|
+
const customUrl = buildCustomDomain(this.getBaseServiceName(), domain, this.environment);
|
|
613
625
|
|
|
614
626
|
// Store URLs in domain state
|
|
615
627
|
const domainState = this.portfolioState.domainStates.get(domain);
|
|
@@ -621,6 +633,11 @@ export class MultiDomainOrchestrator {
|
|
|
621
633
|
console.log(` ✅ Worker deployed successfully`);
|
|
622
634
|
console.log(` 🔗 Worker URL: ${workerUrl}`);
|
|
623
635
|
console.log(` 🔗 Custom URL: ${customUrl}`);
|
|
636
|
+
|
|
637
|
+
// Display custom domain setup instructions
|
|
638
|
+
if (customUrl && workerUrl !== customUrl) {
|
|
639
|
+
this.displayCustomDomainInstructions(customUrl, workerUrl, domain);
|
|
640
|
+
}
|
|
624
641
|
} else {
|
|
625
642
|
console.log(` ✅ Deployment completed (URL not detected in output)`);
|
|
626
643
|
console.log(` 🔗 Expected URL: ${customUrl}`);
|
|
@@ -659,14 +676,22 @@ export class MultiDomainOrchestrator {
|
|
|
659
676
|
return true;
|
|
660
677
|
}
|
|
661
678
|
|
|
662
|
-
// Get the deployment URL from domain state
|
|
679
|
+
// Get the deployment URL from domain state - prefer worker URL for immediate validation
|
|
663
680
|
const domainState = this.portfolioState.domainStates.get(domain);
|
|
664
|
-
const
|
|
665
|
-
|
|
681
|
+
const workerUrl = domainState?.workerUrl;
|
|
682
|
+
const customUrl = domainState?.deploymentUrl;
|
|
683
|
+
|
|
684
|
+
// Use worker URL for health check since it's immediately available after deployment
|
|
685
|
+
// Custom domain may require DNS configuration and won't work immediately
|
|
686
|
+
const healthCheckUrl = workerUrl || customUrl;
|
|
687
|
+
if (!healthCheckUrl) {
|
|
666
688
|
console.log(` ⚠️ No deployment URL found, skipping health check`);
|
|
667
689
|
return true;
|
|
668
690
|
}
|
|
669
|
-
console.log(` 🔍 Running health check: ${
|
|
691
|
+
console.log(` 🔍 Running health check: ${healthCheckUrl}/health`);
|
|
692
|
+
if (workerUrl && customUrl && workerUrl !== customUrl) {
|
|
693
|
+
console.log(` ℹ️ Health check uses worker URL (custom domain requires DNS setup - see below)`);
|
|
694
|
+
}
|
|
670
695
|
|
|
671
696
|
// Retry logic for health checks
|
|
672
697
|
const maxRetries = 3;
|
|
@@ -678,7 +703,7 @@ export class MultiDomainOrchestrator {
|
|
|
678
703
|
console.log(` Attempt ${attempt}/${maxRetries}...`);
|
|
679
704
|
|
|
680
705
|
// Perform actual HTTP health check
|
|
681
|
-
const response = await fetch(`${
|
|
706
|
+
const response = await fetch(`${healthCheckUrl}/health`, {
|
|
682
707
|
method: 'GET',
|
|
683
708
|
headers: {
|
|
684
709
|
'User-Agent': 'Clodo-Orchestrator/2.0'
|
|
@@ -692,7 +717,7 @@ export class MultiDomainOrchestrator {
|
|
|
692
717
|
|
|
693
718
|
// Log successful health check
|
|
694
719
|
this.stateManager.logAuditEvent('HEALTH_CHECK_PASSED', domain, {
|
|
695
|
-
url:
|
|
720
|
+
url: healthCheckUrl,
|
|
696
721
|
status,
|
|
697
722
|
responseTime,
|
|
698
723
|
attempt,
|
|
@@ -703,7 +728,7 @@ export class MultiDomainOrchestrator {
|
|
|
703
728
|
const errorMsg = `Health check returned ${status} - deployment may have issues`;
|
|
704
729
|
console.log(` ⚠️ ${errorMsg}`);
|
|
705
730
|
this.stateManager.logAuditEvent('HEALTH_CHECK_WARNING', domain, {
|
|
706
|
-
url:
|
|
731
|
+
url: healthCheckUrl,
|
|
707
732
|
status,
|
|
708
733
|
responseTime,
|
|
709
734
|
attempt,
|
|
@@ -718,9 +743,9 @@ export class MultiDomainOrchestrator {
|
|
|
718
743
|
const errorMsg = `Health check failed: ${error.message}`;
|
|
719
744
|
if (isLastAttempt) {
|
|
720
745
|
console.log(` ❌ ${errorMsg} (final attempt)`);
|
|
721
|
-
console.log(` 💡 The service may still be deploying. Check manually: curl ${
|
|
746
|
+
console.log(` 💡 The service may still be deploying. Check manually: curl ${healthCheckUrl}/health`);
|
|
722
747
|
this.stateManager.logAuditEvent('HEALTH_CHECK_FAILED', domain, {
|
|
723
|
-
url:
|
|
748
|
+
url: healthCheckUrl,
|
|
724
749
|
error: error.message,
|
|
725
750
|
attempts: maxRetries,
|
|
726
751
|
environment: this.environment
|
|
@@ -738,6 +763,43 @@ export class MultiDomainOrchestrator {
|
|
|
738
763
|
return true;
|
|
739
764
|
}
|
|
740
765
|
|
|
766
|
+
/**
|
|
767
|
+
* Display comprehensive custom domain setup instructions
|
|
768
|
+
* @param {string} customUrl - The custom domain URL
|
|
769
|
+
* @param {string} workerUrl - The worker URL to point DNS to
|
|
770
|
+
* @param {string} domain - The domain name
|
|
771
|
+
*/
|
|
772
|
+
displayCustomDomainInstructions(customUrl, workerUrl, domain) {
|
|
773
|
+
console.log(`\n🌐 Custom Domain Setup Instructions`);
|
|
774
|
+
console.log(`═`.repeat(50));
|
|
775
|
+
console.log(`Your service is deployed and working at: ${workerUrl}`);
|
|
776
|
+
console.log(`To use your custom domain ${customUrl}, follow these steps:\n`);
|
|
777
|
+
console.log(`📋 Step 1: DNS Configuration`);
|
|
778
|
+
console.log(` Create a CNAME record in your DNS settings:`);
|
|
779
|
+
// Extract subdomain from custom URL (everything before the domain)
|
|
780
|
+
const urlObj = new URL(customUrl);
|
|
781
|
+
const subdomain = urlObj.hostname.replace(`.${domain}`, '');
|
|
782
|
+
console.log(` • Name: ${subdomain}`);
|
|
783
|
+
console.log(` • Type: CNAME`);
|
|
784
|
+
console.log(` • Target: ${workerUrl.replace('https://', '')}`);
|
|
785
|
+
console.log(` • TTL: 300 (5 minutes)\n`);
|
|
786
|
+
console.log(`🔧 Step 2: Cloudflare Workers Configuration`);
|
|
787
|
+
console.log(` 1. Go to Cloudflare Dashboard → Workers & Pages`);
|
|
788
|
+
console.log(` 2. Select your worker`);
|
|
789
|
+
console.log(` 3. Go to "Triggers" tab`);
|
|
790
|
+
console.log(` 4. Click "Add Custom Domain"`);
|
|
791
|
+
console.log(` 5. Enter: ${customUrl}\n`);
|
|
792
|
+
console.log(`⏱️ Step 3: Wait for Propagation`);
|
|
793
|
+
console.log(` • DNS changes: 5-30 minutes`);
|
|
794
|
+
console.log(` • SSL certificate: 5-10 minutes`);
|
|
795
|
+
console.log(` • Total setup time: 10-60 minutes\n`);
|
|
796
|
+
console.log(`✅ Step 4: Verify Setup`);
|
|
797
|
+
console.log(` Test your custom domain:`);
|
|
798
|
+
console.log(` curl -v ${customUrl}/health\n`);
|
|
799
|
+
console.log(`💡 Note: Your service is fully functional at the worker URL immediately.`);
|
|
800
|
+
console.log(` The custom domain is optional for branding/user experience.\n`);
|
|
801
|
+
}
|
|
802
|
+
|
|
741
803
|
/**
|
|
742
804
|
* Get rollback plan using state manager
|
|
743
805
|
* @returns {Array} Rollback plan from state manager
|
|
@@ -763,12 +825,14 @@ export class MultiDomainOrchestrator {
|
|
|
763
825
|
}
|
|
764
826
|
|
|
765
827
|
/**
|
|
766
|
-
*
|
|
767
|
-
* @param {Object} options -
|
|
828
|
+
* Deploy a single service to a specific domain/environment
|
|
829
|
+
* @param {Object} options - Deployment options
|
|
768
830
|
* @param {string} options.servicePath - Path to service directory
|
|
769
831
|
* @param {string} options.environment - Target environment
|
|
770
832
|
* @param {string} options.domain - Specific domain to deploy to (used as zone name and domain suffix)
|
|
771
833
|
* @param {string} options.serviceName - Service name for URL generation (e.g., 'data-service', 'auth-service')
|
|
834
|
+
* @param {string} options.workerName - Specific worker name to use
|
|
835
|
+
* @param {string} options.databaseName - Specific database name to use
|
|
772
836
|
* @param {boolean} options.dryRun - Simulate deployment
|
|
773
837
|
* @param {Object} options.credentials - Cloudflare credentials
|
|
774
838
|
* @returns {Promise<Object>} Deployment result
|
|
@@ -779,6 +843,8 @@ export class MultiDomainOrchestrator {
|
|
|
779
843
|
environment = 'production',
|
|
780
844
|
domain,
|
|
781
845
|
serviceName,
|
|
846
|
+
workerName,
|
|
847
|
+
databaseName,
|
|
782
848
|
dryRun = false,
|
|
783
849
|
credentials = {}
|
|
784
850
|
} = options;
|
|
@@ -790,6 +856,10 @@ export class MultiDomainOrchestrator {
|
|
|
790
856
|
servicePath,
|
|
791
857
|
serviceName,
|
|
792
858
|
// Pass through serviceName if provided
|
|
859
|
+
workerName,
|
|
860
|
+
// Pass through workerName if provided
|
|
861
|
+
databaseName,
|
|
862
|
+
// Pass through databaseName if provided
|
|
793
863
|
cloudflareToken: credentials.token,
|
|
794
864
|
cloudflareAccountId: credentials.accountId,
|
|
795
865
|
cloudflareZoneId: credentials.zoneId,
|
package/dist/simple-api.js
CHANGED
|
@@ -34,6 +34,9 @@ export class Clodo {
|
|
|
34
34
|
* @param {string} options.servicePath - Path to service directory
|
|
35
35
|
* @param {string} options.environment - Target environment
|
|
36
36
|
* @param {string} options.domain - Specific domain to deploy to
|
|
37
|
+
* @param {string} options.serviceName - Service name for URL generation
|
|
38
|
+
* @param {string} options.workerName - Specific worker name to use
|
|
39
|
+
* @param {string} options.databaseName - Specific database name to use
|
|
37
40
|
* @param {boolean} options.dryRun - Simulate deployment
|
|
38
41
|
* @returns {Promise<Object>} Deployment result
|
|
39
42
|
*/
|
|
@@ -127,12 +127,14 @@ export class WranglerConfigManager {
|
|
|
127
127
|
* @param {Object} params - Deployment parameters
|
|
128
128
|
* @param {string} params.accountId - Cloudflare account ID
|
|
129
129
|
* @param {string} params.environment - Deployment environment (production, staging, development)
|
|
130
|
+
* @param {string} params.workerName - Specific worker name to use (optional, overrides automatic naming)
|
|
130
131
|
* @returns {Promise<string>} Path to generated/updated customer config
|
|
131
132
|
*/
|
|
132
133
|
async generateCustomerConfig(zoneName, params = {}) {
|
|
133
134
|
const {
|
|
134
135
|
accountId,
|
|
135
|
-
environment = 'production'
|
|
136
|
+
environment = 'production',
|
|
137
|
+
workerName
|
|
136
138
|
} = params;
|
|
137
139
|
if (!zoneName) {
|
|
138
140
|
throw new Error('Zone name is required to generate customer config');
|
|
@@ -186,16 +188,24 @@ export class WranglerConfigManager {
|
|
|
186
188
|
console.log(` ✅ Set account_id to ${accountId} in customer config`);
|
|
187
189
|
}
|
|
188
190
|
|
|
189
|
-
// Update worker name based on zone
|
|
191
|
+
// Update worker name based on zone or provided name
|
|
190
192
|
// Extract base service name and apply zone-based naming
|
|
191
193
|
const customerPrefix = zoneName.split('.')[0]; // clodo.dev → clodo
|
|
192
194
|
|
|
193
195
|
// Update root-level worker name
|
|
194
196
|
if (config.name) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
+
let newWorkerName;
|
|
198
|
+
if (workerName) {
|
|
199
|
+
// Use the provided worker name directly
|
|
200
|
+
newWorkerName = workerName;
|
|
201
|
+
console.log(` ✅ Set worker name to ${newWorkerName} in customer config (provided)`);
|
|
202
|
+
} else {
|
|
203
|
+
// Apply automatic zone-based naming
|
|
204
|
+
const baseName = config.name.replace(/^[^-]+-/, ''); // Remove existing prefix
|
|
205
|
+
newWorkerName = `${customerPrefix}-${baseName}`;
|
|
206
|
+
console.log(` ✅ Set worker name to ${newWorkerName} in customer config (auto-generated)`);
|
|
207
|
+
}
|
|
197
208
|
config.name = newWorkerName;
|
|
198
|
-
console.log(` ✅ Set worker name to ${newWorkerName} in customer config`);
|
|
199
209
|
}
|
|
200
210
|
|
|
201
211
|
// Update SERVICE_DOMAIN in environment variables (all environments)
|
|
@@ -227,9 +237,20 @@ export class WranglerConfigManager {
|
|
|
227
237
|
if (config.env) {
|
|
228
238
|
for (const [envName, envConfig] of Object.entries(config.env)) {
|
|
229
239
|
if (envConfig.name) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
240
|
+
let newEnvWorkerName;
|
|
241
|
+
if (workerName) {
|
|
242
|
+
// Derive environment-specific name from provided worker name
|
|
243
|
+
const baseName = workerName.replace(/-service$/, ''); // Remove -service suffix if present
|
|
244
|
+
const envSuffix = envName === 'production' ? '' : `-${envName}`;
|
|
245
|
+
newEnvWorkerName = `${baseName}${envSuffix}`;
|
|
246
|
+
console.log(` ✅ Set [env.${envName}] worker name to ${newEnvWorkerName} (derived from provided name)`);
|
|
247
|
+
} else {
|
|
248
|
+
// Apply automatic zone-based naming
|
|
249
|
+
const baseName = envConfig.name.replace(/^[^-]+-/, ''); // Remove existing prefix
|
|
250
|
+
newEnvWorkerName = `${customerPrefix}-${baseName}`;
|
|
251
|
+
console.log(` ✅ Set [env.${envName}] worker name to ${newEnvWorkerName} (auto-generated)`);
|
|
252
|
+
}
|
|
253
|
+
envConfig.name = newEnvWorkerName;
|
|
233
254
|
}
|
|
234
255
|
if (envConfig.vars) {
|
|
235
256
|
updateServiceDomain(envConfig.vars);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tamyla/clodo-framework",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.10",
|
|
4
4
|
"description": "Reusable framework for Clodo-style software architecture on Cloudflare Workers + D1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -172,7 +172,7 @@
|
|
|
172
172
|
"bugs": {
|
|
173
173
|
"url": "https://github.com/tamylaa/clodo-framework/issues"
|
|
174
174
|
},
|
|
175
|
-
"homepage": "https://clodo
|
|
175
|
+
"homepage": "https://clodo.dev",
|
|
176
176
|
"release": {
|
|
177
177
|
"branches": [
|
|
178
178
|
"main",
|