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