myaidev-method 0.2.11 ā 0.2.15
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/.claude/CLAUDE.md +46 -0
- package/.claude/agents/content-production-coordinator.md +111 -0
- package/.claude/agents/proprietary-content-verifier.md +96 -0
- package/.claude/agents/visual-content-generator.md +520 -0
- package/.claude/commands/myai-coordinate-content.md +136 -0
- package/.claude/settings.local.json +3 -2
- package/.env.example +33 -0
- package/CHANGELOG.md +228 -0
- package/CONTENT_CREATION_GUIDE.md +3399 -0
- package/DEVELOPER_USE_CASES.md +2085 -0
- package/README.md +234 -2
- package/USER_GUIDE.md +156 -0
- package/VISUAL_GENERATION_FILE_ORGANIZATION.md +105 -0
- package/bin/cli.js +222 -0
- package/package.json +19 -3
- package/src/lib/asset-management.js +532 -0
- package/src/lib/update-manager.js +385 -0
- package/src/lib/visual-config-utils.js +424 -0
- package/src/lib/visual-generation-utils.js +668 -0
- package/src/scripts/configure-visual-apis.js +413 -0
- package/src/scripts/generate-visual-cli.js +279 -0
- package/src/templates/claude/agents/content-production-coordinator.md +111 -0
- package/src/templates/claude/agents/content-writer.md +209 -4
- package/src/templates/claude/agents/proprietary-content-verifier.md +96 -0
- package/src/templates/claude/agents/visual-content-generator.md +520 -0
- package/src/templates/claude/commands/myai-content-writer.md +33 -8
- package/src/templates/claude/commands/myai-coordinate-content.md +136 -0
- package/src/templates/claude/commands/myai-generate-visual.md +318 -0
- package/src/templates/codex/commands/myai-generate-visual.md +307 -0
- package/src/templates/gemini/commands/myai-generate-visual.md +200 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Visual Content Generation API Configuration Script
|
|
5
|
+
*
|
|
6
|
+
* Interactive configuration wizard for setting up Google AI and OpenAI API keys
|
|
7
|
+
* for image and video generation.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node configure-visual-apis.js
|
|
11
|
+
* OR
|
|
12
|
+
* /myai-configure visual (from Claude Code)
|
|
13
|
+
*
|
|
14
|
+
* Platform support: Claude Code, Gemini CLI, Codex CLI
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import inquirer from 'inquirer';
|
|
18
|
+
import fs from 'fs-extra';
|
|
19
|
+
import path from 'path';
|
|
20
|
+
import dotenv from 'dotenv';
|
|
21
|
+
import chalk from 'chalk';
|
|
22
|
+
import ora from 'ora';
|
|
23
|
+
import { fileURLToPath } from 'url';
|
|
24
|
+
|
|
25
|
+
// Load existing environment variables
|
|
26
|
+
dotenv.config();
|
|
27
|
+
|
|
28
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
29
|
+
const __dirname = path.dirname(__filename);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Main configuration function
|
|
33
|
+
*/
|
|
34
|
+
export async function configureVisualAPIs() {
|
|
35
|
+
console.log(chalk.blue('\nšØ Visual Content Generation Configuration'));
|
|
36
|
+
console.log(chalk.gray('Configure Google AI and OpenAI APIs for image/video generation\n'));
|
|
37
|
+
|
|
38
|
+
console.log(chalk.cyan('Supported Platforms:'));
|
|
39
|
+
console.log(chalk.gray('ā
Claude Code'));
|
|
40
|
+
console.log(chalk.gray('ā
Gemini CLI'));
|
|
41
|
+
console.log(chalk.gray('ā
Codex CLI\n'));
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
// Step 1: Which services to configure
|
|
45
|
+
const { services } = await inquirer.prompt([{
|
|
46
|
+
type: 'checkbox',
|
|
47
|
+
name: 'services',
|
|
48
|
+
message: 'Which services do you want to configure?',
|
|
49
|
+
choices: [
|
|
50
|
+
{
|
|
51
|
+
name: 'Google AI (Gemini Flash, Imagen 3, Veo 2)',
|
|
52
|
+
value: 'google',
|
|
53
|
+
checked: true
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'OpenAI (DALL-E 3)',
|
|
57
|
+
value: 'openai',
|
|
58
|
+
checked: true
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
validate: (answer) => {
|
|
62
|
+
if (answer.length < 1) {
|
|
63
|
+
return 'You must choose at least one service.';
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}]);
|
|
68
|
+
|
|
69
|
+
const config = {};
|
|
70
|
+
|
|
71
|
+
// Step 2: Configure Google AI
|
|
72
|
+
if (services.includes('google')) {
|
|
73
|
+
console.log(chalk.yellow('\nš Google AI API Setup:'));
|
|
74
|
+
console.log(chalk.gray('1. Visit: https://ai.google.dev/'));
|
|
75
|
+
console.log(chalk.gray('2. Sign in with your Google account'));
|
|
76
|
+
console.log(chalk.gray('3. Click "Get API key" in Google AI Studio'));
|
|
77
|
+
console.log(chalk.gray('4. Create or select a project'));
|
|
78
|
+
console.log(chalk.gray('5. Click "Create API key"'));
|
|
79
|
+
console.log(chalk.gray('6. Copy the API key\n'));
|
|
80
|
+
|
|
81
|
+
const currentGoogleKey = process.env.GOOGLE_API_KEY;
|
|
82
|
+
if (currentGoogleKey) {
|
|
83
|
+
console.log(chalk.gray(`Current key: ${maskKey(currentGoogleKey)}\n`));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const { googleKey, confirmGoogle } = await inquirer.prompt([
|
|
87
|
+
{
|
|
88
|
+
type: 'password',
|
|
89
|
+
name: 'googleKey',
|
|
90
|
+
message: 'Enter your Google API Key:',
|
|
91
|
+
mask: '*',
|
|
92
|
+
validate: (input) => {
|
|
93
|
+
if (!input) return 'API key is required';
|
|
94
|
+
if (input.length < 20) return 'API key appears to be too short';
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
type: 'confirm',
|
|
100
|
+
name: 'confirmGoogle',
|
|
101
|
+
message: 'Test API key connectivity?',
|
|
102
|
+
default: true
|
|
103
|
+
}
|
|
104
|
+
]);
|
|
105
|
+
|
|
106
|
+
config.GOOGLE_API_KEY = googleKey;
|
|
107
|
+
|
|
108
|
+
// Test Google API
|
|
109
|
+
if (confirmGoogle) {
|
|
110
|
+
const spinner = ora('Testing Google API connection...').start();
|
|
111
|
+
try {
|
|
112
|
+
const testResult = await testGoogleAPI(googleKey);
|
|
113
|
+
if (testResult.success) {
|
|
114
|
+
spinner.succeed(chalk.green('Google API connection successful!'));
|
|
115
|
+
} else {
|
|
116
|
+
spinner.fail(chalk.red(`Google API test failed: ${testResult.error}`));
|
|
117
|
+
const { continueAnyway } = await inquirer.prompt([{
|
|
118
|
+
type: 'confirm',
|
|
119
|
+
name: 'continueAnyway',
|
|
120
|
+
message: 'Continue with this API key anyway?',
|
|
121
|
+
default: false
|
|
122
|
+
}]);
|
|
123
|
+
if (!continueAnyway) {
|
|
124
|
+
delete config.GOOGLE_API_KEY;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} catch (error) {
|
|
128
|
+
spinner.fail(chalk.red(`Google API test error: ${error.message}`));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Step 3: Configure OpenAI
|
|
134
|
+
if (services.includes('openai')) {
|
|
135
|
+
console.log(chalk.yellow('\nš OpenAI API Setup:'));
|
|
136
|
+
console.log(chalk.gray('1. Visit: https://platform.openai.com/api-keys'));
|
|
137
|
+
console.log(chalk.gray('2. Sign in to your OpenAI account'));
|
|
138
|
+
console.log(chalk.gray('3. Click "Create new secret key"'));
|
|
139
|
+
console.log(chalk.gray('4. Name your key (e.g., "MyAIDev Visual Generation")'));
|
|
140
|
+
console.log(chalk.gray('5. Copy the API key (you won\'t see it again!)\n'));
|
|
141
|
+
|
|
142
|
+
const currentOpenAIKey = process.env.OPENAI_API_KEY;
|
|
143
|
+
if (currentOpenAIKey) {
|
|
144
|
+
console.log(chalk.gray(`Current key: ${maskKey(currentOpenAIKey)}\n`));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const { openaiKey, confirmOpenAI } = await inquirer.prompt([
|
|
148
|
+
{
|
|
149
|
+
type: 'password',
|
|
150
|
+
name: 'openaiKey',
|
|
151
|
+
message: 'Enter your OpenAI API Key:',
|
|
152
|
+
mask: '*',
|
|
153
|
+
validate: (input) => {
|
|
154
|
+
if (!input) return 'API key is required';
|
|
155
|
+
if (input.length < 20) return 'API key appears to be too short';
|
|
156
|
+
if (!input.startsWith('sk-')) return 'OpenAI API keys typically start with "sk-"';
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
type: 'confirm',
|
|
162
|
+
name: 'confirmOpenAI',
|
|
163
|
+
message: 'Test API key connectivity?',
|
|
164
|
+
default: true
|
|
165
|
+
}
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
config.OPENAI_API_KEY = openaiKey;
|
|
169
|
+
|
|
170
|
+
// Test OpenAI API
|
|
171
|
+
if (confirmOpenAI) {
|
|
172
|
+
const spinner = ora('Testing OpenAI API connection...').start();
|
|
173
|
+
try {
|
|
174
|
+
const testResult = await testOpenAIAPI(openaiKey);
|
|
175
|
+
if (testResult.success) {
|
|
176
|
+
spinner.succeed(chalk.green('OpenAI API connection successful!'));
|
|
177
|
+
} else {
|
|
178
|
+
spinner.fail(chalk.red(`OpenAI API test failed: ${testResult.error}`));
|
|
179
|
+
const { continueAnyway } = await inquirer.prompt([{
|
|
180
|
+
type: 'confirm',
|
|
181
|
+
name: 'continueAnyway',
|
|
182
|
+
message: 'Continue with this API key anyway?',
|
|
183
|
+
default: false
|
|
184
|
+
}]);
|
|
185
|
+
if (!continueAnyway) {
|
|
186
|
+
delete config.OPENAI_API_KEY;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} catch (error) {
|
|
190
|
+
spinner.fail(chalk.red(`OpenAI API test error: ${error.message}`));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Step 4: Preferences
|
|
196
|
+
if (Object.keys(config).length > 0) {
|
|
197
|
+
console.log(chalk.yellow('\nāļø Preferences:\n'));
|
|
198
|
+
|
|
199
|
+
const serviceChoices = [];
|
|
200
|
+
if (config.GOOGLE_API_KEY) {
|
|
201
|
+
serviceChoices.push(
|
|
202
|
+
{ name: 'Gemini Flash (Fast & Cheap, $0.02)', value: 'gemini' },
|
|
203
|
+
{ name: 'Imagen 3 (Premium Quality, $0.03)', value: 'imagen' }
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
if (config.OPENAI_API_KEY) {
|
|
207
|
+
serviceChoices.push(
|
|
208
|
+
{ name: 'DALL-E 3 (Creative, $0.04-0.12)', value: 'dalle' }
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const preferences = await inquirer.prompt([
|
|
213
|
+
{
|
|
214
|
+
type: 'list',
|
|
215
|
+
name: 'defaultService',
|
|
216
|
+
message: 'Default image generation service?',
|
|
217
|
+
choices: serviceChoices,
|
|
218
|
+
default: 'gemini'
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
type: 'list',
|
|
222
|
+
name: 'defaultQuality',
|
|
223
|
+
message: 'Default quality level?',
|
|
224
|
+
choices: [
|
|
225
|
+
{ name: 'Standard (faster, cheaper)', value: 'standard' },
|
|
226
|
+
{ name: 'HD (higher quality, more expensive)', value: 'hd' }
|
|
227
|
+
],
|
|
228
|
+
default: 'standard'
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
type: 'number',
|
|
232
|
+
name: 'dailyBudget',
|
|
233
|
+
message: 'Daily budget limit (USD)?',
|
|
234
|
+
default: 5.00,
|
|
235
|
+
validate: (input) => {
|
|
236
|
+
if (input <= 0) return 'Budget must be greater than 0';
|
|
237
|
+
if (input > 1000) return 'Budget seems unusually high (max: $1000)';
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
type: 'number',
|
|
243
|
+
name: 'monthlyBudget',
|
|
244
|
+
message: 'Monthly budget limit (USD)?',
|
|
245
|
+
default: 50.00,
|
|
246
|
+
validate: (input) => {
|
|
247
|
+
if (input <= 0) return 'Budget must be greater than 0';
|
|
248
|
+
if (input > 10000) return 'Budget seems unusually high (max: $10,000)';
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
]);
|
|
253
|
+
|
|
254
|
+
config.VISUAL_DEFAULT_SERVICE = preferences.defaultService;
|
|
255
|
+
config.VISUAL_DEFAULT_QUALITY = preferences.defaultQuality;
|
|
256
|
+
config.VISUAL_DAILY_BUDGET = preferences.dailyBudget;
|
|
257
|
+
config.VISUAL_MONTHLY_BUDGET = preferences.monthlyBudget;
|
|
258
|
+
config.VISUAL_WARN_THRESHOLD = 0.80;
|
|
259
|
+
config.VISUAL_ASSETS_PATH = './content-assets';
|
|
260
|
+
|
|
261
|
+
// Step 5: Update .env file
|
|
262
|
+
console.log('');
|
|
263
|
+
const spinner = ora('Updating .env file...').start();
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
await updateEnvFile(config);
|
|
267
|
+
spinner.succeed(chalk.green('.env file updated successfully!'));
|
|
268
|
+
} catch (error) {
|
|
269
|
+
spinner.fail(chalk.red(`Failed to update .env: ${error.message}`));
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Step 6: Summary
|
|
274
|
+
console.log(chalk.green('\nā
Configuration Complete!\n'));
|
|
275
|
+
|
|
276
|
+
console.log(chalk.cyan('š Summary:'));
|
|
277
|
+
if (config.GOOGLE_API_KEY) {
|
|
278
|
+
console.log(chalk.gray(` ā
Google AI configured (Gemini, Imagen, Veo)`));
|
|
279
|
+
}
|
|
280
|
+
if (config.OPENAI_API_KEY) {
|
|
281
|
+
console.log(chalk.gray(` ā
OpenAI configured (DALL-E 3)`));
|
|
282
|
+
}
|
|
283
|
+
console.log(chalk.gray(` Default service: ${config.VISUAL_DEFAULT_SERVICE}`));
|
|
284
|
+
console.log(chalk.gray(` Default quality: ${config.VISUAL_DEFAULT_QUALITY}`));
|
|
285
|
+
console.log(chalk.gray(` Daily budget: $${config.VISUAL_DAILY_BUDGET.toFixed(2)}`));
|
|
286
|
+
console.log(chalk.gray(` Monthly budget: $${config.VISUAL_MONTHLY_BUDGET.toFixed(2)}`));
|
|
287
|
+
|
|
288
|
+
console.log(chalk.yellow('\nš” Quick Start:\n'));
|
|
289
|
+
console.log(chalk.cyan(' # Generate hero image'));
|
|
290
|
+
console.log(chalk.gray(' /myai-generate-visual "Modern developer workspace"\n'));
|
|
291
|
+
|
|
292
|
+
console.log(chalk.cyan(' # Generate with specific service'));
|
|
293
|
+
console.log(chalk.gray(' /myai-generate-visual "AI concept" --type=illustration --service=dalle\n'));
|
|
294
|
+
|
|
295
|
+
console.log(chalk.cyan(' # Generate article with images'));
|
|
296
|
+
console.log(chalk.gray(' /myai-content-writer "Article Title" --with-images\n'));
|
|
297
|
+
|
|
298
|
+
console.log(chalk.white('š Documentation:'));
|
|
299
|
+
console.log(chalk.gray(' VISUAL_CONTENT_GENERATION_GUIDE.md - Complete guide'));
|
|
300
|
+
console.log(chalk.gray(' CONTENT_CREATION_GUIDE.md - Content workflow\n'));
|
|
301
|
+
|
|
302
|
+
} else {
|
|
303
|
+
console.log(chalk.yellow('\nā ļø No configuration saved (no valid API keys)\n'));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
} catch (error) {
|
|
307
|
+
if (error.isTtyError) {
|
|
308
|
+
console.error(chalk.red('\nā Prompt couldn\'t be rendered in the current environment'));
|
|
309
|
+
} else {
|
|
310
|
+
console.error(chalk.red(`\nā Configuration error: ${error.message}`));
|
|
311
|
+
}
|
|
312
|
+
process.exit(1);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Update .env file with configuration
|
|
318
|
+
*/
|
|
319
|
+
async function updateEnvFile(config) {
|
|
320
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
321
|
+
let envContent = '';
|
|
322
|
+
|
|
323
|
+
// Read existing .env if it exists
|
|
324
|
+
if (await fs.pathExists(envPath)) {
|
|
325
|
+
envContent = await fs.readFile(envPath, 'utf-8');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Update or add each config value
|
|
329
|
+
for (const [key, value] of Object.entries(config)) {
|
|
330
|
+
const regex = new RegExp(`^${key}=.*$`, 'm');
|
|
331
|
+
const line = `${key}=${value}`;
|
|
332
|
+
|
|
333
|
+
if (regex.test(envContent)) {
|
|
334
|
+
// Update existing key
|
|
335
|
+
envContent = envContent.replace(regex, line);
|
|
336
|
+
} else {
|
|
337
|
+
// Add new key
|
|
338
|
+
if (!envContent.endsWith('\n') && envContent.length > 0) {
|
|
339
|
+
envContent += '\n';
|
|
340
|
+
}
|
|
341
|
+
envContent += `${line}\n`;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Write back to file
|
|
346
|
+
await fs.writeFile(envPath, envContent);
|
|
347
|
+
|
|
348
|
+
// Reload environment variables
|
|
349
|
+
dotenv.config({ override: true });
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Test Google API connectivity
|
|
354
|
+
*/
|
|
355
|
+
async function testGoogleAPI(apiKey) {
|
|
356
|
+
try {
|
|
357
|
+
const response = await fetch(
|
|
358
|
+
`https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`,
|
|
359
|
+
{ method: 'GET' }
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
if (response.ok) {
|
|
363
|
+
return { success: true };
|
|
364
|
+
} else {
|
|
365
|
+
const errorText = await response.text();
|
|
366
|
+
return { success: false, error: `HTTP ${response.status}: ${errorText}` };
|
|
367
|
+
}
|
|
368
|
+
} catch (error) {
|
|
369
|
+
return { success: false, error: error.message };
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Test OpenAI API connectivity
|
|
375
|
+
*/
|
|
376
|
+
async function testOpenAIAPI(apiKey) {
|
|
377
|
+
try {
|
|
378
|
+
const response = await fetch(
|
|
379
|
+
'https://api.openai.com/v1/models',
|
|
380
|
+
{
|
|
381
|
+
method: 'GET',
|
|
382
|
+
headers: {
|
|
383
|
+
'Authorization': `Bearer ${apiKey}`
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
if (response.ok) {
|
|
389
|
+
return { success: true };
|
|
390
|
+
} else {
|
|
391
|
+
const errorData = await response.json();
|
|
392
|
+
return { success: false, error: errorData.error?.message || `HTTP ${response.status}` };
|
|
393
|
+
}
|
|
394
|
+
} catch (error) {
|
|
395
|
+
return { success: false, error: error.message };
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Mask API key for display
|
|
401
|
+
*/
|
|
402
|
+
function maskKey(key) {
|
|
403
|
+
if (!key || key.length < 16) return '****';
|
|
404
|
+
return `${key.substring(0, 8)}${'*'.repeat(Math.max(4, key.length - 12))}${key.substring(key.length - 4)}`;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Run if executed directly
|
|
408
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
409
|
+
configureVisualAPIs().catch(error => {
|
|
410
|
+
console.error(chalk.red(`\nFatal error: ${error.message}`));
|
|
411
|
+
process.exit(1);
|
|
412
|
+
});
|
|
413
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Visual Content Generation CLI Script
|
|
5
|
+
*
|
|
6
|
+
* Standalone CLI script for generating images and videos.
|
|
7
|
+
* Used by Gemini CLI and Codex CLI platforms.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node generate-visual-cli.js "prompt" --type=hero --service=gemini
|
|
11
|
+
*
|
|
12
|
+
* Platform support: Claude Code, Gemini CLI, Codex CLI
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { generateImage, generateVideoVeo, estimateCost, validateAPIKeys } from '../lib/visual-generation-utils.js';
|
|
16
|
+
import { saveImage, saveVideo, generateMarkdownReference, checkBudgetLimit, getTodaysCost, getMonthCost } from '../lib/asset-management.js';
|
|
17
|
+
import { hasAnyAPIKeys, getConfigStatusMessage, getServiceDisplayInfo } from '../lib/visual-config-utils.js';
|
|
18
|
+
import chalk from 'chalk';
|
|
19
|
+
import ora from 'ora';
|
|
20
|
+
|
|
21
|
+
// Parse command line arguments
|
|
22
|
+
const args = process.argv.slice(2);
|
|
23
|
+
|
|
24
|
+
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
25
|
+
showHelp();
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Extract prompt (first non-option argument)
|
|
30
|
+
const prompt = args.find(arg => !arg.startsWith('--'));
|
|
31
|
+
|
|
32
|
+
if (!prompt) {
|
|
33
|
+
console.error(chalk.red('ā Error: Prompt is required'));
|
|
34
|
+
console.log('\nUsage: node generate-visual-cli.js "your prompt" --type=hero --service=gemini');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Parse options
|
|
39
|
+
const options = {
|
|
40
|
+
type: getOption('type', 'hero'),
|
|
41
|
+
service: getOption('service', null),
|
|
42
|
+
size: getOption('size', '1024x1024'),
|
|
43
|
+
quality: getOption('quality', 'standard'),
|
|
44
|
+
description: getOption('description', null)
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Main execution
|
|
48
|
+
(async () => {
|
|
49
|
+
try {
|
|
50
|
+
// Check configuration
|
|
51
|
+
if (!hasAnyAPIKeys()) {
|
|
52
|
+
console.log(chalk.yellow(getConfigStatusMessage()));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Validate type
|
|
57
|
+
const validTypes = ['hero', 'illustration', 'diagram', 'screenshot', 'video'];
|
|
58
|
+
if (!validTypes.includes(options.type)) {
|
|
59
|
+
console.error(chalk.red(`ā Invalid type: ${options.type}`));
|
|
60
|
+
console.log(chalk.gray(`Valid types: ${validTypes.join(', ')}`));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Display configuration
|
|
65
|
+
console.log(chalk.blue('\nšØ Visual Content Generation\n'));
|
|
66
|
+
console.log(chalk.gray(`Type: ${options.type}`));
|
|
67
|
+
console.log(chalk.gray(`Prompt: "${prompt}"`));
|
|
68
|
+
if (options.service) {
|
|
69
|
+
console.log(chalk.gray(`Service: ${options.service}`));
|
|
70
|
+
}
|
|
71
|
+
console.log('');
|
|
72
|
+
|
|
73
|
+
// Estimate cost
|
|
74
|
+
let service = options.service;
|
|
75
|
+
if (!service) {
|
|
76
|
+
// Auto-select based on available services
|
|
77
|
+
const validation = validateAPIKeys();
|
|
78
|
+
service = validation.availableServices[0];
|
|
79
|
+
console.log(chalk.gray(`Auto-selected service: ${service}`));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const cost = estimateCost(service, { quality: options.quality, size: options.size });
|
|
83
|
+
|
|
84
|
+
// Check budget
|
|
85
|
+
const budget = await checkBudgetLimit(cost);
|
|
86
|
+
const todayCost = await getTodaysCost();
|
|
87
|
+
const monthCost = await getMonthCost();
|
|
88
|
+
|
|
89
|
+
console.log(chalk.cyan('š° Cost Estimate:'));
|
|
90
|
+
console.log(chalk.gray(` This generation: $${cost.toFixed(2)}`));
|
|
91
|
+
console.log(chalk.gray(` Today: $${todayCost.toFixed(2)} / $${budget.dailyBudget.toFixed(2)} (${Math.round((todayCost/budget.dailyBudget)*100)}%)`));
|
|
92
|
+
console.log(chalk.gray(` Month: $${monthCost.toFixed(2)} / $${budget.monthlyBudget.toFixed(2)} (${Math.round((monthCost/budget.monthlyBudget)*100)}%)`));
|
|
93
|
+
console.log('');
|
|
94
|
+
|
|
95
|
+
// Check if budget exceeded
|
|
96
|
+
if (budget.exceedsDailyBudget) {
|
|
97
|
+
console.error(chalk.red('ā Daily budget limit exceeded!'));
|
|
98
|
+
console.log(chalk.yellow(`\nCurrent usage: $${budget.todaysCost.toFixed(2)}`));
|
|
99
|
+
console.log(chalk.yellow(`Daily limit: $${budget.dailyBudget.toFixed(2)}`));
|
|
100
|
+
console.log(chalk.gray('\nOptions:'));
|
|
101
|
+
console.log(chalk.gray('1. Increase VISUAL_DAILY_BUDGET in .env'));
|
|
102
|
+
console.log(chalk.gray('2. Wait until tomorrow (resets at midnight)'));
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (budget.exceedsMonthlyBudget) {
|
|
107
|
+
console.error(chalk.red('ā Monthly budget limit exceeded!'));
|
|
108
|
+
console.log(chalk.yellow(`\nCurrent usage: $${budget.monthCost.toFixed(2)}`));
|
|
109
|
+
console.log(chalk.yellow(`Monthly limit: $${budget.monthlyBudget.toFixed(2)}`));
|
|
110
|
+
console.log(chalk.gray('\nOptions:'));
|
|
111
|
+
console.log(chalk.gray('1. Increase VISUAL_MONTHLY_BUDGET in .env'));
|
|
112
|
+
console.log(chalk.gray('2. Wait until next month'));
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Warnings
|
|
117
|
+
if (budget.dailyWarning) {
|
|
118
|
+
console.log(chalk.yellow(`ā ļø Warning: Approaching daily budget limit (${Math.round((budget.newDailyCost/budget.dailyBudget)*100)}%)`));
|
|
119
|
+
console.log('');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Generate content
|
|
123
|
+
const spinner = ora(`Generating ${options.type} using ${service}...`).start();
|
|
124
|
+
|
|
125
|
+
let result;
|
|
126
|
+
let saved;
|
|
127
|
+
|
|
128
|
+
if (options.type === 'video') {
|
|
129
|
+
// Generate video
|
|
130
|
+
spinner.text = `Generating video using ${service}...`;
|
|
131
|
+
result = await generateVideoVeo(prompt, {
|
|
132
|
+
duration: 5,
|
|
133
|
+
aspectRatio: '16:9'
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
spinner.text = 'Saving video...';
|
|
137
|
+
saved = await saveVideo(result.buffer || Buffer.from(result.data, 'base64'), {
|
|
138
|
+
description: options.description || prompt,
|
|
139
|
+
service: result.service,
|
|
140
|
+
cost: result.cost,
|
|
141
|
+
prompt: prompt,
|
|
142
|
+
duration: result.duration || 5
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
} else {
|
|
146
|
+
// Generate image
|
|
147
|
+
spinner.text = `Generating ${options.type} using ${service}...`;
|
|
148
|
+
result = await generateImage(prompt, {
|
|
149
|
+
preferredService: service,
|
|
150
|
+
type: options.type,
|
|
151
|
+
quality: options.quality,
|
|
152
|
+
size: options.size
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
spinner.text = 'Saving image...';
|
|
156
|
+
saved = await saveImage(result.buffer, {
|
|
157
|
+
type: options.type,
|
|
158
|
+
description: options.description || prompt,
|
|
159
|
+
service: result.service,
|
|
160
|
+
cost: result.cost,
|
|
161
|
+
prompt: result.prompt
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
spinner.succeed(chalk.green(`${options.type === 'video' ? 'Video' : 'Image'} generated successfully!`));
|
|
166
|
+
|
|
167
|
+
// Display results
|
|
168
|
+
console.log('');
|
|
169
|
+
console.log(chalk.cyan(`šø Details:`));
|
|
170
|
+
|
|
171
|
+
const serviceInfo = getServiceDisplayInfo(result.service);
|
|
172
|
+
if (serviceInfo) {
|
|
173
|
+
console.log(chalk.gray(` Service: ${serviceInfo.name}`));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
console.log(chalk.gray(` Type: ${options.type.charAt(0).toUpperCase() + options.type.slice(1)}`));
|
|
177
|
+
console.log(chalk.gray(` Cost: $${result.cost.toFixed(2)}`));
|
|
178
|
+
console.log(chalk.gray(` File: ${saved.relativePath}`));
|
|
179
|
+
console.log(chalk.gray(` Size: ${(saved.size / 1024).toFixed(1)} KB`));
|
|
180
|
+
if (options.type === 'video' && result.duration) {
|
|
181
|
+
console.log(chalk.gray(` Duration: ${result.duration}s`));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Display updated budget
|
|
185
|
+
const newTodayCost = await getTodaysCost();
|
|
186
|
+
const newMonthCost = await getMonthCost();
|
|
187
|
+
|
|
188
|
+
console.log('');
|
|
189
|
+
console.log(chalk.cyan('š° Updated Budget:'));
|
|
190
|
+
console.log(chalk.gray(` Today: $${newTodayCost.toFixed(2)} / $${budget.dailyBudget.toFixed(2)} (${Math.round((newTodayCost/budget.dailyBudget)*100)}%)`));
|
|
191
|
+
console.log(chalk.gray(` Month: $${newMonthCost.toFixed(2)} / $${budget.monthlyBudget.toFixed(2)} (${Math.round((newMonthCost/budget.monthlyBudget)*100)}%)`));
|
|
192
|
+
|
|
193
|
+
// Generate markdown
|
|
194
|
+
const altText = `${options.type} ${options.type === 'video' ? 'video' : 'image'}: ${prompt}`;
|
|
195
|
+
const markdown = generateMarkdownReference(saved.relativePath, altText);
|
|
196
|
+
|
|
197
|
+
console.log('');
|
|
198
|
+
console.log(chalk.cyan('š Markdown Reference:'));
|
|
199
|
+
console.log(chalk.white(markdown));
|
|
200
|
+
console.log('');
|
|
201
|
+
console.log(chalk.gray('Copy and paste into your content ā'));
|
|
202
|
+
console.log('');
|
|
203
|
+
|
|
204
|
+
} catch (error) {
|
|
205
|
+
console.error(chalk.red(`\nā Error: ${error.message}`));
|
|
206
|
+
|
|
207
|
+
// Provide helpful error messages
|
|
208
|
+
if (error.message.includes('rate_limit') || error.message.includes('rate limit')) {
|
|
209
|
+
console.log(chalk.yellow('\nā ļø Rate limited by API'));
|
|
210
|
+
console.log(chalk.gray('Options:'));
|
|
211
|
+
console.log(chalk.gray('1. Wait 60 seconds and try again'));
|
|
212
|
+
console.log(chalk.gray('2. Switch to different service: --service=dalle'));
|
|
213
|
+
console.log(chalk.gray('3. Check your API quota limits'));
|
|
214
|
+
} else if (error.message.includes('API key') || error.message.includes('unauthorized')) {
|
|
215
|
+
console.log(chalk.yellow('\nā ļø API key issue'));
|
|
216
|
+
console.log(chalk.gray('Run configuration: node src/scripts/configure-visual-apis.js'));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
})();
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Get option value from command line arguments
|
|
225
|
+
*/
|
|
226
|
+
function getOption(name, defaultValue) {
|
|
227
|
+
const arg = args.find(a => a.startsWith(`--${name}=`));
|
|
228
|
+
if (arg) {
|
|
229
|
+
return arg.split('=')[1];
|
|
230
|
+
}
|
|
231
|
+
return defaultValue;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Show help message
|
|
236
|
+
*/
|
|
237
|
+
function showHelp() {
|
|
238
|
+
console.log(chalk.blue('\nšØ Visual Content Generation CLI\n'));
|
|
239
|
+
|
|
240
|
+
console.log(chalk.white('Usage:'));
|
|
241
|
+
console.log(chalk.gray(' node generate-visual-cli.js "prompt" [options]\n'));
|
|
242
|
+
|
|
243
|
+
console.log(chalk.white('Options:'));
|
|
244
|
+
console.log(chalk.gray(' --type=<type> Content type (default: hero)'));
|
|
245
|
+
console.log(chalk.gray(' Values: hero, illustration, diagram, screenshot, video'));
|
|
246
|
+
console.log(chalk.gray(' --service=<service> AI service (default: auto-select)'));
|
|
247
|
+
console.log(chalk.gray(' Values: gemini, imagen, dalle, veo'));
|
|
248
|
+
console.log(chalk.gray(' --size=<size> Image size (default: 1024x1024)'));
|
|
249
|
+
console.log(chalk.gray(' Values: 1024x1024, 1792x1024, 1024x1792'));
|
|
250
|
+
console.log(chalk.gray(' --quality=<quality> Quality level (default: standard)'));
|
|
251
|
+
console.log(chalk.gray(' Values: standard, hd'));
|
|
252
|
+
console.log(chalk.gray(' --description=<desc> Filename description (default: from prompt)\n'));
|
|
253
|
+
|
|
254
|
+
console.log(chalk.white('Examples:'));
|
|
255
|
+
console.log(chalk.gray(' # Generate hero image (auto-select service)'));
|
|
256
|
+
console.log(chalk.cyan(' node generate-visual-cli.js "Modern developer workspace"\n'));
|
|
257
|
+
|
|
258
|
+
console.log(chalk.gray(' # Generate diagram with Gemini'));
|
|
259
|
+
console.log(chalk.cyan(' node generate-visual-cli.js "System architecture" --type=diagram --service=gemini\n'));
|
|
260
|
+
|
|
261
|
+
console.log(chalk.gray(' # Generate HD illustration with DALL-E'));
|
|
262
|
+
console.log(chalk.cyan(' node generate-visual-cli.js "AI brain network" --type=illustration --service=dalle --quality=hd\n'));
|
|
263
|
+
|
|
264
|
+
console.log(chalk.gray(' # Generate video'));
|
|
265
|
+
console.log(chalk.cyan(' node generate-visual-cli.js "Product demo" --type=video --service=veo\n'));
|
|
266
|
+
|
|
267
|
+
console.log(chalk.white('Services:'));
|
|
268
|
+
console.log(chalk.gray(' gemini Gemini 2.5 Flash - Fast, $0.02/image'));
|
|
269
|
+
console.log(chalk.gray(' imagen Imagen 3 - Premium quality, $0.03/image'));
|
|
270
|
+
console.log(chalk.gray(' dalle DALL-E 3 - Creative, $0.04-0.12/image'));
|
|
271
|
+
console.log(chalk.gray(' veo Veo 2 - Video generation, $0.10/video\n'));
|
|
272
|
+
|
|
273
|
+
console.log(chalk.white('Prerequisites:'));
|
|
274
|
+
console.log(chalk.gray(' 1. Configure API keys: node src/scripts/configure-visual-apis.js'));
|
|
275
|
+
console.log(chalk.gray(' 2. Or add to .env: GOOGLE_API_KEY=xxx and/or OPENAI_API_KEY=xxx\n'));
|
|
276
|
+
|
|
277
|
+
console.log(chalk.white('Documentation:'));
|
|
278
|
+
console.log(chalk.gray(' See VISUAL_CONTENT_GENERATION_GUIDE.md for complete guide\n'));
|
|
279
|
+
}
|