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.
- package/.env.example +15 -0
- package/LICENSE +21 -0
- package/README.ja.md +225 -0
- package/README.md +225 -0
- package/dist/cli/batch.d.ts +19 -0
- package/dist/cli/batch.d.ts.map +1 -0
- package/dist/cli/batch.js +288 -0
- package/dist/cli/batch.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +203 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/edit.d.ts +14 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +236 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/generate.d.ts +13 -0
- package/dist/tools/generate.d.ts.map +1 -0
- package/dist/tools/generate.js +183 -0
- package/dist/tools/generate.js.map +1 -0
- package/dist/types/batch.d.ts +137 -0
- package/dist/types/batch.d.ts.map +1 -0
- package/dist/types/batch.js +5 -0
- package/dist/types/batch.js.map +1 -0
- package/dist/types/tools.d.ts +52 -0
- package/dist/types/tools.d.ts.map +1 -0
- package/dist/types/tools.js +20 -0
- package/dist/types/tools.js.map +1 -0
- package/dist/utils/batch-config.d.ts +31 -0
- package/dist/utils/batch-config.d.ts.map +1 -0
- package/dist/utils/batch-config.js +236 -0
- package/dist/utils/batch-config.js.map +1 -0
- package/dist/utils/batch-manager.d.ts +24 -0
- package/dist/utils/batch-manager.d.ts.map +1 -0
- package/dist/utils/batch-manager.js +301 -0
- package/dist/utils/batch-manager.js.map +1 -0
- package/dist/utils/debug.d.ts +5 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +16 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/image.d.ts +36 -0
- package/dist/utils/image.d.ts.map +1 -0
- package/dist/utils/image.js +85 -0
- package/dist/utils/image.js.map +1 -0
- package/dist/utils/path.d.ts +16 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +70 -0
- package/dist/utils/path.js.map +1 -0
- package/examples/batch-detailed.json +41 -0
- package/examples/batch-simple.json +14 -0
- package/examples/batch-variants.json +23 -0
- package/examples/batch-with-edits.json +26 -0
- 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 @@
|
|
|
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
|
+
}
|