myaidev-method 0.2.22 → 0.2.23

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.
Files changed (33) hide show
  1. package/USER_GUIDE.md +453 -48
  2. package/bin/cli.js +18 -0
  3. package/content-rules.example.md +80 -0
  4. package/dist/mcp/mcp-launcher.js +237 -0
  5. package/dist/server/.tsbuildinfo +1 -1
  6. package/dist/server/auth/layers.d.ts +1 -1
  7. package/dist/server/auth/services/AuthService.d.ts +1 -1
  8. package/dist/server/auth/services/TokenService.js.map +1 -1
  9. package/dist/server/auth/services/example.d.ts +5 -5
  10. package/package.json +16 -16
  11. package/src/index.js +21 -8
  12. package/src/lib/update-manager.js +2 -1
  13. package/src/lib/visual-config-utils.js +321 -295
  14. package/src/lib/visual-generation-utils.js +1000 -811
  15. package/src/scripts/configure-wordpress-mcp.js +8 -3
  16. package/src/scripts/generate-visual-cli.js +365 -235
  17. package/src/scripts/ping.js +250 -0
  18. package/src/scripts/wordpress/publish-to-wordpress.js +165 -0
  19. package/src/server/auth/services/TokenService.ts +1 -1
  20. package/src/templates/claude/agents/content-rules-setup.md +657 -0
  21. package/src/templates/claude/agents/content-writer.md +328 -1
  22. package/src/templates/claude/agents/visual-content-generator.md +182 -4
  23. package/src/templates/claude/commands/myai-configure.md +1 -1
  24. package/src/templates/claude/commands/myai-content-rules-setup.md +204 -0
  25. package/src/templates/codex/commands/myai-content-rules-setup.md +85 -0
  26. package/src/templates/gemini/commands/myai-content-rules-setup.toml +57 -0
  27. package/.claude/mcp/sparc-orchestrator-server.js +0 -607
  28. package/.claude/mcp/wordpress-server.js +0 -1277
  29. package/src/agents/content-writer-prompt.md +0 -164
  30. package/src/agents/content-writer.json +0 -70
  31. package/src/templates/claude/mcp_config.json +0 -74
  32. package/src/templates/claude/slash_commands.json +0 -166
  33. package/src/templates/scripts/configure-wordpress-mcp.js +0 -181
@@ -8,9 +8,9 @@
8
8
  * @module visual-config-utils
9
9
  */
10
10
 
11
- import fs from 'fs-extra';
12
- import path from 'path';
13
- import dotenv from 'dotenv';
11
+ import fs from "fs-extra";
12
+ import path from "path";
13
+ import dotenv from "dotenv";
14
14
 
15
15
  dotenv.config();
16
16
 
@@ -20,16 +20,17 @@ dotenv.config();
20
20
  * @returns {Object} Configuration object
21
21
  */
22
22
  export function loadVisualConfig() {
23
- return {
24
- googleApiKey: process.env.GOOGLE_API_KEY || null,
25
- openaiApiKey: process.env.OPENAI_API_KEY || null,
26
- defaultService: process.env.VISUAL_DEFAULT_SERVICE || 'gemini',
27
- defaultQuality: process.env.VISUAL_DEFAULT_QUALITY || 'standard',
28
- assetsPath: process.env.VISUAL_ASSETS_PATH || './content-assets',
29
- dailyBudget: parseFloat(process.env.VISUAL_DAILY_BUDGET || 5.0),
30
- monthlyBudget: parseFloat(process.env.VISUAL_MONTHLY_BUDGET || 50.0),
31
- warnThreshold: parseFloat(process.env.VISUAL_WARN_THRESHOLD || 0.8)
32
- };
23
+ return {
24
+ googleApiKey: process.env.GOOGLE_API_KEY || null,
25
+ openaiApiKey: process.env.OPENAI_API_KEY || null,
26
+ falApiKey: process.env.FAL_KEY || null,
27
+ defaultService: process.env.VISUAL_DEFAULT_SERVICE || "gemini",
28
+ defaultQuality: process.env.VISUAL_DEFAULT_QUALITY || "standard",
29
+ assetsPath: process.env.VISUAL_ASSETS_PATH || "./content-assets",
30
+ dailyBudget: parseFloat(process.env.VISUAL_DAILY_BUDGET || 5.0),
31
+ monthlyBudget: parseFloat(process.env.VISUAL_MONTHLY_BUDGET || 50.0),
32
+ warnThreshold: parseFloat(process.env.VISUAL_WARN_THRESHOLD || 0.8),
33
+ };
33
34
  }
34
35
 
35
36
  /**
@@ -38,57 +39,71 @@ export function loadVisualConfig() {
38
39
  * @returns {Object} Validation results
39
40
  */
40
41
  export function validateVisualConfig() {
41
- const config = loadVisualConfig();
42
-
43
- const validation = {
44
- isValid: false,
45
- hasGoogle: false,
46
- hasOpenAI: false,
47
- hasAnyAPIKeys: false,
48
- availableServices: [],
49
- errors: [],
50
- warnings: []
51
- };
52
-
53
- // Validate API keys
54
- if (config.googleApiKey && config.googleApiKey.length > 20) {
55
- validation.hasGoogle = true;
56
- validation.availableServices.push('gemini', 'imagen', 'veo');
57
- } else if (config.googleApiKey) {
58
- validation.errors.push('GOOGLE_API_KEY appears to be invalid (too short)');
59
- }
60
-
61
- if (config.openaiApiKey && config.openaiApiKey.length > 20) {
62
- validation.hasOpenAI = true;
63
- validation.availableServices.push('dalle');
64
- } else if (config.openaiApiKey) {
65
- validation.errors.push('OPENAI_API_KEY appears to be invalid (too short)');
66
- }
67
-
68
- validation.hasAnyAPIKeys = validation.hasGoogle || validation.hasOpenAI;
69
-
70
- // Validate default service
71
- if (config.defaultService && !validation.availableServices.includes(config.defaultService)) {
72
- validation.warnings.push(`Default service '${config.defaultService}' is not available with current API keys`);
73
- }
74
-
75
- // Validate budget values
76
- if (config.dailyBudget <= 0) {
77
- validation.warnings.push('Daily budget should be greater than 0');
78
- }
79
-
80
- if (config.monthlyBudget <= 0) {
81
- validation.warnings.push('Monthly budget should be greater than 0');
82
- }
83
-
84
- if (config.monthlyBudget < config.dailyBudget) {
85
- validation.warnings.push('Monthly budget is less than daily budget');
86
- }
87
-
88
- // Configuration is valid if at least one API key is configured
89
- validation.isValid = validation.hasAnyAPIKeys && validation.errors.length === 0;
90
-
91
- return validation;
42
+ const config = loadVisualConfig();
43
+
44
+ const validation = {
45
+ isValid: false,
46
+ hasGoogle: false,
47
+ hasOpenAI: false,
48
+ hasFal: false,
49
+ hasAnyAPIKeys: false,
50
+ availableServices: [],
51
+ errors: [],
52
+ warnings: [],
53
+ };
54
+
55
+ // Validate API keys
56
+ if (config.googleApiKey && config.googleApiKey.length > 20) {
57
+ validation.hasGoogle = true;
58
+ validation.availableServices.push("gemini", "imagen", "veo");
59
+ } else if (config.googleApiKey) {
60
+ validation.errors.push("GOOGLE_API_KEY appears to be invalid (too short)");
61
+ }
62
+
63
+ if (config.openaiApiKey && config.openaiApiKey.length > 20) {
64
+ validation.hasOpenAI = true;
65
+ validation.availableServices.push("dalle");
66
+ } else if (config.openaiApiKey) {
67
+ validation.errors.push("OPENAI_API_KEY appears to be invalid (too short)");
68
+ }
69
+
70
+ if (config.falApiKey && config.falApiKey.length > 20) {
71
+ validation.hasFal = true;
72
+ validation.availableServices.push("fal");
73
+ } else if (config.falApiKey) {
74
+ validation.errors.push("FAL_KEY appears to be invalid (too short)");
75
+ }
76
+
77
+ validation.hasAnyAPIKeys = validation.hasGoogle || validation.hasOpenAI;
78
+
79
+ // Validate default service
80
+ if (
81
+ config.defaultService &&
82
+ !validation.availableServices.includes(config.defaultService)
83
+ ) {
84
+ validation.warnings.push(
85
+ `Default service '${config.defaultService}' is not available with current API keys`,
86
+ );
87
+ }
88
+
89
+ // Validate budget values
90
+ if (config.dailyBudget <= 0) {
91
+ validation.warnings.push("Daily budget should be greater than 0");
92
+ }
93
+
94
+ if (config.monthlyBudget <= 0) {
95
+ validation.warnings.push("Monthly budget should be greater than 0");
96
+ }
97
+
98
+ if (config.monthlyBudget < config.dailyBudget) {
99
+ validation.warnings.push("Monthly budget is less than daily budget");
100
+ }
101
+
102
+ // Configuration is valid if at least one API key is configured
103
+ validation.isValid =
104
+ validation.hasAnyAPIKeys && validation.errors.length === 0;
105
+
106
+ return validation;
92
107
  }
93
108
 
94
109
  /**
@@ -97,8 +112,8 @@ export function validateVisualConfig() {
97
112
  * @returns {boolean} True if at least one API key is configured
98
113
  */
99
114
  export function hasAnyAPIKeys() {
100
- const validation = validateVisualConfig();
101
- return validation.hasAnyAPIKeys;
115
+ const validation = validateVisualConfig();
116
+ return validation.hasAnyAPIKeys;
102
117
  }
103
118
 
104
119
  /**
@@ -107,8 +122,8 @@ export function hasAnyAPIKeys() {
107
122
  * @returns {Array<string>} List of available service names
108
123
  */
109
124
  export function getAvailableServices() {
110
- const validation = validateVisualConfig();
111
- return validation.availableServices;
125
+ const validation = validateVisualConfig();
126
+ return validation.availableServices;
112
127
  }
113
128
 
114
129
  /**
@@ -118,54 +133,65 @@ export function getAvailableServices() {
118
133
  * @returns {Object} Service information
119
134
  */
120
135
  export function getServiceDisplayInfo(service) {
121
- const services = {
122
- gemini: {
123
- id: 'gemini',
124
- name: 'Gemini 2.5 Flash Image',
125
- nickname: '"Nano Banana"',
126
- provider: 'Google',
127
- apiKey: 'GOOGLE_API_KEY',
128
- speed: 'Fast ⚡⚡⚡',
129
- cost: '$0.02/image',
130
- quality: 'Good',
131
- bestFor: 'Quick hero images, high volume'
132
- },
133
- imagen: {
134
- id: 'imagen',
135
- name: 'Imagen 3',
136
- nickname: 'Premium Quality',
137
- provider: 'Google',
138
- apiKey: 'GOOGLE_API_KEY',
139
- speed: 'Medium ⚡⚡',
140
- cost: '$0.03/image',
141
- quality: 'Excellent',
142
- bestFor: 'Premium hero images, photorealistic content'
143
- },
144
- dalle: {
145
- id: 'dalle',
146
- name: 'DALL-E 3',
147
- nickname: 'Creative AI',
148
- provider: 'OpenAI',
149
- apiKey: 'OPENAI_API_KEY',
150
- speed: 'Medium ⚡⚡',
151
- cost: '$0.04-0.12/image',
152
- quality: 'Excellent',
153
- bestFor: 'Creative illustrations, concept art'
154
- },
155
- veo: {
156
- id: 'veo',
157
- name: 'Veo 2',
158
- nickname: 'Video Generation',
159
- provider: 'Google',
160
- apiKey: 'GOOGLE_API_KEY',
161
- speed: 'Slow ⚡',
162
- cost: '$0.10/video (est.)',
163
- quality: 'Good',
164
- bestFor: 'Product demos, animated diagrams'
165
- }
166
- };
167
-
168
- return services[service] || null;
136
+ const services = {
137
+ gemini: {
138
+ id: "gemini",
139
+ name: "Gemini 2.5 Flash Image",
140
+ nickname: '"Nano Banana"',
141
+ provider: "Google",
142
+ apiKey: "GOOGLE_API_KEY",
143
+ speed: "Fast ⚡⚡⚡",
144
+ cost: "$0.02/image",
145
+ quality: "Good",
146
+ bestFor: "Quick hero images, high volume",
147
+ },
148
+ imagen: {
149
+ id: "imagen",
150
+ name: "Imagen 3",
151
+ nickname: "Premium Quality",
152
+ provider: "Google",
153
+ apiKey: "GOOGLE_API_KEY",
154
+ speed: "Medium ⚡⚡",
155
+ cost: "$0.03/image",
156
+ quality: "Excellent",
157
+ bestFor: "Premium hero images, photorealistic content",
158
+ },
159
+ dalle: {
160
+ id: "dalle",
161
+ name: "DALL-E 3",
162
+ nickname: "Creative AI",
163
+ provider: "OpenAI",
164
+ apiKey: "OPENAI_API_KEY",
165
+ speed: "Medium ⚡⚡",
166
+ cost: "$0.04-0.12/image",
167
+ quality: "Excellent",
168
+ bestFor: "Creative illustrations, concept art",
169
+ },
170
+ veo: {
171
+ id: "veo",
172
+ name: "Veo 2",
173
+ nickname: "Video Generation",
174
+ provider: "Google",
175
+ apiKey: "GOOGLE_API_KEY",
176
+ speed: "Slow ⚡",
177
+ cost: "$0.10/video (est.)",
178
+ quality: "Good",
179
+ bestFor: "Product demos, animated diagrams",
180
+ },
181
+ fal: {
182
+ id: "fal",
183
+ name: "Fal.ai (FLUX, Nano Banana Pro)",
184
+ nickname: "Premium Models",
185
+ provider: "Fal.ai",
186
+ apiKey: "FAL_KEY",
187
+ speed: "Fast ⚡⚡⚡",
188
+ cost: "$0.06-0.15/image",
189
+ quality: "Excellent",
190
+ bestFor: "High-quality branded content, FLUX models",
191
+ },
192
+ };
193
+
194
+ return services[service] || null;
169
195
  }
170
196
 
171
197
  /**
@@ -175,11 +201,11 @@ export function getServiceDisplayInfo(service) {
175
201
  * @returns {string} Status message
176
202
  */
177
203
  export function getConfigStatusMessage(verbose = false) {
178
- const validation = validateVisualConfig();
179
- const config = loadVisualConfig();
204
+ const validation = validateVisualConfig();
205
+ const config = loadVisualConfig();
180
206
 
181
- if (!validation.hasAnyAPIKeys) {
182
- return `⚠️ Visual content generation not configured
207
+ if (!validation.hasAnyAPIKeys) {
208
+ return `⚠️ Visual content generation not configured
183
209
 
184
210
  No API keys found. To enable image/video generation:
185
211
 
@@ -188,39 +214,41 @@ No API keys found. To enable image/video generation:
188
214
  2. Add to your .env file:
189
215
  GOOGLE_API_KEY=your_key_here # For Gemini, Imagen, Veo
190
216
  OPENAI_API_KEY=your_key_here # For DALL-E
217
+ FAL_KEY=your_key_here # For Fal.ai (FLUX, Nano Banana Pro)
191
218
 
192
219
  Get API keys:
193
220
  • Google: https://ai.google.dev/
194
- • OpenAI: https://platform.openai.com/api-keys`;
195
- }
196
-
197
- let message = `✅ Visual content generation configured\n\n`;
198
-
199
- // Show available services
200
- message += `📋 Available Services:\n`;
201
- for (const service of validation.availableServices) {
202
- const info = getServiceDisplayInfo(service);
203
- message += ` • ${info.name} - ${info.cost}\n`;
204
- }
205
-
206
- if (verbose) {
207
- message += `\n⚙️ Settings:\n`;
208
- message += ` Default Service: ${config.defaultService}\n`;
209
- message += ` Default Quality: ${config.defaultQuality}\n`;
210
- message += ` Assets Path: ${config.assetsPath}\n`;
211
- message += ` Daily Budget: $${config.dailyBudget.toFixed(2)}\n`;
212
- message += ` Monthly Budget: $${config.monthlyBudget.toFixed(2)}\n`;
213
- }
214
-
215
- // Show warnings
216
- if (validation.warnings.length > 0) {
217
- message += `\n⚠️ Warnings:\n`;
218
- for (const warning of validation.warnings) {
219
- message += ` • ${warning}\n`;
220
- }
221
- }
222
-
223
- return message;
221
+ • OpenAI: https://platform.openai.com/api-keys
222
+ • Fal.ai: https://fal.ai/dashboard/keys`;
223
+ }
224
+
225
+ let message = `✅ Visual content generation configured\n\n`;
226
+
227
+ // Show available services
228
+ message += `📋 Available Services:\n`;
229
+ for (const service of validation.availableServices) {
230
+ const info = getServiceDisplayInfo(service);
231
+ message += ` • ${info.name} - ${info.cost}\n`;
232
+ }
233
+
234
+ if (verbose) {
235
+ message += `\n⚙️ Settings:\n`;
236
+ message += ` Default Service: ${config.defaultService}\n`;
237
+ message += ` Default Quality: ${config.defaultQuality}\n`;
238
+ message += ` Assets Path: ${config.assetsPath}\n`;
239
+ message += ` Daily Budget: $${config.dailyBudget.toFixed(2)}\n`;
240
+ message += ` Monthly Budget: $${config.monthlyBudget.toFixed(2)}\n`;
241
+ }
242
+
243
+ // Show warnings
244
+ if (validation.warnings.length > 0) {
245
+ message += `\n⚠️ Warnings:\n`;
246
+ for (const warning of validation.warnings) {
247
+ message += ` • ${warning}\n`;
248
+ }
249
+ }
250
+
251
+ return message;
224
252
  }
225
253
 
226
254
  /**
@@ -230,33 +258,33 @@ Get API keys:
230
258
  * @returns {Promise<void>}
231
259
  */
232
260
  export async function updateEnvFile(updates) {
233
- const envPath = path.join(process.cwd(), '.env');
234
- let envContent = '';
235
-
236
- // Read existing .env file
237
- if (await fs.pathExists(envPath)) {
238
- envContent = await fs.readFile(envPath, 'utf-8');
239
- }
240
-
241
- // Update or add each key
242
- for (const [key, value] of Object.entries(updates)) {
243
- const regex = new RegExp(`^${key}=.*$`, 'm');
244
- const line = `${key}=${value}`;
245
-
246
- if (regex.test(envContent)) {
247
- // Update existing key
248
- envContent = envContent.replace(regex, line);
249
- } else {
250
- // Add new key
251
- envContent += `\n${line}`;
252
- }
253
- }
254
-
255
- // Write back to file
256
- await fs.writeFile(envPath, envContent.trim() + '\n');
257
-
258
- // Reload environment variables
259
- dotenv.config({ override: true });
261
+ const envPath = path.join(process.cwd(), ".env");
262
+ let envContent = "";
263
+
264
+ // Read existing .env file
265
+ if (await fs.pathExists(envPath)) {
266
+ envContent = await fs.readFile(envPath, "utf-8");
267
+ }
268
+
269
+ // Update or add each key
270
+ for (const [key, value] of Object.entries(updates)) {
271
+ const regex = new RegExp(`^${key}=.*$`, "m");
272
+ const line = `${key}=${value}`;
273
+
274
+ if (regex.test(envContent)) {
275
+ // Update existing key
276
+ envContent = envContent.replace(regex, line);
277
+ } else {
278
+ // Add new key
279
+ envContent += `\n${line}`;
280
+ }
281
+ }
282
+
283
+ // Write back to file
284
+ await fs.writeFile(envPath, envContent.trim() + "\n");
285
+
286
+ // Reload environment variables
287
+ dotenv.config({ override: true });
260
288
  }
261
289
 
262
290
  /**
@@ -266,15 +294,15 @@ export async function updateEnvFile(updates) {
266
294
  * @returns {string} Masked API key
267
295
  */
268
296
  export function maskAPIKey(apiKey) {
269
- if (!apiKey || apiKey.length < 16) {
270
- return '****';
271
- }
297
+ if (!apiKey || apiKey.length < 16) {
298
+ return "****";
299
+ }
272
300
 
273
- const start = apiKey.substring(0, 8);
274
- const end = apiKey.substring(apiKey.length - 4);
275
- const masked = `${start}${'*'.repeat(Math.max(4, apiKey.length - 12))}${end}`;
301
+ const start = apiKey.substring(0, 8);
302
+ const end = apiKey.substring(apiKey.length - 4);
303
+ const masked = `${start}${"*".repeat(Math.max(4, apiKey.length - 12))}${end}`;
276
304
 
277
- return masked;
305
+ return masked;
278
306
  }
279
307
 
280
308
  /**
@@ -284,57 +312,53 @@ export function maskAPIKey(apiKey) {
284
312
  * @returns {Promise<Object>} Test result
285
313
  */
286
314
  export async function testAPIKey(service) {
287
- const config = loadVisualConfig();
288
-
289
- try {
290
- if (service === 'google') {
291
- if (!config.googleApiKey) {
292
- return { success: false, error: 'GOOGLE_API_KEY not configured' };
293
- }
294
-
295
- // Simple test: list models
296
- const response = await fetch(
297
- `https://generativelanguage.googleapis.com/v1beta/models?key=${config.googleApiKey}`,
298
- { method: 'GET' }
299
- );
300
-
301
- if (response.ok) {
302
- return { success: true, service: 'google' };
303
- } else {
304
- const error = await response.text();
305
- return { success: false, error: `Google API test failed: ${error}` };
306
- }
307
- }
308
-
309
- if (service === 'openai') {
310
- if (!config.openaiApiKey) {
311
- return { success: false, error: 'OPENAI_API_KEY not configured' };
312
- }
313
-
314
- // Simple test: list models
315
- const response = await fetch(
316
- 'https://api.openai.com/v1/models',
317
- {
318
- method: 'GET',
319
- headers: {
320
- 'Authorization': `Bearer ${config.openaiApiKey}`
321
- }
322
- }
323
- );
324
-
325
- if (response.ok) {
326
- return { success: true, service: 'openai' };
327
- } else {
328
- const error = await response.text();
329
- return { success: false, error: `OpenAI API test failed: ${error}` };
330
- }
331
- }
332
-
333
- return { success: false, error: `Unknown service: ${service}` };
334
-
335
- } catch (error) {
336
- return { success: false, error: error.message };
337
- }
315
+ const config = loadVisualConfig();
316
+
317
+ try {
318
+ if (service === "google") {
319
+ if (!config.googleApiKey) {
320
+ return { success: false, error: "GOOGLE_API_KEY not configured" };
321
+ }
322
+
323
+ // Simple test: list models
324
+ const response = await fetch(
325
+ `https://generativelanguage.googleapis.com/v1beta/models?key=${config.googleApiKey}`,
326
+ { method: "GET" },
327
+ );
328
+
329
+ if (response.ok) {
330
+ return { success: true, service: "google" };
331
+ } else {
332
+ const error = await response.text();
333
+ return { success: false, error: `Google API test failed: ${error}` };
334
+ }
335
+ }
336
+
337
+ if (service === "openai") {
338
+ if (!config.openaiApiKey) {
339
+ return { success: false, error: "OPENAI_API_KEY not configured" };
340
+ }
341
+
342
+ // Simple test: list models
343
+ const response = await fetch("https://api.openai.com/v1/models", {
344
+ method: "GET",
345
+ headers: {
346
+ Authorization: `Bearer ${config.openaiApiKey}`,
347
+ },
348
+ });
349
+
350
+ if (response.ok) {
351
+ return { success: true, service: "openai" };
352
+ } else {
353
+ const error = await response.text();
354
+ return { success: false, error: `OpenAI API test failed: ${error}` };
355
+ }
356
+ }
357
+
358
+ return { success: false, error: `Unknown service: ${service}` };
359
+ } catch (error) {
360
+ return { success: false, error: error.message };
361
+ }
338
362
  }
339
363
 
340
364
  /**
@@ -343,39 +367,41 @@ export async function testAPIKey(service) {
343
367
  * @returns {Object} Configuration summary
344
368
  */
345
369
  export function getConfigSummary() {
346
- const validation = validateVisualConfig();
347
- const config = loadVisualConfig();
348
-
349
- return {
350
- configured: validation.hasAnyAPIKeys,
351
- services: {
352
- google: {
353
- configured: validation.hasGoogle,
354
- available: ['gemini', 'imagen', 'veo'].filter(s =>
355
- validation.availableServices.includes(s)
356
- )
357
- },
358
- openai: {
359
- configured: validation.hasOpenAI,
360
- available: validation.availableServices.includes('dalle') ? ['dalle'] : []
361
- }
362
- },
363
- settings: {
364
- defaultService: config.defaultService,
365
- defaultQuality: config.defaultQuality,
366
- assetsPath: config.assetsPath,
367
- budgets: {
368
- daily: config.dailyBudget,
369
- monthly: config.monthlyBudget,
370
- warnThreshold: config.warnThreshold
371
- }
372
- },
373
- validation: {
374
- isValid: validation.isValid,
375
- errors: validation.errors,
376
- warnings: validation.warnings
377
- }
378
- };
370
+ const validation = validateVisualConfig();
371
+ const config = loadVisualConfig();
372
+
373
+ return {
374
+ configured: validation.hasAnyAPIKeys,
375
+ services: {
376
+ google: {
377
+ configured: validation.hasGoogle,
378
+ available: ["gemini", "imagen", "veo"].filter((s) =>
379
+ validation.availableServices.includes(s),
380
+ ),
381
+ },
382
+ openai: {
383
+ configured: validation.hasOpenAI,
384
+ available: validation.availableServices.includes("dalle")
385
+ ? ["dalle"]
386
+ : [],
387
+ },
388
+ },
389
+ settings: {
390
+ defaultService: config.defaultService,
391
+ defaultQuality: config.defaultQuality,
392
+ assetsPath: config.assetsPath,
393
+ budgets: {
394
+ daily: config.dailyBudget,
395
+ monthly: config.monthlyBudget,
396
+ warnThreshold: config.warnThreshold,
397
+ },
398
+ },
399
+ validation: {
400
+ isValid: validation.isValid,
401
+ errors: validation.errors,
402
+ warnings: validation.warnings,
403
+ },
404
+ };
379
405
  }
380
406
 
381
407
  /**
@@ -385,8 +411,8 @@ export function getConfigSummary() {
385
411
  * @returns {boolean} True if service is available
386
412
  */
387
413
  export function isServiceAvailable(service) {
388
- const available = getAvailableServices();
389
- return available.includes(service);
414
+ const available = getAvailableServices();
415
+ return available.includes(service);
390
416
  }
391
417
 
392
418
  /**
@@ -396,29 +422,29 @@ export function isServiceAvailable(service) {
396
422
  * @returns {string} Recommended service name
397
423
  */
398
424
  export function getRecommendedService(type) {
399
- const available = getAvailableServices();
400
- const config = loadVisualConfig();
401
-
402
- // Recommendations by type
403
- const recommendations = {
404
- hero: ['imagen', 'dalle', 'gemini'], // Premium quality for hero
405
- illustration: ['dalle', 'imagen', 'gemini'], // Creative for illustrations
406
- diagram: ['gemini', 'dalle', 'imagen'], // Fast for diagrams
407
- screenshot: ['gemini', 'imagen', 'dalle'], // Fast for screenshots
408
- video: ['veo'] // Only Veo for video
409
- };
410
-
411
- const preferred = recommendations[type] || recommendations.hero;
412
-
413
- // Return first available from preferred list
414
- for (const service of preferred) {
415
- if (available.includes(service)) {
416
- return service;
417
- }
418
- }
419
-
420
- // Fallback to default or first available
421
- return available.includes(config.defaultService)
422
- ? config.defaultService
423
- : available[0];
425
+ const available = getAvailableServices();
426
+ const config = loadVisualConfig();
427
+
428
+ // Recommendations by type
429
+ const recommendations = {
430
+ hero: ["imagen", "dalle", "gemini"], // Premium quality for hero
431
+ illustration: ["dalle", "imagen", "gemini"], // Creative for illustrations
432
+ diagram: ["gemini", "dalle", "imagen"], // Fast for diagrams
433
+ screenshot: ["gemini", "imagen", "dalle"], // Fast for screenshots
434
+ video: ["veo"], // Only Veo for video
435
+ };
436
+
437
+ const preferred = recommendations[type] || recommendations.hero;
438
+
439
+ // Return first available from preferred list
440
+ for (const service of preferred) {
441
+ if (available.includes(service)) {
442
+ return service;
443
+ }
444
+ }
445
+
446
+ // Fallback to default or first available
447
+ return available.includes(config.defaultService)
448
+ ? config.defaultService
449
+ : available[0];
424
450
  }