@tamyla/clodo-framework 4.5.0 โ 4.6.0
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 +28 -0
- package/README.md +157 -13
- package/dist/cli/clodo-service.js +13 -0
- package/dist/cli/commands/config-schema.js +144 -0
- package/dist/cli/commands/create.js +18 -1
- package/dist/cli/commands/deploy.js +61 -2
- package/dist/cli/commands/doctor.js +124 -0
- package/dist/cli/commands/secrets.js +258 -0
- package/dist/cli/security-cli.js +1 -1
- package/dist/index.js +3 -1
- package/dist/security/SecretsManager.js +398 -0
- package/dist/security/index.js +2 -0
- package/dist/service-management/ConfirmationEngine.js +4 -1
- package/dist/service-management/generators/utils/ServiceManifestGenerator.js +5 -1
- package/dist/service-management/handlers/ConfirmationHandler.js +1 -1
- package/dist/service-management/handlers/ValidationHandler.js +696 -0
- package/dist/validation/ConfigSchemaValidator.js +503 -0
- package/dist/validation/configSchemas.js +236 -0
- package/dist/validation/index.js +6 -2
- package/docs/00_START_HERE.md +26 -338
- package/package.json +1 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor Command Module
|
|
3
|
+
* Provides preflight diagnostic checks for Clodo services
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ValidationHandler } from '../../src/service-management/handlers/ValidationHandler.js';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
export function registerDoctorCommand(program) {
|
|
9
|
+
program.command('doctor').description('Run preflight diagnostic checks for service health and deployment readiness').option('--json', 'Output results in JSON format for machine consumption').option('--fix', 'Attempt to automatically fix detected issues').option('--strict', 'Treat warnings as errors (non-zero exit code)').option('--service-path <path>', 'Path to service directory (defaults to current directory)').action(async options => {
|
|
10
|
+
try {
|
|
11
|
+
const handler = new ValidationHandler({
|
|
12
|
+
strict: options.strict
|
|
13
|
+
});
|
|
14
|
+
const results = await handler.runDoctor({
|
|
15
|
+
json: options.json,
|
|
16
|
+
fix: options.fix,
|
|
17
|
+
strict: options.strict,
|
|
18
|
+
servicePath: options.servicePath || process.cwd()
|
|
19
|
+
});
|
|
20
|
+
if (options.json) {
|
|
21
|
+
console.log(JSON.stringify(results, null, 2));
|
|
22
|
+
} else {
|
|
23
|
+
displayHumanReadable(results);
|
|
24
|
+
}
|
|
25
|
+
process.exit(results.exitCode);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error(chalk.red('Doctor command failed:'), error.message);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Display results in human-readable format
|
|
35
|
+
*/
|
|
36
|
+
function displayHumanReadable(results) {
|
|
37
|
+
console.log(chalk.cyan('๐ Clodo Doctor - Preflight Diagnostics'));
|
|
38
|
+
console.log(chalk.gray(`Service: ${results.servicePath}`));
|
|
39
|
+
console.log(chalk.gray(`Timestamp: ${results.timestamp}`));
|
|
40
|
+
console.log('');
|
|
41
|
+
|
|
42
|
+
// Summary
|
|
43
|
+
const {
|
|
44
|
+
summary
|
|
45
|
+
} = results;
|
|
46
|
+
console.log(chalk.bold('Summary:'));
|
|
47
|
+
console.log(` Total checks: ${summary.total}`);
|
|
48
|
+
console.log(chalk.green(` Passed: ${summary.passed}`));
|
|
49
|
+
if (summary.warnings > 0) console.log(chalk.yellow(` Warnings: ${summary.warnings}`));
|
|
50
|
+
if (summary.errors > 0) console.log(chalk.red(` Errors: ${summary.errors}`));
|
|
51
|
+
if (summary.critical > 0) console.log(chalk.red.bold(` Critical: ${summary.critical}`));
|
|
52
|
+
console.log('');
|
|
53
|
+
|
|
54
|
+
// Show fixes applied
|
|
55
|
+
if (results.fixesApplied && results.fixesApplied.length > 0) {
|
|
56
|
+
console.log(chalk.bold('Fixes Applied:'));
|
|
57
|
+
results.fixesApplied.forEach(fix => {
|
|
58
|
+
console.log(chalk.green(` โ
${fix}`));
|
|
59
|
+
});
|
|
60
|
+
console.log('');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Detailed results
|
|
64
|
+
results.checks.forEach(check => {
|
|
65
|
+
const icon = getStatusIcon(check.status);
|
|
66
|
+
const color = getStatusColor(check.status);
|
|
67
|
+
console.log(color(`${icon} ${check.name}`));
|
|
68
|
+
console.log(` ${check.message}`);
|
|
69
|
+
if (check.details && check.details.length > 0) {
|
|
70
|
+
check.details.forEach(detail => {
|
|
71
|
+
console.log(` ${detail}`);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
if (check.fixSuggestions && check.fixSuggestions.length > 0) {
|
|
75
|
+
console.log(chalk.blue(' Suggestions:'));
|
|
76
|
+
check.fixSuggestions.forEach(suggestion => {
|
|
77
|
+
console.log(` โข ${suggestion}`);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
console.log('');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Overall status
|
|
84
|
+
if (results.exitCode === 0) {
|
|
85
|
+
console.log(chalk.green('โ
All checks passed! Service is ready for deployment.'));
|
|
86
|
+
} else {
|
|
87
|
+
console.log(chalk.red('โ Issues found. Address them before deployment.'));
|
|
88
|
+
if (results.fixSuggestions.length > 0) {
|
|
89
|
+
console.log(chalk.blue('Run with --fix to attempt automatic fixes.'));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get status icon
|
|
96
|
+
*/
|
|
97
|
+
function getStatusIcon(status) {
|
|
98
|
+
switch (status) {
|
|
99
|
+
case 'passed':
|
|
100
|
+
return 'โ
';
|
|
101
|
+
case 'warning':
|
|
102
|
+
return 'โ ๏ธ';
|
|
103
|
+
case 'failed':
|
|
104
|
+
return 'โ';
|
|
105
|
+
default:
|
|
106
|
+
return 'โ';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get status color
|
|
112
|
+
*/
|
|
113
|
+
function getStatusColor(status) {
|
|
114
|
+
switch (status) {
|
|
115
|
+
case 'passed':
|
|
116
|
+
return chalk.green;
|
|
117
|
+
case 'warning':
|
|
118
|
+
return chalk.yellow;
|
|
119
|
+
case 'failed':
|
|
120
|
+
return chalk.red;
|
|
121
|
+
default:
|
|
122
|
+
return chalk.gray;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secrets Command Module
|
|
3
|
+
* Provides secret scanning, baseline management, and validation for Clodo services
|
|
4
|
+
*
|
|
5
|
+
* Subcommands:
|
|
6
|
+
* clodo-service secrets scan Scan for potential secrets in the service
|
|
7
|
+
* clodo-service secrets validate Validate scanned secrets against baseline
|
|
8
|
+
* clodo-service secrets baseline Show/update the secrets baseline
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { SecretsManager } from '../../src/security/SecretsManager.js';
|
|
12
|
+
import chalk from 'chalk';
|
|
13
|
+
export function registerSecretsCommand(program) {
|
|
14
|
+
const secrets = program.command('secrets').description('Secret scanning and baseline management for leak prevention');
|
|
15
|
+
|
|
16
|
+
// โโโ secrets scan โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
17
|
+
|
|
18
|
+
secrets.command('scan').description('Scan for potential secrets in the service directory').option('--json', 'Output results in JSON format').option('--include-tests', 'Include test/example/placeholder values in results').option('--service-path <path>', 'Path to service directory (defaults to current directory)').option('--severity <level>', 'Minimum severity to report: critical, high, medium', 'medium').action(async options => {
|
|
19
|
+
try {
|
|
20
|
+
const mgr = new SecretsManager({
|
|
21
|
+
includeTests: options.includeTests
|
|
22
|
+
});
|
|
23
|
+
const servicePath = options.servicePath || process.cwd();
|
|
24
|
+
const findings = await mgr.scan(servicePath);
|
|
25
|
+
|
|
26
|
+
// Filter by severity
|
|
27
|
+
const severityOrder = {
|
|
28
|
+
critical: 3,
|
|
29
|
+
high: 2,
|
|
30
|
+
medium: 1
|
|
31
|
+
};
|
|
32
|
+
const minSeverity = severityOrder[options.severity] || 1;
|
|
33
|
+
const filtered = findings.filter(f => (severityOrder[f.severity] || 0) >= minSeverity);
|
|
34
|
+
if (options.json) {
|
|
35
|
+
console.log(JSON.stringify({
|
|
36
|
+
findings: filtered,
|
|
37
|
+
total: filtered.length,
|
|
38
|
+
servicePath
|
|
39
|
+
}, null, 2));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
displayScanResults(filtered, servicePath);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error(chalk.red('Secrets scan failed:'), error.message);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// โโโ secrets validate โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
50
|
+
|
|
51
|
+
secrets.command('validate').description('Validate secrets against the baseline โ fails if new secrets are found').option('--json', 'Output results in JSON format').option('--strict', 'Exit with non-zero code on any findings (even baselined)').option('--service-path <path>', 'Path to service directory (defaults to current directory)').action(async options => {
|
|
52
|
+
try {
|
|
53
|
+
const mgr = new SecretsManager();
|
|
54
|
+
const servicePath = options.servicePath || process.cwd();
|
|
55
|
+
const result = await mgr.validate(servicePath);
|
|
56
|
+
if (options.json) {
|
|
57
|
+
console.log(JSON.stringify(result, null, 2));
|
|
58
|
+
} else {
|
|
59
|
+
displayValidationResults(result, servicePath);
|
|
60
|
+
}
|
|
61
|
+
const exitCode = result.passed ? 0 : 1;
|
|
62
|
+
if (options.strict && result.totalFindings > 0) {
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
process.exit(exitCode);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error(chalk.red('Secrets validation failed:'), error.message);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// โโโ secrets baseline โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
73
|
+
|
|
74
|
+
const baseline = secrets.command('baseline').description('Manage the .secrets.baseline file');
|
|
75
|
+
|
|
76
|
+
// โโโ secrets baseline show โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
77
|
+
|
|
78
|
+
baseline.command('show').description('Display current baseline entries').option('--json', 'Output results in JSON format').option('--service-path <path>', 'Path to service directory (defaults to current directory)').action(async options => {
|
|
79
|
+
try {
|
|
80
|
+
const mgr = new SecretsManager();
|
|
81
|
+
const servicePath = options.servicePath || process.cwd();
|
|
82
|
+
const entries = await mgr.baselineShow(servicePath);
|
|
83
|
+
if (options.json) {
|
|
84
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
displayBaselineEntries(entries, servicePath);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error(chalk.red('Failed to show baseline:'), error.message);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// โโโ secrets baseline update โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
95
|
+
|
|
96
|
+
baseline.command('update').description('Update baseline with current scan findings').option('--json', 'Output results in JSON format').option('--add-all', 'Add all new findings to the baseline').option('--prune', 'Remove stale entries no longer detected').option('--reason <reason>', 'Reason for updating (audit trail)').option('--service-path <path>', 'Path to service directory (defaults to current directory)').action(async options => {
|
|
97
|
+
try {
|
|
98
|
+
const mgr = new SecretsManager();
|
|
99
|
+
const servicePath = options.servicePath || process.cwd();
|
|
100
|
+
const result = await mgr.baselineUpdate(servicePath, {
|
|
101
|
+
addAll: options.addAll,
|
|
102
|
+
prune: options.prune,
|
|
103
|
+
reason: options.reason
|
|
104
|
+
});
|
|
105
|
+
if (options.json) {
|
|
106
|
+
console.log(JSON.stringify(result, null, 2));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
displayBaselineUpdateResults(result);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(chalk.red('Baseline update failed:'), error.message);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// โโโ secrets patterns โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
117
|
+
|
|
118
|
+
secrets.command('patterns').description('List configured detection patterns and their severity').option('--json', 'Output results in JSON format').action(options => {
|
|
119
|
+
const mgr = new SecretsManager();
|
|
120
|
+
const patterns = mgr.getPatterns();
|
|
121
|
+
if (options.json) {
|
|
122
|
+
console.log(JSON.stringify(patterns, null, 2));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
console.log(chalk.cyan('๐ Secret Detection Patterns'));
|
|
126
|
+
console.log('');
|
|
127
|
+
const grouped = {};
|
|
128
|
+
for (const p of patterns) {
|
|
129
|
+
if (!grouped[p.severity]) grouped[p.severity] = [];
|
|
130
|
+
grouped[p.severity].push(p.name);
|
|
131
|
+
}
|
|
132
|
+
for (const severity of ['critical', 'high', 'medium']) {
|
|
133
|
+
if (grouped[severity]) {
|
|
134
|
+
const color = severity === 'critical' ? chalk.red : severity === 'high' ? chalk.yellow : chalk.blue;
|
|
135
|
+
console.log(color(` [${severity.toUpperCase()}]`));
|
|
136
|
+
grouped[severity].forEach(name => {
|
|
137
|
+
console.log(` โข ${name}`);
|
|
138
|
+
});
|
|
139
|
+
console.log('');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// โโโ Display Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
146
|
+
|
|
147
|
+
function displayScanResults(findings, servicePath) {
|
|
148
|
+
console.log(chalk.cyan('๐ Clodo Secrets Scan'));
|
|
149
|
+
console.log(chalk.gray(`Service: ${servicePath}`));
|
|
150
|
+
console.log('');
|
|
151
|
+
if (findings.length === 0) {
|
|
152
|
+
console.log(chalk.green('โ
No potential secrets found. Code looks clean!'));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
console.log(chalk.yellow(`โ ๏ธ Found ${findings.length} potential secret(s):`));
|
|
156
|
+
console.log('');
|
|
157
|
+
|
|
158
|
+
// Group by severity
|
|
159
|
+
const bySeverity = {
|
|
160
|
+
critical: [],
|
|
161
|
+
high: [],
|
|
162
|
+
medium: []
|
|
163
|
+
};
|
|
164
|
+
findings.forEach(f => {
|
|
165
|
+
if (bySeverity[f.severity]) bySeverity[f.severity].push(f);
|
|
166
|
+
});
|
|
167
|
+
for (const severity of ['critical', 'high', 'medium']) {
|
|
168
|
+
const group = bySeverity[severity];
|
|
169
|
+
if (group.length === 0) continue;
|
|
170
|
+
const color = severity === 'critical' ? chalk.red.bold : severity === 'high' ? chalk.yellow : chalk.blue;
|
|
171
|
+
console.log(color(` โโ ${severity.toUpperCase()} (${group.length}) โโ`));
|
|
172
|
+
group.forEach(f => {
|
|
173
|
+
console.log(` ${chalk.gray(f.file)}:${chalk.white(f.line)} [${f.pattern}]`);
|
|
174
|
+
console.log(` ${chalk.dim(f.match)}`);
|
|
175
|
+
});
|
|
176
|
+
console.log('');
|
|
177
|
+
}
|
|
178
|
+
console.log(chalk.blue('๐ก Run `clodo-service secrets baseline update --add-all` to baseline known findings'));
|
|
179
|
+
console.log(chalk.blue(' Run `clodo-service secrets validate` to check against baseline'));
|
|
180
|
+
}
|
|
181
|
+
function displayValidationResults(result, servicePath) {
|
|
182
|
+
console.log(chalk.cyan('๐ Clodo Secrets Validation'));
|
|
183
|
+
console.log(chalk.gray(`Service: ${servicePath}`));
|
|
184
|
+
console.log('');
|
|
185
|
+
if (result.passed) {
|
|
186
|
+
console.log(chalk.green(`โ
${result.message}`));
|
|
187
|
+
if (result.baselineCount > 0) {
|
|
188
|
+
console.log(chalk.gray(` (${result.baselineCount} known entries in baseline)`));
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
console.log(chalk.red(`โ ${result.message}`));
|
|
192
|
+
console.log('');
|
|
193
|
+
result.newFindings.forEach(f => {
|
|
194
|
+
const color = f.severity === 'critical' ? chalk.red : chalk.yellow;
|
|
195
|
+
console.log(color(` ${f.file}:${f.line} [${f.pattern}] (${f.severity})`));
|
|
196
|
+
console.log(` ${chalk.dim(f.match)}`);
|
|
197
|
+
});
|
|
198
|
+
console.log('');
|
|
199
|
+
console.log(chalk.blue('๐ก Fix the secrets above, or run:'));
|
|
200
|
+
console.log(chalk.blue(' clodo-service secrets baseline update --add-all --reason "reviewed-safe"'));
|
|
201
|
+
}
|
|
202
|
+
if (result.removedFromBaseline.length > 0) {
|
|
203
|
+
console.log('');
|
|
204
|
+
console.log(chalk.gray(`โน๏ธ ${result.removedFromBaseline.length} baseline entries are stale (no longer detected).`));
|
|
205
|
+
console.log(chalk.gray(' Run `clodo-service secrets baseline update --prune` to clean them up.'));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function displayBaselineEntries(entries, servicePath) {
|
|
209
|
+
console.log(chalk.cyan('๐ Secrets Baseline'));
|
|
210
|
+
console.log(chalk.gray(`Service: ${servicePath}`));
|
|
211
|
+
console.log('');
|
|
212
|
+
if (entries.length === 0) {
|
|
213
|
+
console.log(chalk.gray(' No baseline entries. Run `clodo-service secrets scan` to find potential secrets.'));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
console.log(` Total entries: ${entries.length}`);
|
|
217
|
+
console.log('');
|
|
218
|
+
|
|
219
|
+
// Group by file
|
|
220
|
+
const byFile = {};
|
|
221
|
+
entries.forEach(e => {
|
|
222
|
+
if (!byFile[e.file]) byFile[e.file] = [];
|
|
223
|
+
byFile[e.file].push(e);
|
|
224
|
+
});
|
|
225
|
+
Object.entries(byFile).forEach(([file, fileEntries]) => {
|
|
226
|
+
console.log(chalk.white(` ${file}`));
|
|
227
|
+
fileEntries.forEach(e => {
|
|
228
|
+
const info = e.addedAt ? chalk.dim(` (added: ${e.addedAt.split('T')[0]})`) : '';
|
|
229
|
+
const reason = e.reason ? chalk.dim(` [${e.reason}]`) : '';
|
|
230
|
+
console.log(` L${e.line}: [${e.pattern}] ${chalk.dim(e.match)}${info}${reason}`);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
function displayBaselineUpdateResults(result) {
|
|
235
|
+
console.log(chalk.cyan('๐ Baseline Update'));
|
|
236
|
+
console.log('');
|
|
237
|
+
if (result.added > 0) {
|
|
238
|
+
console.log(chalk.green(` โ
Added ${result.added} new entries`));
|
|
239
|
+
}
|
|
240
|
+
if (result.pruned > 0) {
|
|
241
|
+
console.log(chalk.yellow(` ๐งน Pruned ${result.pruned} stale entries`));
|
|
242
|
+
}
|
|
243
|
+
if (result.added === 0 && result.pruned === 0) {
|
|
244
|
+
if (result.newFindings === 0 && result.staleEntries === 0) {
|
|
245
|
+
console.log(chalk.green(' โ
Baseline is already up to date'));
|
|
246
|
+
} else {
|
|
247
|
+
if (result.newFindings > 0) {
|
|
248
|
+
console.log(chalk.yellow(` โ ๏ธ ${result.newFindings} new findings detected but --add-all not specified`));
|
|
249
|
+
}
|
|
250
|
+
if (result.staleEntries > 0) {
|
|
251
|
+
console.log(chalk.gray(` โน๏ธ ${result.staleEntries} stale entries detected but --prune not specified`));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
console.log('');
|
|
256
|
+
console.log(chalk.gray(` Total baseline entries: ${result.total}`));
|
|
257
|
+
console.log(chalk.gray(` Baseline path: ${result.baselinePath}`));
|
|
258
|
+
}
|
package/dist/cli/security-cli.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -58,6 +58,8 @@ export { deployServiceProgrammatic } from './programmatic/deployService.js';
|
|
|
58
58
|
export { validateServiceProgrammatic } from './programmatic/validateService.js';
|
|
59
59
|
export { getFrameworkCapabilities, getFrameworkVersion } from './api/frameworkCapabilities.js';
|
|
60
60
|
export { getAcceptedParameters, validateServicePayload } from './validation/payloadValidation.js';
|
|
61
|
+
export { ConfigSchemaValidator } from './validation/ConfigSchemaValidator.js';
|
|
62
|
+
export { getConfigSchema, getRegisteredConfigTypes } from './validation/configSchemas.js';
|
|
61
63
|
export { IntegrationError, PayloadValidationError, ParameterNotSupportedError, VersionCompatibilityError } from './errors/integrationErrors.js';
|
|
62
64
|
export { MockServiceOrchestrator, createMockFramework } from './testing/mockFramework.js';
|
|
63
65
|
|
|
@@ -74,7 +76,7 @@ export { classifyError, getRecoverySuggestions } from './lib/shared/error-handli
|
|
|
74
76
|
export { FrameworkInfo } from './version/FrameworkInfo.js';
|
|
75
77
|
export { TemplateRuntime } from './utils/TemplateRuntime.js';
|
|
76
78
|
export { HealthChecker } from './monitoring/HealthChecker.js';
|
|
77
|
-
export const FRAMEWORK_VERSION = '4.
|
|
79
|
+
export const FRAMEWORK_VERSION = '4.5.1';
|
|
78
80
|
export const FRAMEWORK_NAME = 'Clodo Framework';
|
|
79
81
|
|
|
80
82
|
// โโโ Compatibility Constants (for consistent wrangler.toml generation) โโ
|