myaidev-method 0.2.24-1 → 0.2.24-2
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-plugin/plugin.json +251 -0
- package/PLUGIN_ARCHITECTURE.md +276 -0
- package/README.md +204 -0
- package/USER_GUIDE.md +436 -9
- package/bin/cli.js +152 -0
- package/extension.json +174 -0
- package/hooks/hooks.json +221 -0
- package/marketplace.json +179 -0
- package/package.json +15 -3
- package/skills/content-verifier/SKILL.md +178 -0
- package/skills/content-writer/SKILL.md +151 -0
- package/skills/coolify-deployer/SKILL.md +207 -0
- package/skills/openstack-manager/SKILL.md +213 -0
- package/skills/security-auditor/SKILL.md +180 -0
- package/skills/security-tester/SKILL.md +171 -0
- package/skills/sparc-architect/SKILL.md +146 -0
- package/skills/sparc-coder/SKILL.md +136 -0
- package/skills/sparc-documenter/SKILL.md +195 -0
- package/skills/sparc-reviewer/SKILL.md +179 -0
- package/skills/sparc-tester/SKILL.md +156 -0
- package/skills/visual-generator/SKILL.md +147 -0
- package/skills/wordpress-publisher/SKILL.md +150 -0
- package/src/lib/content-coordinator.js +2562 -0
- package/src/lib/installation-detector.js +266 -0
- package/src/lib/visual-config-utils.js +1 -1
- package/src/lib/visual-generation-utils.js +34 -14
- package/src/scripts/generate-visual-cli.js +39 -10
- package/src/scripts/ping.js +0 -1
- package/src/templates/claude/agents/content-production-coordinator.md +689 -15
- package/src/templates/claude/commands/myai-content-enrichment.md +227 -0
- package/src/templates/claude/commands/myai-content-writer.md +48 -37
- package/src/templates/claude/commands/myai-coordinate-content.md +347 -11
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installation Detection Utility
|
|
3
|
+
*
|
|
4
|
+
* Detects the current installation state of MyAIDev Method:
|
|
5
|
+
* - Legacy installation (npx-based file copying)
|
|
6
|
+
* - Plugin installation (Claude Code plugin system)
|
|
7
|
+
* - Both (dual installation)
|
|
8
|
+
* - None (not installed)
|
|
9
|
+
*
|
|
10
|
+
* This utility ensures backward compatibility during migration to plugin architecture.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from 'fs-extra';
|
|
14
|
+
import path from 'path';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Installation types enum
|
|
18
|
+
*/
|
|
19
|
+
export const InstallationType = {
|
|
20
|
+
NONE: 'none',
|
|
21
|
+
LEGACY: 'legacy',
|
|
22
|
+
PLUGIN: 'plugin',
|
|
23
|
+
BOTH: 'both'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Detect the current installation state of MyAIDev Method
|
|
28
|
+
*
|
|
29
|
+
* @param {string} projectRoot - The project root directory
|
|
30
|
+
* @returns {Promise<Object>} Installation detection result
|
|
31
|
+
*/
|
|
32
|
+
export async function detectInstallation(projectRoot = process.cwd()) {
|
|
33
|
+
const checks = {
|
|
34
|
+
// Legacy installation indicators
|
|
35
|
+
hasClaudeDir: await fs.pathExists(path.join(projectRoot, '.claude')),
|
|
36
|
+
hasClaudeCommands: await fs.pathExists(path.join(projectRoot, '.claude', 'commands')),
|
|
37
|
+
hasClaudeAgents: await fs.pathExists(path.join(projectRoot, '.claude', 'agents')),
|
|
38
|
+
hasManifest: await fs.pathExists(path.join(projectRoot, '.myaidev-manifest.json')),
|
|
39
|
+
|
|
40
|
+
// Plugin installation indicators
|
|
41
|
+
hasPluginDir: await fs.pathExists(path.join(projectRoot, '.claude-plugin')),
|
|
42
|
+
hasPluginJson: await fs.pathExists(path.join(projectRoot, '.claude-plugin', 'plugin.json')),
|
|
43
|
+
|
|
44
|
+
// Skills-based installation
|
|
45
|
+
hasSkills: await fs.pathExists(path.join(projectRoot, 'skills')),
|
|
46
|
+
|
|
47
|
+
// Check for myaidev-specific files
|
|
48
|
+
hasMyAIDevCommands: false,
|
|
49
|
+
hasMyAIDevAgents: false
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Check for myaidev-specific commands
|
|
53
|
+
if (checks.hasClaudeCommands) {
|
|
54
|
+
const commandsDir = path.join(projectRoot, '.claude', 'commands');
|
|
55
|
+
try {
|
|
56
|
+
const files = await fs.readdir(commandsDir);
|
|
57
|
+
checks.hasMyAIDevCommands = files.some(f => f.startsWith('myai-'));
|
|
58
|
+
} catch (e) {
|
|
59
|
+
// Directory doesn't exist or not readable
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check for myaidev-specific agents
|
|
64
|
+
if (checks.hasClaudeAgents) {
|
|
65
|
+
const agentsDir = path.join(projectRoot, '.claude', 'agents');
|
|
66
|
+
try {
|
|
67
|
+
const files = await fs.readdir(agentsDir);
|
|
68
|
+
checks.hasMyAIDevAgents = files.some(f =>
|
|
69
|
+
f.includes('content-writer') ||
|
|
70
|
+
f.includes('visual') ||
|
|
71
|
+
f.includes('wordpress')
|
|
72
|
+
);
|
|
73
|
+
} catch (e) {
|
|
74
|
+
// Directory doesn't exist or not readable
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Determine installation type
|
|
79
|
+
const hasLegacy = checks.hasManifest || (checks.hasMyAIDevCommands || checks.hasMyAIDevAgents);
|
|
80
|
+
const hasPlugin = checks.hasPluginJson;
|
|
81
|
+
|
|
82
|
+
let installationType;
|
|
83
|
+
if (hasLegacy && hasPlugin) {
|
|
84
|
+
installationType = InstallationType.BOTH;
|
|
85
|
+
} else if (hasLegacy) {
|
|
86
|
+
installationType = InstallationType.LEGACY;
|
|
87
|
+
} else if (hasPlugin) {
|
|
88
|
+
installationType = InstallationType.PLUGIN;
|
|
89
|
+
} else {
|
|
90
|
+
installationType = InstallationType.NONE;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Get version info if available
|
|
94
|
+
let legacyVersion = null;
|
|
95
|
+
let pluginVersion = null;
|
|
96
|
+
|
|
97
|
+
if (checks.hasManifest) {
|
|
98
|
+
try {
|
|
99
|
+
const manifest = await fs.readJson(path.join(projectRoot, '.myaidev-manifest.json'));
|
|
100
|
+
legacyVersion = manifest.version || '1.0.0';
|
|
101
|
+
} catch (e) {
|
|
102
|
+
// Manifest unreadable
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (checks.hasPluginJson) {
|
|
107
|
+
try {
|
|
108
|
+
const pluginJson = await fs.readJson(path.join(projectRoot, '.claude-plugin', 'plugin.json'));
|
|
109
|
+
pluginVersion = pluginJson.version;
|
|
110
|
+
} catch (e) {
|
|
111
|
+
// Plugin json unreadable
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Get installed workflows from manifest
|
|
116
|
+
let installedWorkflows = [];
|
|
117
|
+
if (checks.hasManifest) {
|
|
118
|
+
try {
|
|
119
|
+
const manifest = await fs.readJson(path.join(projectRoot, '.myaidev-manifest.json'));
|
|
120
|
+
installedWorkflows = manifest.installedWorkflows || [];
|
|
121
|
+
} catch (e) {
|
|
122
|
+
// Manifest unreadable
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
installationType,
|
|
128
|
+
hasLegacy,
|
|
129
|
+
hasPlugin,
|
|
130
|
+
canUpgrade: hasLegacy && !hasPlugin,
|
|
131
|
+
canCoexist: true, // Both installation methods can coexist
|
|
132
|
+
checks,
|
|
133
|
+
versions: {
|
|
134
|
+
legacy: legacyVersion,
|
|
135
|
+
plugin: pluginVersion
|
|
136
|
+
},
|
|
137
|
+
installedWorkflows,
|
|
138
|
+
recommendations: getRecommendations(installationType, checks)
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get recommendations based on installation state
|
|
144
|
+
*
|
|
145
|
+
* @param {string} installationType - Current installation type
|
|
146
|
+
* @param {Object} checks - Detection checks
|
|
147
|
+
* @returns {string[]} Array of recommendation strings
|
|
148
|
+
*/
|
|
149
|
+
function getRecommendations(installationType, checks) {
|
|
150
|
+
const recommendations = [];
|
|
151
|
+
|
|
152
|
+
switch (installationType) {
|
|
153
|
+
case InstallationType.NONE:
|
|
154
|
+
recommendations.push('Run "npx myaidev-method init --claude" to install via npx');
|
|
155
|
+
recommendations.push('Or use "/plugin install myaidev-method" for plugin installation');
|
|
156
|
+
break;
|
|
157
|
+
|
|
158
|
+
case InstallationType.LEGACY:
|
|
159
|
+
recommendations.push('Legacy installation detected - all existing commands will continue to work');
|
|
160
|
+
recommendations.push('Consider upgrading to plugin installation for automatic updates');
|
|
161
|
+
recommendations.push('Use "/plugin install myaidev-method" to add plugin capabilities');
|
|
162
|
+
break;
|
|
163
|
+
|
|
164
|
+
case InstallationType.PLUGIN:
|
|
165
|
+
recommendations.push('Plugin installation detected - skills are auto-discovered');
|
|
166
|
+
recommendations.push('Use "/myaidev-method:*" commands or legacy "/myai-*" aliases');
|
|
167
|
+
break;
|
|
168
|
+
|
|
169
|
+
case InstallationType.BOTH:
|
|
170
|
+
recommendations.push('Both legacy and plugin installations detected - all features available');
|
|
171
|
+
recommendations.push('You can use both "/myai-*" and "/myaidev-method:*" command formats');
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return recommendations;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get a human-readable status message for the installation
|
|
180
|
+
*
|
|
181
|
+
* @param {string} projectRoot - The project root directory
|
|
182
|
+
* @returns {Promise<string>} Status message
|
|
183
|
+
*/
|
|
184
|
+
export async function getInstallationStatus(projectRoot = process.cwd()) {
|
|
185
|
+
const detection = await detectInstallation(projectRoot);
|
|
186
|
+
|
|
187
|
+
let status = '\n📦 MyAIDev Method Installation Status\n';
|
|
188
|
+
status += '════════════════════════════════════════\n\n';
|
|
189
|
+
|
|
190
|
+
switch (detection.installationType) {
|
|
191
|
+
case InstallationType.NONE:
|
|
192
|
+
status += '❌ Not Installed\n\n';
|
|
193
|
+
break;
|
|
194
|
+
case InstallationType.LEGACY:
|
|
195
|
+
status += '✅ Legacy Installation (npx)\n';
|
|
196
|
+
if (detection.versions.legacy) {
|
|
197
|
+
status += ` Version: ${detection.versions.legacy}\n`;
|
|
198
|
+
}
|
|
199
|
+
if (detection.installedWorkflows.length > 0) {
|
|
200
|
+
status += ` Workflows: ${detection.installedWorkflows.join(', ')}\n`;
|
|
201
|
+
}
|
|
202
|
+
status += '\n';
|
|
203
|
+
break;
|
|
204
|
+
case InstallationType.PLUGIN:
|
|
205
|
+
status += '✅ Plugin Installation\n';
|
|
206
|
+
if (detection.versions.plugin) {
|
|
207
|
+
status += ` Version: ${detection.versions.plugin}\n`;
|
|
208
|
+
}
|
|
209
|
+
status += '\n';
|
|
210
|
+
break;
|
|
211
|
+
case InstallationType.BOTH:
|
|
212
|
+
status += '✅ Dual Installation (Legacy + Plugin)\n';
|
|
213
|
+
if (detection.versions.legacy) {
|
|
214
|
+
status += ` Legacy Version: ${detection.versions.legacy}\n`;
|
|
215
|
+
}
|
|
216
|
+
if (detection.versions.plugin) {
|
|
217
|
+
status += ` Plugin Version: ${detection.versions.plugin}\n`;
|
|
218
|
+
}
|
|
219
|
+
if (detection.installedWorkflows.length > 0) {
|
|
220
|
+
status += ` Workflows: ${detection.installedWorkflows.join(', ')}\n`;
|
|
221
|
+
}
|
|
222
|
+
status += '\n';
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
status += '📋 Recommendations:\n';
|
|
227
|
+
for (const rec of detection.recommendations) {
|
|
228
|
+
status += ` • ${rec}\n`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return status;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Check if upgrade from legacy to plugin is available
|
|
236
|
+
*
|
|
237
|
+
* @param {string} projectRoot - The project root directory
|
|
238
|
+
* @returns {Promise<Object>} Upgrade availability info
|
|
239
|
+
*/
|
|
240
|
+
export async function checkUpgradeAvailability(projectRoot = process.cwd()) {
|
|
241
|
+
const detection = await detectInstallation(projectRoot);
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
canUpgrade: detection.canUpgrade,
|
|
245
|
+
currentType: detection.installationType,
|
|
246
|
+
upgradeBenefits: [
|
|
247
|
+
'Automatic skill discovery',
|
|
248
|
+
'Namespaced commands (/myaidev-method:*)',
|
|
249
|
+
'Plugin marketplace updates',
|
|
250
|
+
'Cross-platform support preparation'
|
|
251
|
+
],
|
|
252
|
+
preservedFeatures: [
|
|
253
|
+
'All existing /myai-* commands',
|
|
254
|
+
'Installed workflows and agents',
|
|
255
|
+
'Configuration and environment variables',
|
|
256
|
+
'.myaidev-manifest.json tracking'
|
|
257
|
+
]
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export default {
|
|
262
|
+
detectInstallation,
|
|
263
|
+
getInstallationStatus,
|
|
264
|
+
checkUpgradeAvailability,
|
|
265
|
+
InstallationType
|
|
266
|
+
};
|
|
@@ -74,7 +74,7 @@ export function validateVisualConfig() {
|
|
|
74
74
|
validation.errors.push("FAL_KEY appears to be invalid (too short)");
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
validation.hasAnyAPIKeys = validation.hasGoogle || validation.hasOpenAI;
|
|
77
|
+
validation.hasAnyAPIKeys = validation.hasGoogle || validation.hasOpenAI || validation.hasFal;
|
|
78
78
|
|
|
79
79
|
// Validate default service
|
|
80
80
|
if (
|
|
@@ -42,13 +42,16 @@ const OPENAI_IMAGE_MODELS = {
|
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
// FLUX 2 Models (via Fal.ai or BFL API)
|
|
45
|
-
const FLUX2_MODELS = {
|
|
46
|
-
"flux2-pro": "fal-ai/flux-2
|
|
47
|
-
"flux2-flex": "fal-ai/flux-2
|
|
48
|
-
"flux2-dev": "fal-ai/flux-2
|
|
45
|
+
export const FLUX2_MODELS = {
|
|
46
|
+
"flux2-pro": "fal-ai/flux-2-pro", // State-of-the-art quality, fastest, lowest cost
|
|
47
|
+
"flux2-flex": "fal-ai/flux-2-flex", // Developer-controlled parameters
|
|
48
|
+
"flux2-dev": "fal-ai/flux-2-dev", // 32B open-weight model
|
|
49
49
|
// Legacy FLUX 1.x models (still available)
|
|
50
50
|
"flux-pro": "fal-ai/flux-pro/v1.1-ultra",
|
|
51
51
|
"flux-dev": "fal-ai/flux/dev",
|
|
52
|
+
|
|
53
|
+
// nano banana models
|
|
54
|
+
"nano-banana-pro": "fal-ai/nano-banana-pro",
|
|
52
55
|
};
|
|
53
56
|
|
|
54
57
|
// Pricing (USD per image/video)
|
|
@@ -100,6 +103,7 @@ export function validateAPIKeys() {
|
|
|
100
103
|
}
|
|
101
104
|
if (hasFal || hasBFL) {
|
|
102
105
|
availableServices.push(
|
|
106
|
+
"nano-banana-pro",
|
|
103
107
|
"flux2-pro",
|
|
104
108
|
"flux2-flex",
|
|
105
109
|
"flux2-dev",
|
|
@@ -165,6 +169,9 @@ export function estimateCost(service, options = {}) {
|
|
|
165
169
|
case "flux-dev":
|
|
166
170
|
return PRICING.flux_dev;
|
|
167
171
|
|
|
172
|
+
case "nano-banana-pro":
|
|
173
|
+
return PRICING.nano_banana_pro;
|
|
174
|
+
|
|
168
175
|
case "veo3":
|
|
169
176
|
case "veo3-fast":
|
|
170
177
|
return PRICING.veo3; // per second, will multiply by duration
|
|
@@ -642,7 +649,7 @@ export async function generateImageFlux2(prompt, options = {}) {
|
|
|
642
649
|
mimeType: contentType,
|
|
643
650
|
service: "flux2",
|
|
644
651
|
model: model,
|
|
645
|
-
cost: PRICING[model.
|
|
652
|
+
cost: PRICING[model.replaceAll("-", "_")] || PRICING.flux2_pro,
|
|
646
653
|
};
|
|
647
654
|
}
|
|
648
655
|
|
|
@@ -669,13 +676,17 @@ export async function generateImageFlux2(prompt, options = {}) {
|
|
|
669
676
|
*
|
|
670
677
|
* @param {string} prompt - Image description
|
|
671
678
|
* @param {Object} options - Generation options
|
|
672
|
-
* @param {string} options.model - FLUX model (flux-pro, flux-dev)
|
|
679
|
+
* @param {string} options.model - FLUX model (nano-banana-pro, flux-pro, flux-dev)
|
|
673
680
|
* @param {string} options.size - Image size
|
|
674
681
|
* @param {number} options.maxRetries - Maximum retry attempts
|
|
675
682
|
* @returns {Promise<Object>} Generated image data
|
|
676
683
|
*/
|
|
677
684
|
export async function generateImageFal(prompt, options = {}) {
|
|
678
|
-
const {
|
|
685
|
+
const {
|
|
686
|
+
model = "nano-banana-pro",
|
|
687
|
+
size = "1024x1024",
|
|
688
|
+
maxRetries = 3,
|
|
689
|
+
} = options;
|
|
679
690
|
|
|
680
691
|
const apiKey = process.env.FAL_KEY;
|
|
681
692
|
if (!apiKey) {
|
|
@@ -724,7 +735,7 @@ export async function generateImageFal(prompt, options = {}) {
|
|
|
724
735
|
mimeType: contentType,
|
|
725
736
|
service: "fal",
|
|
726
737
|
model: model,
|
|
727
|
-
cost: PRICING[model.
|
|
738
|
+
cost: PRICING[model.replaceAll("-", "_")] || PRICING.flux_pro,
|
|
728
739
|
};
|
|
729
740
|
}
|
|
730
741
|
|
|
@@ -852,12 +863,21 @@ export async function downloadImage(url) {
|
|
|
852
863
|
export async function generateImage(prompt, options = {}) {
|
|
853
864
|
const { preferredService, type = "general", ...serviceOptions } = options;
|
|
854
865
|
|
|
855
|
-
//
|
|
856
|
-
|
|
857
|
-
|
|
866
|
+
// If a model is explicitly specified, use it as the service
|
|
867
|
+
// This allows --service=fal --model=nano-banana-pro to work correctly
|
|
868
|
+
let service;
|
|
869
|
+
if (serviceOptions.model && FLUX2_MODELS[serviceOptions.model]) {
|
|
870
|
+
service = serviceOptions.model;
|
|
871
|
+
} else {
|
|
872
|
+
// Select service normally
|
|
873
|
+
const defaultService = process.env.VISUAL_DEFAULT_SERVICE || "gemini";
|
|
874
|
+
service = selectBestService(preferredService || defaultService);
|
|
875
|
+
}
|
|
858
876
|
|
|
859
|
-
const modelInfo = serviceOptions.model
|
|
860
|
-
|
|
877
|
+
const modelInfo = serviceOptions.model
|
|
878
|
+
? ` ${serviceOptions.model}`
|
|
879
|
+
: ` ${service}`;
|
|
880
|
+
console.log(`🎨 Generating ${type} image using${modelInfo}...`);
|
|
861
881
|
|
|
862
882
|
// Enhance prompt based on image type
|
|
863
883
|
const enhancedPrompt = enhancePrompt(prompt, type);
|
|
@@ -898,7 +918,7 @@ export async function generateImage(prompt, options = {}) {
|
|
|
898
918
|
case "nano-banana-pro":
|
|
899
919
|
result = await generateImageFal(enhancedPrompt, {
|
|
900
920
|
...serviceOptions,
|
|
901
|
-
model: serviceOptions.model || service
|
|
921
|
+
model: serviceOptions.model || service,
|
|
902
922
|
});
|
|
903
923
|
break;
|
|
904
924
|
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
16
|
generateImage,
|
|
17
|
-
|
|
17
|
+
generateVideo,
|
|
18
18
|
estimateCost,
|
|
19
19
|
validateAPIKeys,
|
|
20
|
+
FLUX2_MODELS,
|
|
20
21
|
} from "../lib/visual-generation-utils.js";
|
|
21
22
|
import {
|
|
22
23
|
saveImage,
|
|
@@ -57,6 +58,7 @@ if (!prompt) {
|
|
|
57
58
|
const options = {
|
|
58
59
|
type: getOption("type", "hero"),
|
|
59
60
|
service: getOption("service", null),
|
|
61
|
+
model: getOption("model", null),
|
|
60
62
|
size: getOption("size", "1024x1024"),
|
|
61
63
|
quality: getOption("quality", "standard"),
|
|
62
64
|
description: getOption("description", null),
|
|
@@ -96,11 +98,24 @@ const options = {
|
|
|
96
98
|
|
|
97
99
|
// Estimate cost
|
|
98
100
|
let service = options.service;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
service =
|
|
103
|
-
console.log(chalk.gray(`
|
|
101
|
+
|
|
102
|
+
// Video generation always uses veo3, ignore model/service for cost estimation
|
|
103
|
+
if (options.type === "video") {
|
|
104
|
+
service = "veo3";
|
|
105
|
+
console.log(chalk.gray(`Video generation uses: veo3`));
|
|
106
|
+
} else {
|
|
107
|
+
// For images: if model is specified, it takes precedence over service
|
|
108
|
+
if (options.model && FLUX2_MODELS[options.model]) {
|
|
109
|
+
service = options.model;
|
|
110
|
+
console.log(chalk.gray(`Using model: ${options.model}`));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!service) {
|
|
114
|
+
// Auto-select based on available services
|
|
115
|
+
const validation = validateAPIKeys();
|
|
116
|
+
service = validation.availableServices[0];
|
|
117
|
+
console.log(chalk.gray(`Auto-selected service: ${service}`));
|
|
118
|
+
}
|
|
104
119
|
}
|
|
105
120
|
|
|
106
121
|
const cost = estimateCost(service, {
|
|
@@ -108,13 +123,20 @@ const options = {
|
|
|
108
123
|
size: options.size,
|
|
109
124
|
});
|
|
110
125
|
|
|
126
|
+
// For video, multiply by duration
|
|
127
|
+
const videoDuration = 5; // Default duration
|
|
128
|
+
const finalCost = options.type === "video" ? cost * videoDuration : cost;
|
|
129
|
+
|
|
111
130
|
// Check budget
|
|
112
|
-
const budget = await checkBudgetLimit(
|
|
131
|
+
const budget = await checkBudgetLimit(finalCost);
|
|
113
132
|
const todayCost = await getTodaysCost();
|
|
114
133
|
const monthCost = await getMonthCost();
|
|
115
134
|
|
|
116
135
|
console.log(chalk.cyan("💰 Cost Estimate:"));
|
|
117
|
-
console.log(chalk.gray(` This generation: $${
|
|
136
|
+
console.log(chalk.gray(` This generation: $${finalCost.toFixed(2)}`));
|
|
137
|
+
if (options.type === "video") {
|
|
138
|
+
console.log(chalk.gray(` (${videoDuration}s × $${cost.toFixed(2)}/s)`));
|
|
139
|
+
}
|
|
118
140
|
console.log(
|
|
119
141
|
chalk.gray(
|
|
120
142
|
` Today: $${todayCost.toFixed(2)} / $${budget.dailyBudget.toFixed(2)} (${Math.round((todayCost / budget.dailyBudget) * 100)}%)`,
|
|
@@ -177,7 +199,7 @@ const options = {
|
|
|
177
199
|
if (options.type === "video") {
|
|
178
200
|
// Generate video
|
|
179
201
|
spinner.text = `Generating video using ${service}...`;
|
|
180
|
-
result = await
|
|
202
|
+
result = await generateVideo(prompt, {
|
|
181
203
|
duration: 5,
|
|
182
204
|
aspectRatio: "16:9",
|
|
183
205
|
});
|
|
@@ -198,6 +220,7 @@ const options = {
|
|
|
198
220
|
spinner.text = `Generating ${options.type} using ${service}...`;
|
|
199
221
|
result = await generateImage(prompt, {
|
|
200
222
|
preferredService: service,
|
|
223
|
+
model: options.model,
|
|
201
224
|
type: options.type,
|
|
202
225
|
quality: options.quality,
|
|
203
226
|
size: options.size,
|
|
@@ -330,9 +353,15 @@ function showHelp() {
|
|
|
330
353
|
console.log(
|
|
331
354
|
chalk.gray(" --service=<service> AI service (default: auto-select)"),
|
|
332
355
|
);
|
|
356
|
+
console.log(
|
|
357
|
+
chalk.gray(" Values: gemini, imagen, dalle, veo"),
|
|
358
|
+
);
|
|
359
|
+
console.log(
|
|
360
|
+
chalk.gray(" --model=<model> Specific model (for fal service)"),
|
|
361
|
+
);
|
|
333
362
|
console.log(
|
|
334
363
|
chalk.gray(
|
|
335
|
-
" Values:
|
|
364
|
+
" Values: nano-banana-pro, flux-pro, flux-dev",
|
|
336
365
|
),
|
|
337
366
|
);
|
|
338
367
|
console.log(
|
package/src/scripts/ping.js
CHANGED