myaidev-method 0.2.23 → 0.2.24
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/bin/cli.js +55 -2
- package/dist/server/.tsbuildinfo +1 -1
- package/package.json +6 -1
- package/src/config/workflows.js +28 -44
- package/src/lib/config-manager.js +470 -0
- package/src/lib/content-generator.js +427 -0
- package/src/lib/html-conversion-utils.js +843 -0
- package/src/lib/seo-optimizer.js +515 -0
- package/src/lib/wordpress-client.js +633 -0
- package/src/lib/workflow-installer.js +3 -3
- package/src/scripts/html-conversion-cli.js +526 -0
- package/src/scripts/init/configure.js +436 -0
- package/src/scripts/init/install.js +460 -0
- package/src/scripts/utils/file-utils.js +404 -0
- package/src/scripts/utils/logger.js +300 -0
- package/src/scripts/utils/write-content.js +293 -0
- package/src/templates/claude/agents/visual-content-generator.md +129 -4
- package/src/templates/claude/commands/myai-convert-html.md +186 -0
- package/src/templates/diagrams/architecture.d2 +52 -0
- package/src/templates/diagrams/flowchart.d2 +42 -0
- package/src/templates/diagrams/sequence.d2 +47 -0
- package/src/templates/docs/content-creation-guide.md +164 -0
- package/src/templates/docs/deployment-guide.md +336 -0
- package/src/templates/docs/visual-generation-guide.md +248 -0
- package/src/templates/docs/wordpress-publishing-guide.md +208 -0
- package/src/templates/infographics/comparison-table.html +347 -0
- package/src/templates/infographics/data-chart.html +268 -0
- package/src/templates/infographics/process-flow.html +365 -0
- /package/src/scripts/{wordpress-health-check.js → wordpress/wordpress-health-check.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "myaidev-method",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.24",
|
|
4
4
|
"description": "Comprehensive development framework with SPARC methodology for AI-assisted software development, security testing (PTES, OWASP, penetration testing, compliance auditing), AI visual content generation (Gemini, OpenAI GPT Image 1.5, Imagen, FLUX 2, Veo 3), OpenStack VM management, multi-platform publishing (WordPress, PayloadCMS, Astro, Docusaurus, Mintlify), and Coolify deployment",
|
|
5
5
|
"mcpName": "io.github.myaione/myaidev-method",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"test": "node test/run-tests.js",
|
|
15
15
|
"test:gutenberg": "node test/test-gutenberg-converter.js",
|
|
16
16
|
"test:install": "node test/test-installation.js",
|
|
17
|
+
"test:workflows": "node test/test-workflow-installer.js",
|
|
17
18
|
"test:mcp": "node test/test-enhanced-mcp-server.js",
|
|
18
19
|
"test:mcp:simple": "node test/test-simple-mcp.js",
|
|
19
20
|
"mcp:start": "node src/mcp/mcp-launcher.js start",
|
|
@@ -50,6 +51,7 @@
|
|
|
50
51
|
"configure": "node src/scripts/configure-cms.js",
|
|
51
52
|
"configure:visual": "node src/scripts/configure-visual-apis.js",
|
|
52
53
|
"generate:visual": "node src/scripts/generate-visual-cli.js",
|
|
54
|
+
"convert:html": "node src/scripts/html-conversion-cli.js",
|
|
53
55
|
"init": "node src/scripts/init-project.js"
|
|
54
56
|
},
|
|
55
57
|
"keywords": [
|
|
@@ -119,6 +121,8 @@
|
|
|
119
121
|
"marked": "^17.0.1",
|
|
120
122
|
"node-fetch": "^3.3.2",
|
|
121
123
|
"ora": "^9.0.0",
|
|
124
|
+
"pptxgenjs": "^4.0.1",
|
|
125
|
+
"puppeteer": "^24.35.0",
|
|
122
126
|
"simple-git": "^3.22.0",
|
|
123
127
|
"ssh2": "^1.15.0",
|
|
124
128
|
"zod": "^4.3.5"
|
|
@@ -141,6 +145,7 @@
|
|
|
141
145
|
"files": [
|
|
142
146
|
"bin/",
|
|
143
147
|
"src/",
|
|
148
|
+
"docs/",
|
|
144
149
|
"dist/",
|
|
145
150
|
"README.md",
|
|
146
151
|
"USER_GUIDE.md",
|
package/src/config/workflows.js
CHANGED
|
@@ -6,30 +6,41 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const WORKFLOWS = {
|
|
9
|
-
// Content Creation Workflow
|
|
9
|
+
// Content Creation Workflow (includes WordPress publishing)
|
|
10
10
|
content: {
|
|
11
11
|
name: 'Content Creation',
|
|
12
12
|
description: 'Professional content writing with SEO optimization and WordPress publishing',
|
|
13
13
|
agents: [
|
|
14
14
|
'content-writer',
|
|
15
|
-
'proprietary-content-verifier'
|
|
15
|
+
'proprietary-content-verifier',
|
|
16
|
+
'wordpress-admin'
|
|
16
17
|
],
|
|
17
18
|
commands: [
|
|
18
|
-
'myai-content-writer'
|
|
19
|
+
'myai-content-writer',
|
|
20
|
+
'myai-wordpress-publish',
|
|
21
|
+
'myai-coordinate-content'
|
|
19
22
|
],
|
|
20
23
|
scripts: [
|
|
21
|
-
'utils/write-content.js'
|
|
24
|
+
'utils/write-content.js',
|
|
25
|
+
'wordpress/publish-to-wordpress.js',
|
|
26
|
+
'wordpress/wordpress-health-check.js'
|
|
22
27
|
],
|
|
23
28
|
libs: [
|
|
24
29
|
'content-generator.js',
|
|
25
|
-
'seo-optimizer.js'
|
|
30
|
+
'seo-optimizer.js',
|
|
31
|
+
'wordpress-client.js'
|
|
26
32
|
],
|
|
27
33
|
docs: [
|
|
28
|
-
'content-creation-guide.md'
|
|
34
|
+
'content-creation-guide.md',
|
|
35
|
+
'wordpress-publishing-guide.md'
|
|
29
36
|
],
|
|
30
|
-
mcpServers: [],
|
|
37
|
+
mcpServers: ['myaidev'],
|
|
31
38
|
dependencies: ['core'],
|
|
32
|
-
envVars: [
|
|
39
|
+
envVars: [
|
|
40
|
+
'WORDPRESS_URL',
|
|
41
|
+
'WORDPRESS_USERNAME',
|
|
42
|
+
'WORDPRESS_APP_PASSWORD'
|
|
43
|
+
]
|
|
33
44
|
},
|
|
34
45
|
|
|
35
46
|
// Visual Content Generation Workflow
|
|
@@ -92,9 +103,7 @@ const WORKFLOWS = {
|
|
|
92
103
|
],
|
|
93
104
|
scripts: [],
|
|
94
105
|
libs: [],
|
|
95
|
-
docs: [
|
|
96
|
-
'sparc-methodology.md'
|
|
97
|
-
],
|
|
106
|
+
docs: [],
|
|
98
107
|
mcpServers: [],
|
|
99
108
|
dependencies: ['core'],
|
|
100
109
|
envVars: []
|
|
@@ -113,9 +122,7 @@ const WORKFLOWS = {
|
|
|
113
122
|
'ci/lint.js'
|
|
114
123
|
],
|
|
115
124
|
libs: [],
|
|
116
|
-
docs: [
|
|
117
|
-
'git-workflow-guide.md'
|
|
118
|
-
],
|
|
125
|
+
docs: [],
|
|
119
126
|
mcpServers: [],
|
|
120
127
|
dependencies: ['core'],
|
|
121
128
|
envVars: []
|
|
@@ -192,9 +199,7 @@ const WORKFLOWS = {
|
|
|
192
199
|
libs: [
|
|
193
200
|
'payloadcms-utils.js'
|
|
194
201
|
],
|
|
195
|
-
docs: [
|
|
196
|
-
'payloadcms-publishing-guide.md'
|
|
197
|
-
],
|
|
202
|
+
docs: [],
|
|
198
203
|
mcpServers: [],
|
|
199
204
|
dependencies: ['core', 'content'],
|
|
200
205
|
envVars: [
|
|
@@ -219,9 +224,7 @@ const WORKFLOWS = {
|
|
|
219
224
|
libs: [
|
|
220
225
|
'static-generator.js'
|
|
221
226
|
],
|
|
222
|
-
docs: [
|
|
223
|
-
'static-site-guide.md'
|
|
224
|
-
],
|
|
227
|
+
docs: [],
|
|
225
228
|
mcpServers: [],
|
|
226
229
|
dependencies: ['core', 'content'],
|
|
227
230
|
envVars: []
|
|
@@ -242,9 +245,7 @@ const WORKFLOWS = {
|
|
|
242
245
|
libs: [
|
|
243
246
|
'coolify-client.js'
|
|
244
247
|
],
|
|
245
|
-
docs: [
|
|
246
|
-
'coolify-deployment-guide.md'
|
|
247
|
-
],
|
|
248
|
+
docs: [],
|
|
248
249
|
mcpServers: [],
|
|
249
250
|
dependencies: ['core', 'deployment'],
|
|
250
251
|
envVars: [
|
|
@@ -273,10 +274,7 @@ const WORKFLOWS = {
|
|
|
273
274
|
libs: [
|
|
274
275
|
'config-manager.js'
|
|
275
276
|
],
|
|
276
|
-
docs: [
|
|
277
|
-
'README.md',
|
|
278
|
-
'TECHNICAL_ARCHITECTURE.md'
|
|
279
|
-
],
|
|
277
|
+
docs: [],
|
|
280
278
|
mcpServers: [],
|
|
281
279
|
dependencies: [],
|
|
282
280
|
envVars: []
|
|
@@ -320,12 +318,7 @@ const WORKFLOWS = {
|
|
|
320
318
|
'security/cvss-calculator.js',
|
|
321
319
|
'security/report-generator.js'
|
|
322
320
|
],
|
|
323
|
-
docs: [
|
|
324
|
-
'SECURITY_TESTING_GUIDE.md',
|
|
325
|
-
'LEGAL_REQUIREMENTS.md',
|
|
326
|
-
'TOOL_INSTALLATION.md',
|
|
327
|
-
'PENTEST_METHODOLOGY.md'
|
|
328
|
-
],
|
|
321
|
+
docs: [],
|
|
329
322
|
mcpServers: [],
|
|
330
323
|
dependencies: ['core', 'development'],
|
|
331
324
|
envVars: [
|
|
@@ -371,12 +364,7 @@ const WORKFLOWS = {
|
|
|
371
364
|
'security/cvss-calculator.js',
|
|
372
365
|
'security/report-generator.js'
|
|
373
366
|
],
|
|
374
|
-
docs: [
|
|
375
|
-
'SECURITY_AUDIT_GUIDE.md',
|
|
376
|
-
'COMPLIANCE_FRAMEWORKS.md',
|
|
377
|
-
'SYSTEM_HARDENING.md',
|
|
378
|
-
'LEGAL_REQUIREMENTS.md'
|
|
379
|
-
],
|
|
367
|
+
docs: [],
|
|
380
368
|
mcpServers: [],
|
|
381
369
|
dependencies: ['core'],
|
|
382
370
|
envVars: [
|
|
@@ -416,11 +404,7 @@ const WORKFLOWS = {
|
|
|
416
404
|
'security/web-scanner.js',
|
|
417
405
|
'security/report-generator.js'
|
|
418
406
|
],
|
|
419
|
-
docs: [
|
|
420
|
-
'WEBAPP_SECURITY_GUIDE.md',
|
|
421
|
-
'OWASP_TOP10.md',
|
|
422
|
-
'LEGAL_REQUIREMENTS.md'
|
|
423
|
-
],
|
|
407
|
+
docs: [],
|
|
424
408
|
mcpServers: [],
|
|
425
409
|
dependencies: ['core', 'development', 'security-pentest'],
|
|
426
410
|
envVars: [
|
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Manager
|
|
3
|
+
* Central configuration management for MyAIDev Method
|
|
4
|
+
* Handles loading, validating, and persisting configurations
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import fs from 'fs-extra';
|
|
9
|
+
|
|
10
|
+
// Configuration schemas for validation
|
|
11
|
+
export const SCHEMAS = {
|
|
12
|
+
wordpress: {
|
|
13
|
+
required: ['WORDPRESS_URL', 'WORDPRESS_USERNAME', 'WORDPRESS_APP_PASSWORD'],
|
|
14
|
+
optional: ['WORDPRESS_DEFAULT_STATUS', 'WORDPRESS_DEFAULT_CATEGORIES'],
|
|
15
|
+
defaults: {
|
|
16
|
+
WORDPRESS_DEFAULT_STATUS: 'draft'
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
openstack: {
|
|
20
|
+
required: ['OS_AUTH_URL', 'OS_USERNAME', 'OS_PASSWORD', 'OS_PROJECT_NAME'],
|
|
21
|
+
optional: ['OS_REGION_NAME', 'OS_USER_DOMAIN_NAME', 'OS_PROJECT_DOMAIN_NAME'],
|
|
22
|
+
defaults: {
|
|
23
|
+
OS_USER_DOMAIN_NAME: 'Default',
|
|
24
|
+
OS_PROJECT_DOMAIN_NAME: 'Default'
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
content: {
|
|
28
|
+
required: [],
|
|
29
|
+
optional: ['DEFAULT_WORD_COUNT', 'DEFAULT_TONE', 'CONTENT_OUTPUT_DIR'],
|
|
30
|
+
defaults: {
|
|
31
|
+
DEFAULT_WORD_COUNT: '1500',
|
|
32
|
+
DEFAULT_TONE: 'professional',
|
|
33
|
+
CONTENT_OUTPUT_DIR: './content'
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
defaults: {
|
|
37
|
+
required: [],
|
|
38
|
+
optional: ['LOG_LEVEL', 'CLI_TYPE'],
|
|
39
|
+
defaults: {
|
|
40
|
+
LOG_LEVEL: 'INFO',
|
|
41
|
+
CLI_TYPE: 'claude'
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Configuration Manager Class
|
|
48
|
+
*/
|
|
49
|
+
export class ConfigManager {
|
|
50
|
+
constructor(projectRoot = process.cwd()) {
|
|
51
|
+
this.projectRoot = projectRoot;
|
|
52
|
+
this.configCache = null;
|
|
53
|
+
this.lastLoad = null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get path to .env file
|
|
58
|
+
* @returns {string}
|
|
59
|
+
*/
|
|
60
|
+
getEnvPath() {
|
|
61
|
+
return path.join(this.projectRoot, '.env');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get path to .claude/config directory
|
|
66
|
+
* @returns {string}
|
|
67
|
+
*/
|
|
68
|
+
getClaudeConfigDir() {
|
|
69
|
+
return path.join(this.projectRoot, '.claude', 'config');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Load all configurations from various sources
|
|
74
|
+
* @param {boolean} [force=false] - Force reload even if cached
|
|
75
|
+
* @returns {Promise<Object>} Merged configuration
|
|
76
|
+
*/
|
|
77
|
+
async loadAll(force = false) {
|
|
78
|
+
// Return cached config if recent (within 5 seconds)
|
|
79
|
+
if (!force && this.configCache && this.lastLoad) {
|
|
80
|
+
const age = Date.now() - this.lastLoad;
|
|
81
|
+
if (age < 5000) {
|
|
82
|
+
return this.configCache;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const config = {};
|
|
87
|
+
|
|
88
|
+
// 1. Load from .env file
|
|
89
|
+
const envConfig = await this.loadEnvFile();
|
|
90
|
+
Object.assign(config, envConfig);
|
|
91
|
+
|
|
92
|
+
// 2. Load from .claude/config/*.json files
|
|
93
|
+
const claudeConfigs = await this.loadClaudeConfigs();
|
|
94
|
+
Object.assign(config, claudeConfigs);
|
|
95
|
+
|
|
96
|
+
// 3. Apply defaults for any missing values
|
|
97
|
+
this.applyDefaults(config);
|
|
98
|
+
|
|
99
|
+
// Cache the result
|
|
100
|
+
this.configCache = config;
|
|
101
|
+
this.lastLoad = Date.now();
|
|
102
|
+
|
|
103
|
+
return config;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Load configuration from .env file
|
|
108
|
+
* @returns {Promise<Object>}
|
|
109
|
+
*/
|
|
110
|
+
async loadEnvFile() {
|
|
111
|
+
const envPath = this.getEnvPath();
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const exists = await fs.pathExists(envPath);
|
|
115
|
+
if (!exists) {
|
|
116
|
+
return {};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const content = await fs.readFile(envPath, 'utf-8');
|
|
120
|
+
return this.parseEnvContent(content);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.warn(`Warning: Failed to load .env file: ${error.message}`);
|
|
123
|
+
return {};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Load configuration files from .claude/config/
|
|
129
|
+
* @returns {Promise<Object>}
|
|
130
|
+
*/
|
|
131
|
+
async loadClaudeConfigs() {
|
|
132
|
+
const configDir = this.getClaudeConfigDir();
|
|
133
|
+
const merged = {};
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const exists = await fs.pathExists(configDir);
|
|
137
|
+
if (!exists) {
|
|
138
|
+
return merged;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const files = await fs.readdir(configDir);
|
|
142
|
+
const jsonFiles = files.filter(f => f.endsWith('.json'));
|
|
143
|
+
|
|
144
|
+
for (const file of jsonFiles) {
|
|
145
|
+
const filePath = path.join(configDir, file);
|
|
146
|
+
try {
|
|
147
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
148
|
+
const data = JSON.parse(content);
|
|
149
|
+
Object.assign(merged, data);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
console.warn(`Warning: Failed to load config file ${file}: ${err.message}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return merged;
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.warn(`Warning: Failed to load claude configs: ${error.message}`);
|
|
158
|
+
return merged;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Apply default values for missing configuration
|
|
164
|
+
* @param {Object} config - Configuration object to modify
|
|
165
|
+
*/
|
|
166
|
+
applyDefaults(config) {
|
|
167
|
+
for (const schema of Object.values(SCHEMAS)) {
|
|
168
|
+
for (const [key, value] of Object.entries(schema.defaults || {})) {
|
|
169
|
+
if (config[key] === undefined) {
|
|
170
|
+
config[key] = value;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get a specific configuration value
|
|
178
|
+
* @param {string} key - Configuration key
|
|
179
|
+
* @param {any} [defaultValue] - Default value if not found
|
|
180
|
+
* @returns {Promise<any>}
|
|
181
|
+
*/
|
|
182
|
+
async getConfig(key, defaultValue = undefined) {
|
|
183
|
+
const config = await this.loadAll();
|
|
184
|
+
return config[key] !== undefined ? config[key] : defaultValue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Save workflow-specific configuration
|
|
189
|
+
* @param {string} name - Workflow/config name (e.g., 'wordpress', 'openstack')
|
|
190
|
+
* @param {Object} config - Configuration values
|
|
191
|
+
* @returns {Promise<boolean>}
|
|
192
|
+
*/
|
|
193
|
+
async saveWorkflowConfig(name, config) {
|
|
194
|
+
const configDir = this.getClaudeConfigDir();
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
await fs.ensureDir(configDir);
|
|
198
|
+
|
|
199
|
+
const configPath = path.join(configDir, `${name}.json`);
|
|
200
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
201
|
+
|
|
202
|
+
// Invalidate cache
|
|
203
|
+
this.configCache = null;
|
|
204
|
+
|
|
205
|
+
return true;
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error(`Failed to save ${name} config: ${error.message}`);
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Save configuration to .env file
|
|
214
|
+
* @param {Object} config - Configuration to save
|
|
215
|
+
* @param {boolean} [merge=true] - Merge with existing .env
|
|
216
|
+
* @returns {Promise<boolean>}
|
|
217
|
+
*/
|
|
218
|
+
async saveEnvConfig(config, merge = true) {
|
|
219
|
+
const envPath = this.getEnvPath();
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
let existing = {};
|
|
223
|
+
if (merge) {
|
|
224
|
+
existing = await this.loadEnvFile();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const merged = { ...existing, ...config };
|
|
228
|
+
const content = this.formatEnvContent(merged);
|
|
229
|
+
|
|
230
|
+
await fs.writeFile(envPath, content, 'utf-8');
|
|
231
|
+
|
|
232
|
+
// Invalidate cache
|
|
233
|
+
this.configCache = null;
|
|
234
|
+
|
|
235
|
+
return true;
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error(`Failed to save .env config: ${error.message}`);
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Validate configuration against a schema
|
|
244
|
+
* @param {Object} config - Configuration to validate
|
|
245
|
+
* @param {string} schemaName - Schema name ('wordpress', 'openstack', etc.)
|
|
246
|
+
* @returns {{valid: boolean, missing: string[], warnings: string[]}}
|
|
247
|
+
*/
|
|
248
|
+
validateConfig(config, schemaName) {
|
|
249
|
+
const schema = SCHEMAS[schemaName];
|
|
250
|
+
if (!schema) {
|
|
251
|
+
return { valid: false, missing: [], warnings: [`Unknown schema: ${schemaName}`] };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const missing = [];
|
|
255
|
+
const warnings = [];
|
|
256
|
+
|
|
257
|
+
// Check required fields
|
|
258
|
+
for (const field of schema.required || []) {
|
|
259
|
+
if (!config[field] || config[field].trim() === '') {
|
|
260
|
+
missing.push(field);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Check for empty optional fields
|
|
265
|
+
for (const field of schema.optional || []) {
|
|
266
|
+
if (config[field] === '') {
|
|
267
|
+
warnings.push(`${field} is empty, using default`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
valid: missing.length === 0,
|
|
273
|
+
missing,
|
|
274
|
+
warnings
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Merge multiple configurations
|
|
280
|
+
* @param {Object} base - Base configuration
|
|
281
|
+
* @param {...Object} overrides - Override configurations
|
|
282
|
+
* @returns {Object} Merged configuration
|
|
283
|
+
*/
|
|
284
|
+
mergeConfigs(base, ...overrides) {
|
|
285
|
+
const result = { ...base };
|
|
286
|
+
|
|
287
|
+
for (const override of overrides) {
|
|
288
|
+
for (const [key, value] of Object.entries(override)) {
|
|
289
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
290
|
+
result[key] = value;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return result;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Check if a specific configuration is complete
|
|
300
|
+
* @param {string} schemaName - Schema name to check
|
|
301
|
+
* @returns {Promise<{complete: boolean, missing: string[]}>}
|
|
302
|
+
*/
|
|
303
|
+
async isConfigured(schemaName) {
|
|
304
|
+
const config = await this.loadAll();
|
|
305
|
+
const validation = this.validateConfig(config, schemaName);
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
complete: validation.valid,
|
|
309
|
+
missing: validation.missing
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get configuration for a specific workflow
|
|
315
|
+
* @param {string} workflowName - Workflow name
|
|
316
|
+
* @returns {Promise<Object>}
|
|
317
|
+
*/
|
|
318
|
+
async getWorkflowConfig(workflowName) {
|
|
319
|
+
const config = await this.loadAll();
|
|
320
|
+
const prefix = workflowName.toUpperCase() + '_';
|
|
321
|
+
|
|
322
|
+
const workflowConfig = {};
|
|
323
|
+
for (const [key, value] of Object.entries(config)) {
|
|
324
|
+
if (key.startsWith(prefix) || key.startsWith(workflowName.toUpperCase())) {
|
|
325
|
+
workflowConfig[key] = value;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return workflowConfig;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Parse .env content into object
|
|
334
|
+
* @param {string} content - .env file content
|
|
335
|
+
* @returns {Object}
|
|
336
|
+
*/
|
|
337
|
+
parseEnvContent(content) {
|
|
338
|
+
const result = {};
|
|
339
|
+
const lines = content.split('\n');
|
|
340
|
+
|
|
341
|
+
for (const line of lines) {
|
|
342
|
+
const trimmed = line.trim();
|
|
343
|
+
|
|
344
|
+
// Skip empty lines and comments
|
|
345
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const match = trimmed.match(/^([^=]+)=(.*)$/);
|
|
350
|
+
if (match) {
|
|
351
|
+
const key = match[1].trim();
|
|
352
|
+
let value = match[2].trim();
|
|
353
|
+
|
|
354
|
+
// Remove quotes if present
|
|
355
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
356
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
357
|
+
value = value.slice(1, -1);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
result[key] = value;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Format object as .env content
|
|
369
|
+
* @param {Object} config - Configuration object
|
|
370
|
+
* @returns {string}
|
|
371
|
+
*/
|
|
372
|
+
formatEnvContent(config) {
|
|
373
|
+
const lines = [];
|
|
374
|
+
|
|
375
|
+
// Group by prefix for better organization
|
|
376
|
+
const groups = {};
|
|
377
|
+
const noPrefix = [];
|
|
378
|
+
|
|
379
|
+
for (const [key, value] of Object.entries(config)) {
|
|
380
|
+
if (value === undefined || value === null) continue;
|
|
381
|
+
|
|
382
|
+
const prefixMatch = key.match(/^([A-Z]+)_/);
|
|
383
|
+
if (prefixMatch) {
|
|
384
|
+
const prefix = prefixMatch[1];
|
|
385
|
+
if (!groups[prefix]) groups[prefix] = [];
|
|
386
|
+
groups[prefix].push([key, value]);
|
|
387
|
+
} else {
|
|
388
|
+
noPrefix.push([key, value]);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Format groups
|
|
393
|
+
for (const [prefix, entries] of Object.entries(groups)) {
|
|
394
|
+
lines.push(`# ${prefix} Configuration`);
|
|
395
|
+
for (const [key, value] of entries) {
|
|
396
|
+
lines.push(this.formatEnvLine(key, value));
|
|
397
|
+
}
|
|
398
|
+
lines.push('');
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Format ungrouped
|
|
402
|
+
if (noPrefix.length > 0) {
|
|
403
|
+
lines.push('# General Configuration');
|
|
404
|
+
for (const [key, value] of noPrefix) {
|
|
405
|
+
lines.push(this.formatEnvLine(key, value));
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return lines.join('\n');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Format a single .env line
|
|
414
|
+
* @param {string} key - Key
|
|
415
|
+
* @param {any} value - Value
|
|
416
|
+
* @returns {string}
|
|
417
|
+
*/
|
|
418
|
+
formatEnvLine(key, value) {
|
|
419
|
+
const stringValue = String(value);
|
|
420
|
+
const needsQuotes = stringValue.includes(' ') ||
|
|
421
|
+
stringValue.includes('#') ||
|
|
422
|
+
stringValue.includes('=');
|
|
423
|
+
|
|
424
|
+
return needsQuotes ? `${key}="${stringValue}"` : `${key}=${stringValue}`;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Delete a configuration key
|
|
429
|
+
* @param {string} key - Key to delete
|
|
430
|
+
* @returns {Promise<boolean>}
|
|
431
|
+
*/
|
|
432
|
+
async deleteConfig(key) {
|
|
433
|
+
const config = await this.loadEnvFile();
|
|
434
|
+
|
|
435
|
+
if (config[key] !== undefined) {
|
|
436
|
+
delete config[key];
|
|
437
|
+
await this.saveEnvConfig(config, false);
|
|
438
|
+
this.configCache = null;
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* List all configuration keys
|
|
447
|
+
* @returns {Promise<string[]>}
|
|
448
|
+
*/
|
|
449
|
+
async listConfigKeys() {
|
|
450
|
+
const config = await this.loadAll();
|
|
451
|
+
return Object.keys(config).sort();
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Singleton instance for convenience
|
|
456
|
+
let defaultInstance = null;
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Get the default ConfigManager instance
|
|
460
|
+
* @param {string} [projectRoot] - Project root directory
|
|
461
|
+
* @returns {ConfigManager}
|
|
462
|
+
*/
|
|
463
|
+
export function getConfigManager(projectRoot) {
|
|
464
|
+
if (!defaultInstance || (projectRoot && projectRoot !== defaultInstance.projectRoot)) {
|
|
465
|
+
defaultInstance = new ConfigManager(projectRoot);
|
|
466
|
+
}
|
|
467
|
+
return defaultInstance;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export default ConfigManager;
|