@djangocfg/imgai 2.1.42 → 2.1.43
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/dist/cli/bin.cjs +272 -272
- package/dist/cli/bin.cjs.map +1 -1
- package/dist/cli/bin.js +267 -267
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/index.cjs +264 -262
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +261 -259
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/bin.cjs
CHANGED
|
@@ -1,275 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
var chalk = require('chalk');
|
|
4
5
|
var commander = require('commander');
|
|
5
|
-
var path2 = require('path');
|
|
6
|
-
var url = require('url');
|
|
7
6
|
var dotenv = require('dotenv');
|
|
8
|
-
var chalk = require('chalk');
|
|
9
7
|
var ora = require('ora');
|
|
10
|
-
var
|
|
11
|
-
var
|
|
12
|
-
var
|
|
8
|
+
var path = require('path');
|
|
9
|
+
var url = require('url');
|
|
10
|
+
var fs3 = require('fs-extra');
|
|
11
|
+
var glob = require('glob');
|
|
12
|
+
var sharp2 = require('sharp');
|
|
13
13
|
var OpenAI = require('openai');
|
|
14
14
|
var Anthropic = require('@anthropic-ai/sdk');
|
|
15
|
-
var
|
|
15
|
+
var inquirer = require('inquirer');
|
|
16
16
|
|
|
17
17
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
18
18
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
19
19
|
|
|
20
|
-
var path2__default = /*#__PURE__*/_interopDefault(path2);
|
|
21
|
-
var dotenv__default = /*#__PURE__*/_interopDefault(dotenv);
|
|
22
20
|
var chalk__default = /*#__PURE__*/_interopDefault(chalk);
|
|
21
|
+
var dotenv__default = /*#__PURE__*/_interopDefault(dotenv);
|
|
23
22
|
var ora__default = /*#__PURE__*/_interopDefault(ora);
|
|
24
|
-
var
|
|
25
|
-
var
|
|
26
|
-
var
|
|
23
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
24
|
+
var fs3__default = /*#__PURE__*/_interopDefault(fs3);
|
|
25
|
+
var sharp2__default = /*#__PURE__*/_interopDefault(sharp2);
|
|
27
26
|
var OpenAI__default = /*#__PURE__*/_interopDefault(OpenAI);
|
|
28
27
|
var Anthropic__default = /*#__PURE__*/_interopDefault(Anthropic);
|
|
28
|
+
var inquirer__default = /*#__PURE__*/_interopDefault(inquirer);
|
|
29
29
|
|
|
30
|
-
var ImageGenerator = class {
|
|
31
|
-
config;
|
|
32
|
-
openai;
|
|
33
|
-
anthropic;
|
|
34
|
-
constructor(config) {
|
|
35
|
-
this.config = config;
|
|
36
|
-
this.initializeProviders();
|
|
37
|
-
}
|
|
38
|
-
initializeProviders() {
|
|
39
|
-
const openaiKey = this.config.openaiApiKey || process.env.OPENAI_API_KEY;
|
|
40
|
-
const anthropicKey = this.config.anthropicApiKey || process.env.ANTHROPIC_API_KEY;
|
|
41
|
-
if (openaiKey) {
|
|
42
|
-
this.openai = new OpenAI__default.default({ apiKey: openaiKey });
|
|
43
|
-
}
|
|
44
|
-
if (anthropicKey) {
|
|
45
|
-
this.anthropic = new Anthropic__default.default({ apiKey: anthropicKey });
|
|
46
|
-
}
|
|
47
|
-
if (!this.openai && this.config.provider === "openai") {
|
|
48
|
-
throw new Error("OpenAI API key required. Set OPENAI_API_KEY or pass openaiApiKey in config.");
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
52
|
-
// SINGLE IMAGE GENERATION
|
|
53
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
54
|
-
async generate(options) {
|
|
55
|
-
const startTime = Date.now();
|
|
56
|
-
try {
|
|
57
|
-
const fullPrompt = this.buildPrompt(options.prompt);
|
|
58
|
-
const imageData = await this.generateWithOpenAI(fullPrompt);
|
|
59
|
-
const filename = options.filename || this.generateFilename(options.prompt);
|
|
60
|
-
const category = options.category || "general";
|
|
61
|
-
const outputDir = path2__default.default.join(
|
|
62
|
-
this.config.projectRoot,
|
|
63
|
-
this.config.outputDir,
|
|
64
|
-
category
|
|
65
|
-
);
|
|
66
|
-
await fs__default.default.ensureDir(outputDir);
|
|
67
|
-
const originalPath = path2__default.default.join(outputDir, `${filename}.png`);
|
|
68
|
-
await fs__default.default.writeFile(originalPath, Buffer.from(imageData, "base64"));
|
|
69
|
-
const resizeOptions = options.resize || this.config.resize;
|
|
70
|
-
let finalPath = originalPath;
|
|
71
|
-
if (resizeOptions) {
|
|
72
|
-
finalPath = await this.resizeImage(originalPath, resizeOptions);
|
|
73
|
-
if (finalPath !== originalPath) {
|
|
74
|
-
await fs__default.default.remove(originalPath);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
const imageInfo = await this.getImageInfo(finalPath, category);
|
|
78
|
-
imageInfo.metadata = {
|
|
79
|
-
prompt: options.prompt,
|
|
80
|
-
caption: options.metadata?.caption,
|
|
81
|
-
tags: options.tags,
|
|
82
|
-
category,
|
|
83
|
-
generatedAt: /* @__PURE__ */ new Date(),
|
|
84
|
-
model: "dall-e-3"
|
|
85
|
-
};
|
|
86
|
-
const duration = Date.now() - startTime;
|
|
87
|
-
return {
|
|
88
|
-
success: true,
|
|
89
|
-
imagePath: finalPath,
|
|
90
|
-
imageUrl: imageInfo.url,
|
|
91
|
-
imageInfo,
|
|
92
|
-
duration
|
|
93
|
-
};
|
|
94
|
-
} catch (error) {
|
|
95
|
-
return {
|
|
96
|
-
success: false,
|
|
97
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
98
|
-
duration: Date.now() - startTime
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
103
|
-
// BATCH GENERATION
|
|
104
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
105
|
-
async generateBatch(options) {
|
|
106
|
-
const startTime = Date.now();
|
|
107
|
-
const results = [];
|
|
108
|
-
const concurrency = options.concurrency || 2;
|
|
109
|
-
const delayBetween = options.delayBetween || 2e3;
|
|
110
|
-
for (let i = 0; i < options.items.length; i += concurrency) {
|
|
111
|
-
const batch = options.items.slice(i, i + concurrency);
|
|
112
|
-
const batchPromises = batch.map(async (item) => {
|
|
113
|
-
const result = await this.generate({
|
|
114
|
-
prompt: item.prompt,
|
|
115
|
-
filename: item.filename,
|
|
116
|
-
category: item.category
|
|
117
|
-
});
|
|
118
|
-
return result;
|
|
119
|
-
});
|
|
120
|
-
const batchResults = await Promise.all(batchPromises);
|
|
121
|
-
for (const result of batchResults) {
|
|
122
|
-
results.push(result);
|
|
123
|
-
if (options.onProgress) {
|
|
124
|
-
options.onProgress(results.length, options.items.length, result);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if (i + concurrency < options.items.length) {
|
|
128
|
-
await this.delay(delayBetween);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return {
|
|
132
|
-
total: results.length,
|
|
133
|
-
success: results.filter((r) => r.success).length,
|
|
134
|
-
failed: results.filter((r) => !r.success).length,
|
|
135
|
-
results,
|
|
136
|
-
duration: Date.now() - startTime
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
140
|
-
// PROMPT ENHANCEMENT (using Claude/GPT)
|
|
141
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
142
|
-
async enhancePrompt(basePrompt, context) {
|
|
143
|
-
const systemPrompt = `You are an expert at creating image generation prompts.
|
|
144
|
-
Given a description, create:
|
|
145
|
-
1. A detailed visual prompt (max 150 words) for DALL-E 3
|
|
146
|
-
2. A descriptive filename (lowercase, hyphens, max 30 chars)
|
|
147
|
-
3. A short caption (max 50 words)
|
|
148
|
-
|
|
149
|
-
${this.config.prefix ? `Style prefix: ${this.config.prefix}` : ""}
|
|
150
|
-
${this.config.suffix ? `Style suffix: ${this.config.suffix}` : ""}
|
|
151
|
-
|
|
152
|
-
Return ONLY valid JSON: {"prompt": "...", "filename": "...", "caption": "..."}`;
|
|
153
|
-
const userPrompt = context ? `Description: ${basePrompt}
|
|
154
|
-
Context: ${context}` : `Description: ${basePrompt}`;
|
|
155
|
-
if (this.anthropic && this.config.provider === "anthropic") {
|
|
156
|
-
const response = await this.anthropic.messages.create({
|
|
157
|
-
model: "claude-3-5-sonnet-20241022",
|
|
158
|
-
max_tokens: 500,
|
|
159
|
-
messages: [
|
|
160
|
-
{ role: "user", content: `${systemPrompt}
|
|
161
|
-
|
|
162
|
-
${userPrompt}` }
|
|
163
|
-
]
|
|
164
|
-
});
|
|
165
|
-
const text = response.content[0].type === "text" ? response.content[0].text : "";
|
|
166
|
-
return JSON.parse(text);
|
|
167
|
-
}
|
|
168
|
-
if (this.openai) {
|
|
169
|
-
const response = await this.openai.chat.completions.create({
|
|
170
|
-
model: "gpt-4-turbo-preview",
|
|
171
|
-
messages: [
|
|
172
|
-
{ role: "system", content: systemPrompt },
|
|
173
|
-
{ role: "user", content: userPrompt }
|
|
174
|
-
],
|
|
175
|
-
max_tokens: 500,
|
|
176
|
-
temperature: 0.7,
|
|
177
|
-
response_format: { type: "json_object" }
|
|
178
|
-
});
|
|
179
|
-
const text = response.choices[0]?.message?.content || "{}";
|
|
180
|
-
return JSON.parse(text);
|
|
181
|
-
}
|
|
182
|
-
throw new Error("No AI provider configured for prompt enhancement");
|
|
183
|
-
}
|
|
184
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
185
|
-
// PRIVATE METHODS
|
|
186
|
-
// ──────────────────────────────────────────────────────────────────────────
|
|
187
|
-
async generateWithOpenAI(prompt) {
|
|
188
|
-
if (!this.openai) {
|
|
189
|
-
throw new Error("OpenAI client not initialized");
|
|
190
|
-
}
|
|
191
|
-
const response = await this.openai.images.generate({
|
|
192
|
-
model: "dall-e-3",
|
|
193
|
-
prompt,
|
|
194
|
-
n: 1,
|
|
195
|
-
size: this.config.size,
|
|
196
|
-
quality: this.config.quality,
|
|
197
|
-
style: this.config.dalleStyle || "natural",
|
|
198
|
-
response_format: "b64_json"
|
|
199
|
-
});
|
|
200
|
-
const imageData = response.data?.[0]?.b64_json;
|
|
201
|
-
if (!imageData) {
|
|
202
|
-
throw new Error("No image data received from OpenAI");
|
|
203
|
-
}
|
|
204
|
-
return imageData;
|
|
205
|
-
}
|
|
206
|
-
async resizeImage(inputPath, options) {
|
|
207
|
-
const { width, height, quality = 85, format = "webp", fit = "inside" } = options;
|
|
208
|
-
let pipeline = sharp__default.default(inputPath);
|
|
209
|
-
if (width || height) {
|
|
210
|
-
pipeline = pipeline.resize(width, height, {
|
|
211
|
-
fit,
|
|
212
|
-
withoutEnlargement: true
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
const outputPath = inputPath.replace(/\.[^.]+$/, `.${format}`);
|
|
216
|
-
switch (format) {
|
|
217
|
-
case "webp":
|
|
218
|
-
pipeline = pipeline.webp({ quality });
|
|
219
|
-
break;
|
|
220
|
-
case "jpeg":
|
|
221
|
-
pipeline = pipeline.jpeg({ quality });
|
|
222
|
-
break;
|
|
223
|
-
case "png":
|
|
224
|
-
pipeline = pipeline.png({ quality });
|
|
225
|
-
break;
|
|
226
|
-
case "avif":
|
|
227
|
-
pipeline = pipeline.avif({ quality });
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
230
|
-
await pipeline.toFile(outputPath);
|
|
231
|
-
return outputPath;
|
|
232
|
-
}
|
|
233
|
-
async getImageInfo(imagePath, category) {
|
|
234
|
-
const stats = await fs__default.default.stat(imagePath);
|
|
235
|
-
const metadata = await sharp__default.default(imagePath).metadata();
|
|
236
|
-
const filename = path2__default.default.basename(imagePath);
|
|
237
|
-
const extension = path2__default.default.extname(filename).slice(1);
|
|
238
|
-
const id = path2__default.default.basename(filename, path2__default.default.extname(filename));
|
|
239
|
-
const publicDir = path2__default.default.join(this.config.projectRoot, this.config.publicDir);
|
|
240
|
-
const relativePath = path2__default.default.relative(publicDir, imagePath);
|
|
241
|
-
const url = "/" + relativePath.replace(/\\/g, "/");
|
|
242
|
-
return {
|
|
243
|
-
id,
|
|
244
|
-
filename,
|
|
245
|
-
extension,
|
|
246
|
-
path: relativePath,
|
|
247
|
-
url,
|
|
248
|
-
size: stats.size,
|
|
249
|
-
width: metadata.width,
|
|
250
|
-
height: metadata.height,
|
|
251
|
-
createdAt: stats.birthtime,
|
|
252
|
-
modifiedAt: stats.mtime
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
buildPrompt(basePrompt) {
|
|
256
|
-
const parts = [];
|
|
257
|
-
if (this.config.prefix) {
|
|
258
|
-
parts.push(this.config.prefix);
|
|
259
|
-
}
|
|
260
|
-
parts.push(basePrompt);
|
|
261
|
-
if (this.config.suffix) {
|
|
262
|
-
parts.push(this.config.suffix);
|
|
263
|
-
}
|
|
264
|
-
return parts.join(" ");
|
|
265
|
-
}
|
|
266
|
-
generateFilename(prompt) {
|
|
267
|
-
return prompt.toLowerCase().replace(/[^a-z0-9\s]/g, "").replace(/\s+/g, "-").substring(0, 30);
|
|
268
|
-
}
|
|
269
|
-
delay(ms) {
|
|
270
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
30
|
var DEFAULT_EXTENSIONS = ["png", "jpg", "jpeg", "webp", "avif", "gif", "svg"];
|
|
274
31
|
var DEFAULT_DIRECTORIES = ["static/images", "images", "assets"];
|
|
275
32
|
var ImageScanner = class {
|
|
@@ -288,15 +45,15 @@ var ImageScanner = class {
|
|
|
288
45
|
includeDimensions = true,
|
|
289
46
|
recursive = true
|
|
290
47
|
} = options;
|
|
291
|
-
const publicDir =
|
|
48
|
+
const publicDir = path__default.default.join(this.config.projectRoot, this.config.publicDir);
|
|
292
49
|
const images = [];
|
|
293
50
|
const byCategory = {};
|
|
294
51
|
const byExtension = {};
|
|
295
52
|
let totalSize = 0;
|
|
296
53
|
const patterns = directories.flatMap((dir) => {
|
|
297
|
-
const basePath =
|
|
54
|
+
const basePath = path__default.default.join(publicDir, dir);
|
|
298
55
|
const extPattern = `*.{${extensions.join(",")}}`;
|
|
299
|
-
return recursive ? [
|
|
56
|
+
return recursive ? [path__default.default.join(basePath, "**", extPattern)] : [path__default.default.join(basePath, extPattern)];
|
|
300
57
|
});
|
|
301
58
|
for (const pattern of patterns) {
|
|
302
59
|
const files = await glob.glob(pattern, { nodir: true });
|
|
@@ -363,17 +120,17 @@ var ImageScanner = class {
|
|
|
363
120
|
// PRIVATE METHODS
|
|
364
121
|
// ──────────────────────────────────────────────────────────────────────────
|
|
365
122
|
async processImage(filePath, publicDir, includeDimensions) {
|
|
366
|
-
const stats = await
|
|
367
|
-
const filename =
|
|
368
|
-
const extension =
|
|
369
|
-
const id =
|
|
370
|
-
const relativePath =
|
|
123
|
+
const stats = await fs3__default.default.stat(filePath);
|
|
124
|
+
const filename = path__default.default.basename(filePath);
|
|
125
|
+
const extension = path__default.default.extname(filename).slice(1).toLowerCase();
|
|
126
|
+
const id = path__default.default.basename(filename, path__default.default.extname(filename));
|
|
127
|
+
const relativePath = path__default.default.relative(publicDir, filePath).replace(/\\/g, "/");
|
|
371
128
|
const url = "/" + relativePath;
|
|
372
129
|
let width;
|
|
373
130
|
let height;
|
|
374
131
|
if (includeDimensions && extension !== "svg") {
|
|
375
132
|
try {
|
|
376
|
-
const metadata = await
|
|
133
|
+
const metadata = await sharp2__default.default(filePath).metadata();
|
|
377
134
|
width = metadata.width;
|
|
378
135
|
height = metadata.height;
|
|
379
136
|
} catch {
|
|
@@ -411,6 +168,8 @@ function formatDuration(ms) {
|
|
|
411
168
|
if (ms < 1e3) return `${ms}ms`;
|
|
412
169
|
return `${(ms / 1e3).toFixed(2)}s`;
|
|
413
170
|
}
|
|
171
|
+
|
|
172
|
+
// src/config/generator.ts
|
|
414
173
|
var ConfigGenerator = class {
|
|
415
174
|
config;
|
|
416
175
|
scanner;
|
|
@@ -423,10 +182,10 @@ var ConfigGenerator = class {
|
|
|
423
182
|
// ──────────────────────────────────────────────────────────────────────────
|
|
424
183
|
async generate() {
|
|
425
184
|
const catalog = await this.scanner.buildCatalog();
|
|
426
|
-
const outputPath =
|
|
427
|
-
await
|
|
185
|
+
const outputPath = path__default.default.join(this.config.projectRoot, this.config.configOutputPath);
|
|
186
|
+
await fs3__default.default.ensureDir(path__default.default.dirname(outputPath));
|
|
428
187
|
const content = this.generateTypeScript(catalog);
|
|
429
|
-
await
|
|
188
|
+
await fs3__default.default.writeFile(outputPath, content, "utf-8");
|
|
430
189
|
return outputPath;
|
|
431
190
|
}
|
|
432
191
|
// ──────────────────────────────────────────────────────────────────────────
|
|
@@ -542,8 +301,249 @@ export const IMAGE_CATALOG_META = {
|
|
|
542
301
|
return ` '${category}': [${imageRefs.join(", ")}]`;
|
|
543
302
|
}
|
|
544
303
|
};
|
|
304
|
+
var ImageGenerator = class {
|
|
305
|
+
config;
|
|
306
|
+
openai;
|
|
307
|
+
anthropic;
|
|
308
|
+
constructor(config) {
|
|
309
|
+
this.config = config;
|
|
310
|
+
this.initializeProviders();
|
|
311
|
+
}
|
|
312
|
+
initializeProviders() {
|
|
313
|
+
const openaiKey = this.config.openaiApiKey || process.env.OPENAI_API_KEY;
|
|
314
|
+
const anthropicKey = this.config.anthropicApiKey || process.env.ANTHROPIC_API_KEY;
|
|
315
|
+
if (openaiKey) {
|
|
316
|
+
this.openai = new OpenAI__default.default({ apiKey: openaiKey });
|
|
317
|
+
}
|
|
318
|
+
if (anthropicKey) {
|
|
319
|
+
this.anthropic = new Anthropic__default.default({ apiKey: anthropicKey });
|
|
320
|
+
}
|
|
321
|
+
if (!this.openai && this.config.provider === "openai") {
|
|
322
|
+
throw new Error("OpenAI API key required. Set OPENAI_API_KEY or pass openaiApiKey in config.");
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
326
|
+
// SINGLE IMAGE GENERATION
|
|
327
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
328
|
+
async generate(options) {
|
|
329
|
+
const startTime = Date.now();
|
|
330
|
+
try {
|
|
331
|
+
const fullPrompt = this.buildPrompt(options.prompt);
|
|
332
|
+
const imageData = await this.generateWithOpenAI(fullPrompt);
|
|
333
|
+
const filename = options.filename || this.generateFilename(options.prompt);
|
|
334
|
+
const category = options.category || "general";
|
|
335
|
+
const outputDir = path__default.default.join(
|
|
336
|
+
this.config.projectRoot,
|
|
337
|
+
this.config.outputDir,
|
|
338
|
+
category
|
|
339
|
+
);
|
|
340
|
+
await fs3__default.default.ensureDir(outputDir);
|
|
341
|
+
const originalPath = path__default.default.join(outputDir, `${filename}.png`);
|
|
342
|
+
await fs3__default.default.writeFile(originalPath, Buffer.from(imageData, "base64"));
|
|
343
|
+
const resizeOptions = options.resize || this.config.resize;
|
|
344
|
+
let finalPath = originalPath;
|
|
345
|
+
if (resizeOptions) {
|
|
346
|
+
finalPath = await this.resizeImage(originalPath, resizeOptions);
|
|
347
|
+
if (finalPath !== originalPath) {
|
|
348
|
+
await fs3__default.default.remove(originalPath);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const imageInfo = await this.getImageInfo(finalPath, category);
|
|
352
|
+
imageInfo.metadata = {
|
|
353
|
+
prompt: options.prompt,
|
|
354
|
+
caption: options.metadata?.caption,
|
|
355
|
+
tags: options.tags,
|
|
356
|
+
category,
|
|
357
|
+
generatedAt: /* @__PURE__ */ new Date(),
|
|
358
|
+
model: "dall-e-3"
|
|
359
|
+
};
|
|
360
|
+
const duration = Date.now() - startTime;
|
|
361
|
+
return {
|
|
362
|
+
success: true,
|
|
363
|
+
imagePath: finalPath,
|
|
364
|
+
imageUrl: imageInfo.url,
|
|
365
|
+
imageInfo,
|
|
366
|
+
duration
|
|
367
|
+
};
|
|
368
|
+
} catch (error) {
|
|
369
|
+
return {
|
|
370
|
+
success: false,
|
|
371
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
372
|
+
duration: Date.now() - startTime
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
377
|
+
// BATCH GENERATION
|
|
378
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
379
|
+
async generateBatch(options) {
|
|
380
|
+
const startTime = Date.now();
|
|
381
|
+
const results = [];
|
|
382
|
+
const concurrency = options.concurrency || 2;
|
|
383
|
+
const delayBetween = options.delayBetween || 2e3;
|
|
384
|
+
for (let i = 0; i < options.items.length; i += concurrency) {
|
|
385
|
+
const batch = options.items.slice(i, i + concurrency);
|
|
386
|
+
const batchPromises = batch.map(async (item) => {
|
|
387
|
+
const result = await this.generate({
|
|
388
|
+
prompt: item.prompt,
|
|
389
|
+
filename: item.filename,
|
|
390
|
+
category: item.category
|
|
391
|
+
});
|
|
392
|
+
return result;
|
|
393
|
+
});
|
|
394
|
+
const batchResults = await Promise.all(batchPromises);
|
|
395
|
+
for (const result of batchResults) {
|
|
396
|
+
results.push(result);
|
|
397
|
+
if (options.onProgress) {
|
|
398
|
+
options.onProgress(results.length, options.items.length, result);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (i + concurrency < options.items.length) {
|
|
402
|
+
await this.delay(delayBetween);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
total: results.length,
|
|
407
|
+
success: results.filter((r) => r.success).length,
|
|
408
|
+
failed: results.filter((r) => !r.success).length,
|
|
409
|
+
results,
|
|
410
|
+
duration: Date.now() - startTime
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
414
|
+
// PROMPT ENHANCEMENT (using Claude/GPT)
|
|
415
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
416
|
+
async enhancePrompt(basePrompt, context) {
|
|
417
|
+
const systemPrompt = `You are an expert at creating image generation prompts.
|
|
418
|
+
Given a description, create:
|
|
419
|
+
1. A detailed visual prompt (max 150 words) for DALL-E 3
|
|
420
|
+
2. A descriptive filename (lowercase, hyphens, max 30 chars)
|
|
421
|
+
3. A short caption (max 50 words)
|
|
422
|
+
|
|
423
|
+
${this.config.prefix ? `Style prefix: ${this.config.prefix}` : ""}
|
|
424
|
+
${this.config.suffix ? `Style suffix: ${this.config.suffix}` : ""}
|
|
425
|
+
|
|
426
|
+
Return ONLY valid JSON: {"prompt": "...", "filename": "...", "caption": "..."}`;
|
|
427
|
+
const userPrompt = context ? `Description: ${basePrompt}
|
|
428
|
+
Context: ${context}` : `Description: ${basePrompt}`;
|
|
429
|
+
if (this.anthropic && this.config.provider === "anthropic") {
|
|
430
|
+
const response = await this.anthropic.messages.create({
|
|
431
|
+
model: "claude-3-5-sonnet-20241022",
|
|
432
|
+
max_tokens: 500,
|
|
433
|
+
messages: [
|
|
434
|
+
{ role: "user", content: `${systemPrompt}
|
|
545
435
|
|
|
546
|
-
|
|
436
|
+
${userPrompt}` }
|
|
437
|
+
]
|
|
438
|
+
});
|
|
439
|
+
const text = response.content[0].type === "text" ? response.content[0].text : "";
|
|
440
|
+
return JSON.parse(text);
|
|
441
|
+
}
|
|
442
|
+
if (this.openai) {
|
|
443
|
+
const response = await this.openai.chat.completions.create({
|
|
444
|
+
model: "gpt-4-turbo-preview",
|
|
445
|
+
messages: [
|
|
446
|
+
{ role: "system", content: systemPrompt },
|
|
447
|
+
{ role: "user", content: userPrompt }
|
|
448
|
+
],
|
|
449
|
+
max_tokens: 500,
|
|
450
|
+
temperature: 0.7,
|
|
451
|
+
response_format: { type: "json_object" }
|
|
452
|
+
});
|
|
453
|
+
const text = response.choices[0]?.message?.content || "{}";
|
|
454
|
+
return JSON.parse(text);
|
|
455
|
+
}
|
|
456
|
+
throw new Error("No AI provider configured for prompt enhancement");
|
|
457
|
+
}
|
|
458
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
459
|
+
// PRIVATE METHODS
|
|
460
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
461
|
+
async generateWithOpenAI(prompt) {
|
|
462
|
+
if (!this.openai) {
|
|
463
|
+
throw new Error("OpenAI client not initialized");
|
|
464
|
+
}
|
|
465
|
+
const response = await this.openai.images.generate({
|
|
466
|
+
model: "dall-e-3",
|
|
467
|
+
prompt,
|
|
468
|
+
n: 1,
|
|
469
|
+
size: this.config.size,
|
|
470
|
+
quality: this.config.quality,
|
|
471
|
+
style: this.config.dalleStyle || "natural",
|
|
472
|
+
response_format: "b64_json"
|
|
473
|
+
});
|
|
474
|
+
const imageData = response.data?.[0]?.b64_json;
|
|
475
|
+
if (!imageData) {
|
|
476
|
+
throw new Error("No image data received from OpenAI");
|
|
477
|
+
}
|
|
478
|
+
return imageData;
|
|
479
|
+
}
|
|
480
|
+
async resizeImage(inputPath, options) {
|
|
481
|
+
const { width, height, quality = 85, format = "webp", fit = "inside" } = options;
|
|
482
|
+
let pipeline = sharp2__default.default(inputPath);
|
|
483
|
+
if (width || height) {
|
|
484
|
+
pipeline = pipeline.resize(width, height, {
|
|
485
|
+
fit,
|
|
486
|
+
withoutEnlargement: true
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
const outputPath = inputPath.replace(/\.[^.]+$/, `.${format}`);
|
|
490
|
+
switch (format) {
|
|
491
|
+
case "webp":
|
|
492
|
+
pipeline = pipeline.webp({ quality });
|
|
493
|
+
break;
|
|
494
|
+
case "jpeg":
|
|
495
|
+
pipeline = pipeline.jpeg({ quality });
|
|
496
|
+
break;
|
|
497
|
+
case "png":
|
|
498
|
+
pipeline = pipeline.png({ quality });
|
|
499
|
+
break;
|
|
500
|
+
case "avif":
|
|
501
|
+
pipeline = pipeline.avif({ quality });
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
await pipeline.toFile(outputPath);
|
|
505
|
+
return outputPath;
|
|
506
|
+
}
|
|
507
|
+
async getImageInfo(imagePath, category) {
|
|
508
|
+
const stats = await fs3__default.default.stat(imagePath);
|
|
509
|
+
const metadata = await sharp2__default.default(imagePath).metadata();
|
|
510
|
+
const filename = path__default.default.basename(imagePath);
|
|
511
|
+
const extension = path__default.default.extname(filename).slice(1);
|
|
512
|
+
const id = path__default.default.basename(filename, path__default.default.extname(filename));
|
|
513
|
+
const publicDir = path__default.default.join(this.config.projectRoot, this.config.publicDir);
|
|
514
|
+
const relativePath = path__default.default.relative(publicDir, imagePath);
|
|
515
|
+
const url = "/" + relativePath.replace(/\\/g, "/");
|
|
516
|
+
return {
|
|
517
|
+
id,
|
|
518
|
+
filename,
|
|
519
|
+
extension,
|
|
520
|
+
path: relativePath,
|
|
521
|
+
url,
|
|
522
|
+
size: stats.size,
|
|
523
|
+
width: metadata.width,
|
|
524
|
+
height: metadata.height,
|
|
525
|
+
createdAt: stats.birthtime,
|
|
526
|
+
modifiedAt: stats.mtime
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
buildPrompt(basePrompt) {
|
|
530
|
+
const parts = [];
|
|
531
|
+
if (this.config.prefix) {
|
|
532
|
+
parts.push(this.config.prefix);
|
|
533
|
+
}
|
|
534
|
+
parts.push(basePrompt);
|
|
535
|
+
if (this.config.suffix) {
|
|
536
|
+
parts.push(this.config.suffix);
|
|
537
|
+
}
|
|
538
|
+
return parts.join(" ");
|
|
539
|
+
}
|
|
540
|
+
generateFilename(prompt) {
|
|
541
|
+
return prompt.toLowerCase().replace(/[^a-z0-9\s]/g, "").replace(/\s+/g, "-").substring(0, 30);
|
|
542
|
+
}
|
|
543
|
+
delay(ms) {
|
|
544
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
547
|
var ImageAICLI = class {
|
|
548
548
|
config;
|
|
549
549
|
generator;
|
|
@@ -873,10 +873,10 @@ var ImageAICLI = class {
|
|
|
873
873
|
|
|
874
874
|
// src/cli/bin.ts
|
|
875
875
|
var __filename$1 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bin.cjs', document.baseURI).href)));
|
|
876
|
-
var __dirname$1 =
|
|
876
|
+
var __dirname$1 = path__default.default.dirname(__filename$1);
|
|
877
877
|
dotenv__default.default.config();
|
|
878
|
-
dotenv__default.default.config({ path:
|
|
879
|
-
dotenv__default.default.config({ path:
|
|
878
|
+
dotenv__default.default.config({ path: path__default.default.resolve(process.cwd(), ".env.local") });
|
|
879
|
+
dotenv__default.default.config({ path: path__default.default.resolve(__dirname$1, "../../.env") });
|
|
880
880
|
var program = new commander.Command();
|
|
881
881
|
function createConfig(options) {
|
|
882
882
|
const projectRoot = options.root || process.cwd();
|
|
@@ -974,7 +974,7 @@ program.command("batch").alias("b").description("Batch generate images from file
|
|
|
974
974
|
const config = createConfig(options);
|
|
975
975
|
const generator = new ImageGenerator(config);
|
|
976
976
|
const fs4 = await import('fs-extra');
|
|
977
|
-
const filePath =
|
|
977
|
+
const filePath = path__default.default.resolve(process.cwd(), file);
|
|
978
978
|
if (!await fs4.pathExists(filePath)) {
|
|
979
979
|
console.error(chalk__default.default.red(`File not found: ${filePath}`));
|
|
980
980
|
process.exit(1);
|