grok-imagine-image-mcp-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.env.example +15 -0
  2. package/LICENSE +21 -0
  3. package/README.ja.md +225 -0
  4. package/README.md +225 -0
  5. package/dist/cli/batch.d.ts +19 -0
  6. package/dist/cli/batch.d.ts.map +1 -0
  7. package/dist/cli/batch.js +288 -0
  8. package/dist/cli/batch.js.map +1 -0
  9. package/dist/index.d.ts +9 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +203 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/tools/edit.d.ts +14 -0
  14. package/dist/tools/edit.d.ts.map +1 -0
  15. package/dist/tools/edit.js +236 -0
  16. package/dist/tools/edit.js.map +1 -0
  17. package/dist/tools/generate.d.ts +13 -0
  18. package/dist/tools/generate.d.ts.map +1 -0
  19. package/dist/tools/generate.js +183 -0
  20. package/dist/tools/generate.js.map +1 -0
  21. package/dist/types/batch.d.ts +137 -0
  22. package/dist/types/batch.d.ts.map +1 -0
  23. package/dist/types/batch.js +5 -0
  24. package/dist/types/batch.js.map +1 -0
  25. package/dist/types/tools.d.ts +52 -0
  26. package/dist/types/tools.d.ts.map +1 -0
  27. package/dist/types/tools.js +20 -0
  28. package/dist/types/tools.js.map +1 -0
  29. package/dist/utils/batch-config.d.ts +31 -0
  30. package/dist/utils/batch-config.d.ts.map +1 -0
  31. package/dist/utils/batch-config.js +236 -0
  32. package/dist/utils/batch-config.js.map +1 -0
  33. package/dist/utils/batch-manager.d.ts +24 -0
  34. package/dist/utils/batch-manager.d.ts.map +1 -0
  35. package/dist/utils/batch-manager.js +301 -0
  36. package/dist/utils/batch-manager.js.map +1 -0
  37. package/dist/utils/debug.d.ts +5 -0
  38. package/dist/utils/debug.d.ts.map +1 -0
  39. package/dist/utils/debug.js +16 -0
  40. package/dist/utils/debug.js.map +1 -0
  41. package/dist/utils/image.d.ts +36 -0
  42. package/dist/utils/image.d.ts.map +1 -0
  43. package/dist/utils/image.js +85 -0
  44. package/dist/utils/image.js.map +1 -0
  45. package/dist/utils/path.d.ts +16 -0
  46. package/dist/utils/path.d.ts.map +1 -0
  47. package/dist/utils/path.js +70 -0
  48. package/dist/utils/path.js.map +1 -0
  49. package/examples/batch-detailed.json +41 -0
  50. package/examples/batch-simple.json +14 -0
  51. package/examples/batch-variants.json +23 -0
  52. package/examples/batch-with-edits.json +26 -0
  53. package/package.json +66 -0
@@ -0,0 +1,301 @@
1
+ /**
2
+ * Batch execution manager with concurrency control
3
+ */
4
+ import { resolveOutputPath, getDefaultOutputDirectory } from './batch-config.js';
5
+ import { generateImage } from '../tools/generate.js';
6
+ import { editImage } from '../tools/edit.js';
7
+ import { debugLog } from './debug.js';
8
+ /**
9
+ * Semaphore for concurrency control
10
+ */
11
+ class Semaphore {
12
+ permits;
13
+ queue = [];
14
+ constructor(permits) {
15
+ this.permits = permits;
16
+ }
17
+ async acquire() {
18
+ if (this.permits > 0) {
19
+ this.permits--;
20
+ return;
21
+ }
22
+ return new Promise((resolve) => {
23
+ this.queue.push(resolve);
24
+ });
25
+ }
26
+ release() {
27
+ const next = this.queue.shift();
28
+ if (next) {
29
+ next();
30
+ }
31
+ else {
32
+ this.permits++;
33
+ }
34
+ }
35
+ }
36
+ /**
37
+ * Cost per image by model
38
+ */
39
+ const MODEL_COSTS = {
40
+ 'grok-imagine-image': { base: 0.02, edit_input: 0.002 },
41
+ 'grok-2-image': { base: 0.07, edit_input: 0 },
42
+ 'grok-2-image-latest': { base: 0.07, edit_input: 0 },
43
+ 'grok-2-image-1212': { base: 0.07, edit_input: 0 },
44
+ };
45
+ /**
46
+ * BatchManager handles batch execution with concurrency control
47
+ */
48
+ export class BatchManager {
49
+ apiKey;
50
+ constructor(apiKey) {
51
+ this.apiKey = apiKey;
52
+ }
53
+ /**
54
+ * Estimate cost for batch execution
55
+ */
56
+ estimateBatchCost(config) {
57
+ const breakdown = [];
58
+ const modelCounts = {};
59
+ for (const job of config.jobs) {
60
+ const model = job.model || config.default_model || 'grok-imagine-image';
61
+ const n = job.n || 1;
62
+ const isEdit = !!(job.image_path || job.image_base64 || job.image_url);
63
+ const key = `${model}${isEdit ? '_edit' : ''}`;
64
+ if (!modelCounts[key]) {
65
+ modelCounts[key] = { count: 0, images: 0, isEdit };
66
+ }
67
+ modelCounts[key].count++;
68
+ modelCounts[key].images += n;
69
+ }
70
+ let totalMin = 0;
71
+ let totalMax = 0;
72
+ let totalImages = 0;
73
+ for (const [key, data] of Object.entries(modelCounts)) {
74
+ const modelName = key.replace('_edit', '');
75
+ const costs = MODEL_COSTS[modelName] || MODEL_COSTS['grok-imagine-image'];
76
+ const baseCost = costs.base * data.images;
77
+ const editCost = data.isEdit ? costs.edit_input * data.count : 0;
78
+ const cost = baseCost + editCost;
79
+ breakdown.push({
80
+ model: key,
81
+ count: data.count,
82
+ images: data.images,
83
+ costMin: cost,
84
+ costMax: cost,
85
+ });
86
+ totalMin += cost;
87
+ totalMax += cost;
88
+ totalImages += data.images;
89
+ }
90
+ return {
91
+ totalJobs: config.jobs.length,
92
+ totalImages,
93
+ estimatedCostMin: totalMin,
94
+ estimatedCostMax: totalMax,
95
+ breakdown,
96
+ };
97
+ }
98
+ /**
99
+ * Execute batch jobs with concurrency control
100
+ */
101
+ async executeBatch(config, options = {}) {
102
+ const startTime = Date.now();
103
+ const startedAt = new Date().toISOString();
104
+ const maxConcurrent = config.max_concurrent || 2;
105
+ const timeout = config.timeout || 600000;
106
+ const outputDir = config.output_dir || getDefaultOutputDirectory();
107
+ const semaphore = new Semaphore(maxConcurrent);
108
+ debugLog(`Starting batch execution: ${config.jobs.length} jobs, max concurrent: ${maxConcurrent}`);
109
+ const results = [];
110
+ const jobPromises = [];
111
+ // Create job promises
112
+ for (let i = 0; i < config.jobs.length; i++) {
113
+ const job = config.jobs[i];
114
+ const outputPath = resolveOutputPath(job, i, outputDir, options.allowAnyPath);
115
+ const jobPromise = (async () => {
116
+ await semaphore.acquire();
117
+ try {
118
+ const result = await this.executeJob(job, i, outputPath, config);
119
+ results.push(result);
120
+ }
121
+ finally {
122
+ semaphore.release();
123
+ }
124
+ })();
125
+ jobPromises.push(jobPromise);
126
+ }
127
+ // Execute with timeout
128
+ let timedOut = false;
129
+ try {
130
+ await Promise.race([
131
+ Promise.all(jobPromises),
132
+ new Promise((_, reject) => setTimeout(() => {
133
+ timedOut = true;
134
+ reject(new Error('Batch execution timed out'));
135
+ }, timeout)),
136
+ ]);
137
+ }
138
+ catch (error) {
139
+ if (timedOut) {
140
+ debugLog('Batch timed out, waiting for in-progress jobs...');
141
+ // Wait a bit for in-progress jobs to complete
142
+ await new Promise((resolve) => setTimeout(resolve, 2000));
143
+ }
144
+ else {
145
+ throw error;
146
+ }
147
+ }
148
+ // Mark incomplete jobs as cancelled
149
+ const completedIndices = new Set(results.map((r) => r.index));
150
+ for (let i = 0; i < config.jobs.length; i++) {
151
+ if (!completedIndices.has(i + 1)) {
152
+ results.push({
153
+ index: i + 1,
154
+ prompt: config.jobs[i].prompt,
155
+ status: 'cancelled',
156
+ error: 'Job cancelled due to timeout',
157
+ });
158
+ }
159
+ }
160
+ // Sort results by index
161
+ results.sort((a, b) => a.index - b.index);
162
+ const endTime = Date.now();
163
+ const finishedAt = new Date().toISOString();
164
+ // Calculate totals
165
+ const succeeded = results.filter((r) => r.status === 'completed').length;
166
+ const failed = results.filter((r) => r.status === 'failed').length;
167
+ const cancelled = results.filter((r) => r.status === 'cancelled').length;
168
+ // Estimate cost
169
+ const estimate = this.estimateBatchCost(config);
170
+ return {
171
+ total: config.jobs.length,
172
+ succeeded,
173
+ failed,
174
+ cancelled,
175
+ results,
176
+ started_at: startedAt,
177
+ finished_at: finishedAt,
178
+ total_duration_ms: endTime - startTime,
179
+ estimated_cost: estimate.estimatedCostMin,
180
+ };
181
+ }
182
+ /**
183
+ * Execute a single job with retry logic
184
+ */
185
+ async executeJob(job, index, outputPath, config) {
186
+ const jobIndex = index + 1;
187
+ const isEditJob = !!(job.image_path || job.image_base64 || job.image_url);
188
+ const retryPolicy = config.retry_policy || { max_retries: 2, retry_delay_ms: 1000 };
189
+ const maxRetries = retryPolicy.max_retries ?? 2;
190
+ const retryDelay = retryPolicy.retry_delay_ms ?? 1000;
191
+ const retryPatterns = retryPolicy.retry_on_errors ?? ['rate_limit', 'timeout', '429', '503'];
192
+ let lastError = '';
193
+ const startTime = Date.now();
194
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
195
+ try {
196
+ debugLog(`Job ${jobIndex}: Starting (attempt ${attempt + 1}/${maxRetries + 1})`);
197
+ let result;
198
+ let revisedPrompt;
199
+ let outputPaths = [];
200
+ if (isEditJob) {
201
+ // Edit job
202
+ result = await editImage(this.apiKey, {
203
+ prompt: job.prompt,
204
+ image_path: job.image_path,
205
+ image_base64: job.image_base64,
206
+ image_url: job.image_url,
207
+ output_path: outputPath,
208
+ model: job.model || config.default_model,
209
+ n: job.n || 1,
210
+ resolution: job.resolution || config.default_resolution,
211
+ });
212
+ }
213
+ else {
214
+ // Generate job
215
+ result = await generateImage(this.apiKey, {
216
+ prompt: job.prompt,
217
+ output_path: outputPath,
218
+ model: job.model || config.default_model,
219
+ n: job.n || 1,
220
+ aspect_ratio: job.aspect_ratio || config.default_aspect_ratio,
221
+ resolution: job.resolution || config.default_resolution,
222
+ });
223
+ }
224
+ // Parse result
225
+ if (typeof result === 'string') {
226
+ // Extract output paths from result text
227
+ const pathMatches = result.match(/(?:saved|generated|edited)[^:]*:\s*([^\n]+)/gi);
228
+ if (pathMatches) {
229
+ for (const match of pathMatches) {
230
+ const pathMatch = match.match(/:\s*(.+)/);
231
+ if (pathMatch) {
232
+ outputPaths.push(pathMatch[1].trim());
233
+ }
234
+ }
235
+ }
236
+ // Extract revised prompt
237
+ const promptMatch = result.match(/Revised prompt:\s*(.+)/i);
238
+ if (promptMatch) {
239
+ revisedPrompt = promptMatch[1].trim();
240
+ }
241
+ }
242
+ else if (result && result.content) {
243
+ // Result with content array
244
+ const textContent = result.content.find((c) => c.type === 'text');
245
+ if (textContent && textContent.text) {
246
+ const pathMatches = textContent.text.match(/(?:saved|generated|edited)[^:]*:\s*([^\n]+)/gi);
247
+ if (pathMatches) {
248
+ for (const match of pathMatches) {
249
+ const pathMatch = match.match(/:\s*(.+)/);
250
+ if (pathMatch) {
251
+ outputPaths.push(pathMatch[1].trim());
252
+ }
253
+ }
254
+ }
255
+ const promptMatch = textContent.text.match(/Revised prompt:\s*(.+)/i);
256
+ if (promptMatch) {
257
+ revisedPrompt = promptMatch[1].trim();
258
+ }
259
+ }
260
+ }
261
+ // If no paths extracted, use the expected output path
262
+ if (outputPaths.length === 0) {
263
+ outputPaths.push(outputPath);
264
+ }
265
+ const duration = Date.now() - startTime;
266
+ debugLog(`Job ${jobIndex}: Completed in ${duration}ms`);
267
+ return {
268
+ index: jobIndex,
269
+ prompt: job.prompt,
270
+ status: 'completed',
271
+ output_paths: outputPaths,
272
+ duration_ms: duration,
273
+ revised_prompt: revisedPrompt,
274
+ is_edit: isEditJob,
275
+ };
276
+ }
277
+ catch (error) {
278
+ lastError = error.message || String(error);
279
+ debugLog(`Job ${jobIndex}: Failed (attempt ${attempt + 1}): ${lastError}`);
280
+ // Check if we should retry
281
+ const shouldRetry = attempt < maxRetries &&
282
+ retryPatterns.some((pattern) => lastError.toLowerCase().includes(pattern.toLowerCase()));
283
+ if (shouldRetry) {
284
+ debugLog(`Job ${jobIndex}: Retrying in ${retryDelay}ms...`);
285
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
286
+ }
287
+ }
288
+ }
289
+ // All retries exhausted
290
+ const duration = Date.now() - startTime;
291
+ return {
292
+ index: jobIndex,
293
+ prompt: job.prompt,
294
+ status: 'failed',
295
+ error: lastError,
296
+ duration_ms: duration,
297
+ is_edit: isEditJob,
298
+ };
299
+ }
300
+ }
301
+ //# sourceMappingURL=batch-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-manager.js","sourceRoot":"","sources":["../../src/utils/batch-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Debug logging utility
3
+ */
4
+ export declare function debugLog(message: string, data?: any): void;
5
+ //# sourceMappingURL=debug.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../../src/utils/debug.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAS1D"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Debug logging utility
3
+ */
4
+ const DEBUG = process.env.DEBUG === 'true';
5
+ export function debugLog(message, data) {
6
+ if (DEBUG) {
7
+ const timestamp = new Date().toISOString();
8
+ if (data !== undefined) {
9
+ console.error(`[${timestamp}] ${message}`, JSON.stringify(data, null, 2));
10
+ }
11
+ else {
12
+ console.error(`[${timestamp}] ${message}`);
13
+ }
14
+ }
15
+ }
16
+ //# sourceMappingURL=debug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug.js","sourceRoot":"","sources":["../../src/utils/debug.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,MAAM,CAAC;AAE3C,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,IAAU;IAClD,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC3C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,CAAC,KAAK,CAAC,IAAI,SAAS,KAAK,OAAO,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5E,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Image utilities for saving and processing images
3
+ */
4
+ /**
5
+ * Save base64 image data to file
6
+ */
7
+ export declare function saveBase64Image(base64Data: string, outputPath: string): Promise<void>;
8
+ /**
9
+ * Download image from URL and save to file
10
+ */
11
+ export declare function downloadAndSaveImage(url: string, outputPath: string): Promise<void>;
12
+ /**
13
+ * Validate aspect ratio format
14
+ */
15
+ export declare function validateAspectRatio(aspectRatio: string): boolean;
16
+ /**
17
+ * Get file extension from output format or path
18
+ */
19
+ export declare function getImageExtension(outputPath: string): string;
20
+ /**
21
+ * Generate thumbnail data from image file
22
+ */
23
+ export declare function generateThumbnailData(imagePath: string, maxWidth?: number, maxHeight?: number): Promise<string>;
24
+ /**
25
+ * Check if thumbnail generation is enabled via environment variable
26
+ */
27
+ export declare function isThumbnailEnabled(): boolean;
28
+ /**
29
+ * Create MCP content object with thumbnail
30
+ */
31
+ export declare function createThumbnailContent(base64Data: string): {
32
+ type: string;
33
+ data: string;
34
+ mimeType: string;
35
+ };
36
+ //# sourceMappingURL=image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/utils/image.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH;;GAEG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAIhE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAM5D;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,QAAQ,GAAE,MAAY,EACtB,SAAS,GAAE,MAAY,GACtB,OAAO,CAAC,MAAM,CAAC,CAmBjB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAMA"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Image utilities for saving and processing images
3
+ */
4
+ import * as fs from 'fs/promises';
5
+ import * as path from 'path';
6
+ import { debugLog } from './debug.js';
7
+ /**
8
+ * Save base64 image data to file
9
+ */
10
+ export async function saveBase64Image(base64Data, outputPath) {
11
+ const buffer = Buffer.from(base64Data, 'base64');
12
+ await fs.writeFile(outputPath, buffer);
13
+ debugLog(`Saved image to: ${outputPath}`);
14
+ }
15
+ /**
16
+ * Download image from URL and save to file
17
+ */
18
+ export async function downloadAndSaveImage(url, outputPath) {
19
+ debugLog(`Downloading image from: ${url}`);
20
+ const response = await fetch(url);
21
+ if (!response.ok) {
22
+ throw new Error(`Failed to download image: ${response.status} ${response.statusText}`);
23
+ }
24
+ const arrayBuffer = await response.arrayBuffer();
25
+ const buffer = Buffer.from(arrayBuffer);
26
+ await fs.writeFile(outputPath, buffer);
27
+ debugLog(`Saved image to: ${outputPath}`);
28
+ }
29
+ /**
30
+ * Validate aspect ratio format
31
+ */
32
+ export function validateAspectRatio(aspectRatio) {
33
+ // Format: "width:height" e.g., "4:3", "16:9", "1:1"
34
+ const pattern = /^\d+:\d+$/;
35
+ return pattern.test(aspectRatio);
36
+ }
37
+ /**
38
+ * Get file extension from output format or path
39
+ */
40
+ export function getImageExtension(outputPath) {
41
+ const ext = path.extname(outputPath).toLowerCase();
42
+ if (['.png', '.jpg', '.jpeg', '.webp'].includes(ext)) {
43
+ return ext.substring(1);
44
+ }
45
+ return 'png'; // default
46
+ }
47
+ /**
48
+ * Generate thumbnail data from image file
49
+ */
50
+ export async function generateThumbnailData(imagePath, maxWidth = 256, maxHeight = 256) {
51
+ try {
52
+ // Dynamic import for sharp (optional dependency)
53
+ const sharp = (await import('sharp')).default;
54
+ const imageBuffer = await fs.readFile(imagePath);
55
+ const thumbnailBuffer = await sharp(imageBuffer)
56
+ .resize(maxWidth, maxHeight, {
57
+ fit: 'inside',
58
+ withoutEnlargement: true,
59
+ })
60
+ .jpeg({ quality: 80 })
61
+ .toBuffer();
62
+ return thumbnailBuffer.toString('base64');
63
+ }
64
+ catch (error) {
65
+ debugLog(`Failed to generate thumbnail: ${error.message}`);
66
+ throw error;
67
+ }
68
+ }
69
+ /**
70
+ * Check if thumbnail generation is enabled via environment variable
71
+ */
72
+ export function isThumbnailEnabled() {
73
+ return process.env.XAI_IMAGE_THUMBNAIL === 'true';
74
+ }
75
+ /**
76
+ * Create MCP content object with thumbnail
77
+ */
78
+ export function createThumbnailContent(base64Data) {
79
+ return {
80
+ type: 'image',
81
+ data: base64Data,
82
+ mimeType: 'image/jpeg',
83
+ };
84
+ }
85
+ //# sourceMappingURL=image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/utils/image.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Path utilities for cross-platform file handling
3
+ */
4
+ /**
5
+ * Normalize and validate output path
6
+ */
7
+ export declare function normalizeAndValidatePath(outputPath: string): Promise<string>;
8
+ /**
9
+ * Generate unique file path to avoid overwriting
10
+ */
11
+ export declare function generateUniqueFilePath(filePath: string): Promise<string>;
12
+ /**
13
+ * Get display-friendly path (relative to cwd if possible)
14
+ */
15
+ export declare function getDisplayPath(filePath: string): string;
16
+ //# sourceMappingURL=path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH;;GAEG;AACH,wBAAsB,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBlF;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkB9E;AAcD;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMvD"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Path utilities for cross-platform file handling
3
+ */
4
+ import * as path from 'path';
5
+ import * as fs from 'fs/promises';
6
+ import { debugLog } from './debug.js';
7
+ /**
8
+ * Normalize and validate output path
9
+ */
10
+ export async function normalizeAndValidatePath(outputPath) {
11
+ // Handle relative paths
12
+ let normalizedPath = outputPath;
13
+ if (!path.isAbsolute(outputPath)) {
14
+ // Use OUTPUT_DIR env var or current working directory
15
+ const baseDir = process.env.OUTPUT_DIR || process.cwd();
16
+ normalizedPath = path.join(baseDir, outputPath);
17
+ }
18
+ // Ensure directory exists
19
+ const dir = path.dirname(normalizedPath);
20
+ try {
21
+ await fs.mkdir(dir, { recursive: true });
22
+ debugLog(`Directory ensured: ${dir}`);
23
+ }
24
+ catch (error) {
25
+ debugLog(`Failed to create directory: ${dir}`, error.message);
26
+ throw new Error(`Cannot create output directory: ${dir}`);
27
+ }
28
+ return normalizedPath;
29
+ }
30
+ /**
31
+ * Generate unique file path to avoid overwriting
32
+ */
33
+ export async function generateUniqueFilePath(filePath) {
34
+ const ext = path.extname(filePath);
35
+ const baseName = path.basename(filePath, ext);
36
+ const dir = path.dirname(filePath);
37
+ let counter = 0;
38
+ let uniquePath = filePath;
39
+ while (await fileExists(uniquePath)) {
40
+ counter++;
41
+ uniquePath = path.join(dir, `${baseName}_${counter}${ext}`);
42
+ }
43
+ if (counter > 0) {
44
+ debugLog(`Generated unique path: ${uniquePath}`);
45
+ }
46
+ return uniquePath;
47
+ }
48
+ /**
49
+ * Check if file exists
50
+ */
51
+ async function fileExists(filePath) {
52
+ try {
53
+ await fs.access(filePath);
54
+ return true;
55
+ }
56
+ catch {
57
+ return false;
58
+ }
59
+ }
60
+ /**
61
+ * Get display-friendly path (relative to cwd if possible)
62
+ */
63
+ export function getDisplayPath(filePath) {
64
+ const cwd = process.cwd();
65
+ if (filePath.startsWith(cwd)) {
66
+ return path.relative(cwd, filePath) || filePath;
67
+ }
68
+ return filePath;
69
+ }
70
+ //# sourceMappingURL=path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,UAAkB;IAC/D,wBAAwB;IACxB,IAAI,cAAc,GAAG,UAAU,CAAC;IAEhC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACjC,sDAAsD;QACtD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACxD,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAClD,CAAC;IAED,0BAA0B;IAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IACzC,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,QAAQ,CAAC,sBAAsB,GAAG,EAAE,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,QAAQ,CAAC,+BAA+B,GAAG,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9D,MAAM,IAAI,KAAK,CAAC,mCAAmC,GAAG,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,cAAc,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,QAAgB;IAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEnC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,UAAU,GAAG,QAAQ,CAAC;IAE1B,OAAO,MAAM,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,CAAC;QACV,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,IAAI,OAAO,GAAG,GAAG,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,QAAQ,CAAC,0BAA0B,UAAU,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,UAAU,CAAC,QAAgB;IACxC,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,QAAQ,CAAC;IAClD,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,41 @@
1
+ {
2
+ "jobs": [
3
+ {
4
+ "prompt": "A majestic mountain landscape at sunrise",
5
+ "output_path": "mountain_sunrise.jpg",
6
+ "aspect_ratio": "16:9",
7
+ "resolution": "1k"
8
+ },
9
+ {
10
+ "prompt": "Portrait of a wise old wizard with a long beard",
11
+ "output_path": "wizard_portrait.jpg",
12
+ "aspect_ratio": "3:4",
13
+ "resolution": "1k"
14
+ },
15
+ {
16
+ "prompt": "A tropical beach with crystal clear water",
17
+ "output_path": "beach.jpg",
18
+ "aspect_ratio": "16:9",
19
+ "resolution": "1k"
20
+ },
21
+ {
22
+ "prompt": "A cozy coffee shop interior with warm lighting",
23
+ "output_path": "coffee_shop.jpg",
24
+ "aspect_ratio": "4:3"
25
+ },
26
+ {
27
+ "prompt": "An astronaut floating in space with Earth in the background",
28
+ "output_path": "astronaut.jpg",
29
+ "aspect_ratio": "1:1",
30
+ "resolution": "1k"
31
+ }
32
+ ],
33
+ "output_dir": "./output",
34
+ "max_concurrent": 3,
35
+ "default_model": "grok-imagine-image",
36
+ "default_resolution": "1k",
37
+ "retry_policy": {
38
+ "max_retries": 2,
39
+ "retry_delay_ms": 2000
40
+ }
41
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "jobs": [
3
+ {
4
+ "prompt": "A beautiful sunset over mountains with orange and purple sky"
5
+ },
6
+ {
7
+ "prompt": "A cute cat playing with a ball of yarn"
8
+ },
9
+ {
10
+ "prompt": "A futuristic city at night with neon lights"
11
+ }
12
+ ],
13
+ "output_dir": "./output"
14
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "jobs": [
3
+ {
4
+ "prompt": "A cute robot mascot character for a tech company",
5
+ "output_path": "robot_mascot.jpg",
6
+ "n": 3
7
+ },
8
+ {
9
+ "prompt": "Logo design for a coffee brand, minimalist style",
10
+ "output_path": "coffee_logo.jpg",
11
+ "n": 5
12
+ },
13
+ {
14
+ "prompt": "Abstract art with geometric shapes and vibrant colors",
15
+ "output_path": "abstract_art.jpg",
16
+ "n": 4,
17
+ "aspect_ratio": "1:1",
18
+ "resolution": "1k"
19
+ }
20
+ ],
21
+ "output_dir": "./output/variants",
22
+ "max_concurrent": 2
23
+ }