n8n-nodes-commandos-image 0.1.8 → 0.1.10
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/nodes/CommandosImage.node.js +71 -130
- package/nodes/CommandosImage.node.ts +336 -414
- package/package.json +1 -1
|
@@ -15,9 +15,6 @@ const resolveGenerationUrl = (resolvedModel) => {
|
|
|
15
15
|
}
|
|
16
16
|
return GENERATION_URL_DEFAULT;
|
|
17
17
|
};
|
|
18
|
-
/**
|
|
19
|
-
* Maps standard ratios to specific model requirements
|
|
20
|
-
*/
|
|
21
18
|
const mapRatio = (model, ratio) => {
|
|
22
19
|
if (model.includes('seedream')) {
|
|
23
20
|
if (ratio === '2:3')
|
|
@@ -27,11 +24,10 @@ const mapRatio = (model, ratio) => {
|
|
|
27
24
|
}
|
|
28
25
|
return ratio;
|
|
29
26
|
};
|
|
30
|
-
const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'basic', resolution = '1K', }) => {
|
|
27
|
+
const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'basic', resolution = '1K', outputFormat, }) => {
|
|
31
28
|
const hasReferences = references.length > 0;
|
|
32
29
|
const requestedModel = model;
|
|
33
30
|
let resolvedModel = model;
|
|
34
|
-
// Model Resolution
|
|
35
31
|
if (requestedModel === 'flux-pro') {
|
|
36
32
|
resolvedModel = hasReferences ? 'flux-2/pro-image-to-image' : 'flux-2/pro-text-to-image';
|
|
37
33
|
}
|
|
@@ -44,12 +40,8 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
|
|
|
44
40
|
else if (requestedModel === 'seedream') {
|
|
45
41
|
resolvedModel = hasReferences ? 'seedream/4.5-edit' : 'seedream/4.5';
|
|
46
42
|
}
|
|
47
|
-
// Fallback for MJ or generic seedream
|
|
48
43
|
if (!hasReferences && (requestedModel === 'seedream' || requestedModel === 'midjourney')) {
|
|
49
|
-
if (requestedModel === '
|
|
50
|
-
// Handled above for 4.5
|
|
51
|
-
}
|
|
52
|
-
else if (requestedModel === 'midjourney') {
|
|
44
|
+
if (requestedModel === 'midjourney') {
|
|
53
45
|
resolvedModel = 'midjourney';
|
|
54
46
|
}
|
|
55
47
|
}
|
|
@@ -59,7 +51,7 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
|
|
|
59
51
|
filesUrl: references,
|
|
60
52
|
prompt,
|
|
61
53
|
size: ratio,
|
|
62
|
-
callBackUrl: 'https://api.comandos.ai/internal/kie/callback',
|
|
54
|
+
callBackUrl: 'https://api.comandos.ai/internal/kie/callback',
|
|
63
55
|
fallbackModel: 'FLUX_MAX',
|
|
64
56
|
};
|
|
65
57
|
}
|
|
@@ -70,6 +62,7 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
|
|
|
70
62
|
prompt,
|
|
71
63
|
aspect_ratio: ratio,
|
|
72
64
|
resolution,
|
|
65
|
+
output_format: 'jpeg',
|
|
73
66
|
...(hasReferences ? { input_urls: references } : {}),
|
|
74
67
|
},
|
|
75
68
|
};
|
|
@@ -81,6 +74,7 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
|
|
|
81
74
|
prompt,
|
|
82
75
|
aspect_ratio: mapRatio(resolvedModel, ratio),
|
|
83
76
|
quality,
|
|
77
|
+
output_format: 'jpeg',
|
|
84
78
|
...(hasReferences ? { image_urls: references } : {}),
|
|
85
79
|
},
|
|
86
80
|
};
|
|
@@ -92,7 +86,7 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
|
|
|
92
86
|
prompt,
|
|
93
87
|
aspect_ratio: ratio,
|
|
94
88
|
resolution,
|
|
95
|
-
output_format: 'jpg', //
|
|
89
|
+
output_format: outputFormat || 'jpg', // User selection or fallback to jpg
|
|
96
90
|
...(hasReferences ? { image_input: references } : {}),
|
|
97
91
|
},
|
|
98
92
|
};
|
|
@@ -103,7 +97,7 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
|
|
|
103
97
|
input: {
|
|
104
98
|
prompt,
|
|
105
99
|
image_size: ratio,
|
|
106
|
-
output_format: 'jpeg',
|
|
100
|
+
output_format: 'jpeg',
|
|
107
101
|
...(hasReferences ? { image_urls: references } : {}),
|
|
108
102
|
},
|
|
109
103
|
};
|
|
@@ -119,13 +113,13 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
|
|
|
119
113
|
};
|
|
120
114
|
}
|
|
121
115
|
else {
|
|
122
|
-
// Default fallback (v4 edit style)
|
|
123
116
|
body = {
|
|
124
117
|
model: requestedModel === 'gpt4o-image' ? 'gpt-4o' : 'bytedance/seedream-v4-edit',
|
|
125
118
|
input: {
|
|
126
119
|
image_urls: references,
|
|
127
120
|
prompt,
|
|
128
121
|
image_size: mapRatio('seedream', ratio),
|
|
122
|
+
output_format: 'jpeg',
|
|
129
123
|
},
|
|
130
124
|
};
|
|
131
125
|
}
|
|
@@ -140,12 +134,10 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
|
|
|
140
134
|
};
|
|
141
135
|
};
|
|
142
136
|
const extractReferences = (raw, model) => {
|
|
143
|
-
if (!raw || typeof raw !== 'object')
|
|
137
|
+
if (!raw || typeof raw !== 'object')
|
|
144
138
|
return [];
|
|
145
|
-
}
|
|
146
139
|
const collection = raw;
|
|
147
140
|
const items = Array.isArray(collection.reference) ? collection.reference : [];
|
|
148
|
-
// Dynamic limit based on model docs
|
|
149
141
|
let limit = 8;
|
|
150
142
|
if (model === 'seedream')
|
|
151
143
|
limit = 14;
|
|
@@ -153,10 +145,7 @@ const extractReferences = (raw, model) => {
|
|
|
153
145
|
limit = 16;
|
|
154
146
|
if (model.includes('nano-banana'))
|
|
155
147
|
limit = 10;
|
|
156
|
-
return items
|
|
157
|
-
.map((entry) => String((entry === null || entry === void 0 ? void 0 : entry.url) || '').trim())
|
|
158
|
-
.filter((value) => value.length > 0 && /^https?:\/\//i.test(value))
|
|
159
|
-
.slice(0, limit);
|
|
148
|
+
return items.map((entry) => String((entry === null || entry === void 0 ? void 0 : entry.url) || '').trim()).filter((v) => v.length > 0 && /^https?:\/\//i.test(v)).slice(0, limit);
|
|
160
149
|
};
|
|
161
150
|
class CommandosImage {
|
|
162
151
|
constructor() {
|
|
@@ -166,18 +155,11 @@ class CommandosImage {
|
|
|
166
155
|
group: ['transform'],
|
|
167
156
|
version: 1,
|
|
168
157
|
description: 'Create image tasks in Commandos API',
|
|
169
|
-
defaults: {
|
|
170
|
-
name: 'Commandos Image',
|
|
171
|
-
},
|
|
158
|
+
defaults: { name: 'Commandos Image' },
|
|
172
159
|
icon: 'file:Image.png',
|
|
173
160
|
inputs: ['main'],
|
|
174
161
|
outputs: ['main'],
|
|
175
|
-
credentials: [
|
|
176
|
-
{
|
|
177
|
-
name: 'commandosApi',
|
|
178
|
-
required: true,
|
|
179
|
-
},
|
|
180
|
-
],
|
|
162
|
+
credentials: [{ name: 'commandosApi', required: true }],
|
|
181
163
|
properties: [
|
|
182
164
|
{
|
|
183
165
|
displayName: 'Operation',
|
|
@@ -185,14 +167,8 @@ class CommandosImage {
|
|
|
185
167
|
type: 'options',
|
|
186
168
|
noDataExpression: true,
|
|
187
169
|
options: [
|
|
188
|
-
{
|
|
189
|
-
|
|
190
|
-
value: 'create',
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
name: 'Check Status',
|
|
194
|
-
value: 'status',
|
|
195
|
-
},
|
|
170
|
+
{ name: 'Create Task', value: 'create' },
|
|
171
|
+
{ name: 'Check Status', value: 'status' },
|
|
196
172
|
],
|
|
197
173
|
default: 'create',
|
|
198
174
|
},
|
|
@@ -209,25 +185,15 @@ class CommandosImage {
|
|
|
209
185
|
{ name: 'Midjourney', value: 'midjourney' },
|
|
210
186
|
],
|
|
211
187
|
default: 'seedream',
|
|
212
|
-
displayOptions: {
|
|
213
|
-
show: {
|
|
214
|
-
operation: ['create'],
|
|
215
|
-
},
|
|
216
|
-
},
|
|
188
|
+
displayOptions: { show: { operation: ['create'] } },
|
|
217
189
|
},
|
|
218
190
|
{
|
|
219
191
|
displayName: 'Prompt',
|
|
220
192
|
name: 'prompt',
|
|
221
193
|
type: 'string',
|
|
222
194
|
default: '',
|
|
223
|
-
typeOptions: {
|
|
224
|
-
|
|
225
|
-
},
|
|
226
|
-
displayOptions: {
|
|
227
|
-
show: {
|
|
228
|
-
operation: ['create'],
|
|
229
|
-
},
|
|
230
|
-
},
|
|
195
|
+
typeOptions: { rows: 4 },
|
|
196
|
+
displayOptions: { show: { operation: ['create'] } },
|
|
231
197
|
},
|
|
232
198
|
{
|
|
233
199
|
displayName: 'Ratio',
|
|
@@ -246,9 +212,35 @@ class CommandosImage {
|
|
|
246
212
|
],
|
|
247
213
|
default: '2:3',
|
|
248
214
|
displayOptions: {
|
|
249
|
-
show: {
|
|
250
|
-
|
|
251
|
-
|
|
215
|
+
show: { operation: ['create'] },
|
|
216
|
+
hide: { model: ['gpt4o-image'] },
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
displayName: 'Ratio',
|
|
221
|
+
name: 'ratioGpt',
|
|
222
|
+
type: 'options',
|
|
223
|
+
options: [
|
|
224
|
+
{ name: '1:1', value: '1:1' },
|
|
225
|
+
{ name: '2:3', value: '2:3' },
|
|
226
|
+
{ name: '3:2', value: '3:2' },
|
|
227
|
+
],
|
|
228
|
+
default: '2:3',
|
|
229
|
+
displayOptions: {
|
|
230
|
+
show: { operation: ['create'], model: ['gpt4o-image'] },
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
displayName: 'Output Format',
|
|
235
|
+
name: 'outputFormatBananaPro',
|
|
236
|
+
type: 'options',
|
|
237
|
+
options: [
|
|
238
|
+
{ name: 'PNG', value: 'png' },
|
|
239
|
+
{ name: 'JPG', value: 'jpg' },
|
|
240
|
+
],
|
|
241
|
+
default: 'jpg',
|
|
242
|
+
displayOptions: {
|
|
243
|
+
show: { operation: ['create'], model: ['nano-banana-pro'] },
|
|
252
244
|
},
|
|
253
245
|
},
|
|
254
246
|
{
|
|
@@ -262,10 +254,7 @@ class CommandosImage {
|
|
|
262
254
|
],
|
|
263
255
|
default: 'basic',
|
|
264
256
|
displayOptions: {
|
|
265
|
-
show: {
|
|
266
|
-
operation: ['create'],
|
|
267
|
-
model: ['seedream', 'gpt4o-image', 'flux-pro'],
|
|
268
|
-
},
|
|
257
|
+
show: { operation: ['create'], model: ['seedream', 'gpt4o-image', 'flux-pro'] },
|
|
269
258
|
},
|
|
270
259
|
},
|
|
271
260
|
{
|
|
@@ -279,10 +268,7 @@ class CommandosImage {
|
|
|
279
268
|
],
|
|
280
269
|
default: '1K',
|
|
281
270
|
displayOptions: {
|
|
282
|
-
show: {
|
|
283
|
-
operation: ['create'],
|
|
284
|
-
model: ['flux-pro', 'nano-banana-pro'],
|
|
285
|
-
},
|
|
271
|
+
show: { operation: ['create'], model: ['flux-pro', 'nano-banana-pro'] },
|
|
286
272
|
},
|
|
287
273
|
},
|
|
288
274
|
{
|
|
@@ -290,39 +276,22 @@ class CommandosImage {
|
|
|
290
276
|
name: 'references',
|
|
291
277
|
type: 'fixedCollection',
|
|
292
278
|
default: {},
|
|
293
|
-
typeOptions: {
|
|
294
|
-
multipleValues: true,
|
|
295
|
-
},
|
|
279
|
+
typeOptions: { multipleValues: true },
|
|
296
280
|
options: [
|
|
297
281
|
{
|
|
298
282
|
name: 'reference',
|
|
299
283
|
displayName: 'Reference',
|
|
300
|
-
values: [
|
|
301
|
-
{
|
|
302
|
-
displayName: 'Reference URL',
|
|
303
|
-
name: 'url',
|
|
304
|
-
type: 'string',
|
|
305
|
-
default: '',
|
|
306
|
-
},
|
|
307
|
-
],
|
|
284
|
+
values: [{ displayName: 'Reference URL', name: 'url', type: 'string', default: '' }],
|
|
308
285
|
},
|
|
309
286
|
],
|
|
310
|
-
displayOptions: {
|
|
311
|
-
show: {
|
|
312
|
-
operation: ['create'],
|
|
313
|
-
},
|
|
314
|
-
},
|
|
287
|
+
displayOptions: { show: { operation: ['create'] } },
|
|
315
288
|
},
|
|
316
289
|
{
|
|
317
290
|
displayName: 'Task ID',
|
|
318
291
|
name: 'taskId',
|
|
319
292
|
type: 'string',
|
|
320
293
|
default: '',
|
|
321
|
-
displayOptions: {
|
|
322
|
-
show: {
|
|
323
|
-
operation: ['status'],
|
|
324
|
-
},
|
|
325
|
-
},
|
|
294
|
+
displayOptions: { show: { operation: ['status'] } },
|
|
326
295
|
},
|
|
327
296
|
],
|
|
328
297
|
};
|
|
@@ -332,82 +301,54 @@ class CommandosImage {
|
|
|
332
301
|
const operation = this.getNodeParameter('operation', 0);
|
|
333
302
|
const credentials = await this.getCredentials('commandosApi');
|
|
334
303
|
const licenseKey = String(credentials.licenseKey || '').trim();
|
|
335
|
-
if (!licenseKey)
|
|
304
|
+
if (!licenseKey)
|
|
336
305
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'License key is required');
|
|
337
|
-
}
|
|
338
306
|
const results = [];
|
|
339
307
|
for (let i = 0; i < items.length; i += 1) {
|
|
340
308
|
try {
|
|
341
309
|
if (operation === 'create') {
|
|
342
310
|
const model = String(this.getNodeParameter('model', i));
|
|
343
311
|
const prompt = String(this.getNodeParameter('prompt', i) || '').trim();
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
? String(this.getNodeParameter('quality', i, 'basic'))
|
|
348
|
-
: 'basic';
|
|
349
|
-
const resolution = ['flux-pro', 'nano-banana-pro'].includes(model)
|
|
350
|
-
? String(this.getNodeParameter('resolution', i, '1K'))
|
|
351
|
-
: '1K';
|
|
352
|
-
if (!prompt) {
|
|
353
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Prompt is required', { itemIndex: i });
|
|
312
|
+
let ratio = '';
|
|
313
|
+
if (model === 'gpt4o-image') {
|
|
314
|
+
ratio = String(this.getNodeParameter('ratioGpt', i));
|
|
354
315
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
prompt,
|
|
358
|
-
ratio,
|
|
359
|
-
references,
|
|
360
|
-
quality,
|
|
361
|
-
resolution
|
|
362
|
-
});
|
|
363
|
-
if (!request.url) {
|
|
364
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'URL генерации не настроен. Проверь переменные окружения COMMANDOS_IMAGE_URL_DEFAULT/COMMANDOS_IMAGE_URL_GPT4O/COMMANDOS_IMAGE_URL_MJ', { itemIndex: i });
|
|
316
|
+
else {
|
|
317
|
+
ratio = String(this.getNodeParameter('ratio', i));
|
|
365
318
|
}
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
_v: '0.1.8'
|
|
375
|
-
};
|
|
319
|
+
const references = extractReferences(this.getNodeParameter('references', i, {}), model);
|
|
320
|
+
const quality = ['seedream', 'gpt4o-image', 'flux-pro'].includes(model) ? String(this.getNodeParameter('quality', i, 'basic')) : 'basic';
|
|
321
|
+
const resolution = ['flux-pro', 'nano-banana-pro'].includes(model) ? String(this.getNodeParameter('resolution', i, '1K')) : '1K';
|
|
322
|
+
const outputFormat = (model === 'nano-banana-pro') ? String(this.getNodeParameter('outputFormatBananaPro', i, 'jpg')) : undefined;
|
|
323
|
+
if (!prompt)
|
|
324
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Prompt is required', { itemIndex: i });
|
|
325
|
+
const request = buildGenerationRequest({ model, prompt, ratio, references, quality, resolution, outputFormat });
|
|
326
|
+
const payload = { ...request, licenseKey, _v: '0.1.10' };
|
|
376
327
|
const response = await this.helpers.request({
|
|
377
328
|
method: 'POST',
|
|
378
329
|
url: `${COMMANDOS_API_URL}/tasks`,
|
|
379
|
-
headers: {
|
|
380
|
-
|
|
381
|
-
'X-License-Key': licenseKey,
|
|
382
|
-
},
|
|
383
|
-
body: {
|
|
384
|
-
process_type: 'IMAGE_GENERATION',
|
|
385
|
-
payload,
|
|
386
|
-
},
|
|
330
|
+
headers: { 'Content-Type': 'application/json', 'X-License-Key': licenseKey },
|
|
331
|
+
body: { process_type: 'IMAGE_GENERATION', payload },
|
|
387
332
|
json: true,
|
|
388
333
|
});
|
|
389
334
|
results.push({ json: response });
|
|
390
335
|
}
|
|
391
336
|
else if (operation === 'status') {
|
|
392
337
|
const taskId = String(this.getNodeParameter('taskId', i) || '').trim();
|
|
393
|
-
if (!taskId)
|
|
338
|
+
if (!taskId)
|
|
394
339
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Task ID is required', { itemIndex: i });
|
|
395
|
-
}
|
|
396
340
|
const response = await this.helpers.request({
|
|
397
341
|
method: 'GET',
|
|
398
342
|
url: `${COMMANDOS_API_URL}/tasks/${encodeURIComponent(taskId)}`,
|
|
399
|
-
headers: {
|
|
400
|
-
'X-License-Key': licenseKey,
|
|
401
|
-
},
|
|
343
|
+
headers: { 'X-License-Key': licenseKey },
|
|
402
344
|
json: true,
|
|
403
345
|
});
|
|
404
346
|
results.push({ json: response });
|
|
405
347
|
}
|
|
406
348
|
}
|
|
407
349
|
catch (error) {
|
|
408
|
-
if (error instanceof n8n_workflow_1.NodeOperationError)
|
|
350
|
+
if (error instanceof n8n_workflow_1.NodeOperationError)
|
|
409
351
|
throw error;
|
|
410
|
-
}
|
|
411
352
|
const message = error instanceof Error ? error.message : 'Request failed';
|
|
412
353
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), message, { itemIndex: i });
|
|
413
354
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
IDataObject,
|
|
3
|
+
IExecuteFunctions,
|
|
4
|
+
INodeExecutionData,
|
|
5
|
+
INodeType,
|
|
6
|
+
INodeTypeDescription,
|
|
7
7
|
} from 'n8n-workflow';
|
|
8
8
|
import { NodeOperationError } from 'n8n-workflow';
|
|
9
9
|
|
|
@@ -13,448 +13,370 @@ const GENERATION_URL_GPT4O = String(process.env.COMMANDOS_IMAGE_URL_GPT4O || '')
|
|
|
13
13
|
const GENERATION_URL_MJ = String(process.env.COMMANDOS_IMAGE_URL_MJ || '').trim();
|
|
14
14
|
|
|
15
15
|
type BuildParams = {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
model: string;
|
|
17
|
+
prompt: string;
|
|
18
|
+
ratio: string;
|
|
19
|
+
references: string[];
|
|
20
|
+
quality?: string;
|
|
21
|
+
resolution?: string;
|
|
22
|
+
outputFormat?: string;
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
type BuildResult = {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
url: string;
|
|
27
|
+
body: Record<string, unknown>;
|
|
28
|
+
requestedModel: string;
|
|
29
|
+
resolvedModel: string;
|
|
30
|
+
ratio: string;
|
|
31
|
+
references: string[];
|
|
32
|
+
hasReferences: boolean;
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
const resolveGenerationUrl = (resolvedModel: string): string => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
if (resolvedModel === 'gpt4o-image') {
|
|
37
|
+
return GENERATION_URL_GPT4O || GENERATION_URL_DEFAULT;
|
|
38
|
+
}
|
|
39
|
+
if (resolvedModel === 'midjourney') {
|
|
40
|
+
return GENERATION_URL_MJ || GENERATION_URL_DEFAULT;
|
|
41
|
+
}
|
|
42
|
+
return GENERATION_URL_DEFAULT;
|
|
42
43
|
};
|
|
43
44
|
|
|
44
|
-
/**
|
|
45
|
-
* Maps standard ratios to specific model requirements
|
|
46
|
-
*/
|
|
47
45
|
const mapRatio = (model: string, ratio: string): string => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
if (model.includes('seedream')) {
|
|
47
|
+
if (ratio === '2:3') return 'portrait_3_2';
|
|
48
|
+
if (ratio === '3:2') return 'landscape_2_3';
|
|
49
|
+
}
|
|
50
|
+
return ratio;
|
|
53
51
|
};
|
|
54
52
|
|
|
55
53
|
const buildGenerationRequest = ({
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
model,
|
|
55
|
+
prompt,
|
|
56
|
+
ratio,
|
|
57
|
+
references,
|
|
58
|
+
quality = 'basic',
|
|
59
|
+
resolution = '1K',
|
|
60
|
+
outputFormat,
|
|
62
61
|
}: BuildParams): BuildResult => {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
const hasReferences = references.length > 0;
|
|
63
|
+
const requestedModel = model;
|
|
64
|
+
let resolvedModel = model;
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
66
|
+
if (requestedModel === 'flux-pro') {
|
|
67
|
+
resolvedModel = hasReferences ? 'flux-2/pro-image-to-image' : 'flux-2/pro-text-to-image';
|
|
68
|
+
} else if (requestedModel === 'nano-banana') {
|
|
69
|
+
resolvedModel = hasReferences ? 'google/nano-banana-edit' : 'google/nano-banana';
|
|
70
|
+
} else if (requestedModel === 'nano-banana-pro') {
|
|
71
|
+
resolvedModel = 'nano-banana-pro';
|
|
72
|
+
} else if (requestedModel === 'seedream') {
|
|
73
|
+
resolvedModel = hasReferences ? 'seedream/4.5-edit' : 'seedream/4.5';
|
|
74
|
+
}
|
|
77
75
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
} else if (requestedModel === 'midjourney') {
|
|
83
|
-
resolvedModel = 'midjourney';
|
|
76
|
+
if (!hasReferences && (requestedModel === 'seedream' || requestedModel === 'midjourney')) {
|
|
77
|
+
if (requestedModel === 'midjourney') {
|
|
78
|
+
resolvedModel = 'midjourney';
|
|
79
|
+
}
|
|
84
80
|
}
|
|
85
|
-
}
|
|
86
81
|
|
|
87
|
-
|
|
82
|
+
let body: Record<string, unknown> = {};
|
|
88
83
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
84
|
+
if (resolvedModel === 'gpt4o-image') {
|
|
85
|
+
body = {
|
|
86
|
+
filesUrl: references,
|
|
87
|
+
prompt,
|
|
88
|
+
size: ratio,
|
|
89
|
+
callBackUrl: 'https://api.comandos.ai/internal/kie/callback',
|
|
90
|
+
fallbackModel: 'FLUX_MAX',
|
|
91
|
+
};
|
|
92
|
+
} else if (resolvedModel.includes('flux-2/')) {
|
|
93
|
+
body = {
|
|
94
|
+
model: resolvedModel,
|
|
95
|
+
input: {
|
|
96
|
+
prompt,
|
|
97
|
+
aspect_ratio: ratio,
|
|
98
|
+
resolution,
|
|
99
|
+
output_format: 'jpeg',
|
|
100
|
+
...(hasReferences ? { input_urls: references } : {}),
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
} else if (resolvedModel === 'seedream/4.5' || resolvedModel === 'seedream/4.5-edit') {
|
|
104
|
+
body = {
|
|
105
|
+
model: resolvedModel,
|
|
106
|
+
input: {
|
|
107
|
+
prompt,
|
|
108
|
+
aspect_ratio: mapRatio(resolvedModel, ratio),
|
|
109
|
+
quality,
|
|
110
|
+
output_format: 'jpeg',
|
|
111
|
+
...(hasReferences ? { image_urls: references } : {}),
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
} else if (resolvedModel === 'nano-banana-pro') {
|
|
115
|
+
body = {
|
|
116
|
+
model: resolvedModel,
|
|
117
|
+
input: {
|
|
118
|
+
prompt,
|
|
119
|
+
aspect_ratio: ratio,
|
|
120
|
+
resolution,
|
|
121
|
+
output_format: outputFormat || 'jpg', // User selection or fallback to jpg
|
|
122
|
+
...(hasReferences ? { image_input: references } : {}),
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
} else if (resolvedModel.includes('google/nano-banana')) {
|
|
126
|
+
body = {
|
|
127
|
+
model: resolvedModel,
|
|
128
|
+
input: {
|
|
129
|
+
prompt,
|
|
130
|
+
image_size: ratio,
|
|
131
|
+
output_format: 'jpeg',
|
|
132
|
+
...(hasReferences ? { image_urls: references } : {}),
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
} else if (resolvedModel === 'midjourney') {
|
|
136
|
+
body = {
|
|
137
|
+
taskType: hasReferences ? 'mj_img2img' : 'mj_txt2img',
|
|
138
|
+
speed: 'fast',
|
|
139
|
+
prompt,
|
|
140
|
+
fileUrls: references,
|
|
141
|
+
aspectRatio: ratio,
|
|
142
|
+
enableTranslation: true,
|
|
143
|
+
};
|
|
144
|
+
} else {
|
|
145
|
+
body = {
|
|
146
|
+
model: requestedModel === 'gpt4o-image' ? 'gpt-4o' : 'bytedance/seedream-v4-edit',
|
|
147
|
+
input: {
|
|
148
|
+
image_urls: references,
|
|
149
|
+
prompt,
|
|
150
|
+
image_size: mapRatio('seedream', ratio),
|
|
151
|
+
output_format: 'jpeg',
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
158
155
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
156
|
+
return {
|
|
157
|
+
url: resolveGenerationUrl(resolvedModel),
|
|
158
|
+
body,
|
|
159
|
+
requestedModel,
|
|
160
|
+
resolvedModel,
|
|
161
|
+
ratio,
|
|
162
|
+
references,
|
|
163
|
+
hasReferences,
|
|
164
|
+
};
|
|
168
165
|
};
|
|
169
166
|
|
|
170
167
|
const extractReferences = (raw: unknown, model: string): string[] => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (model === 'seedream') limit = 14;
|
|
180
|
-
if (model === 'gpt4o-image') limit = 16;
|
|
181
|
-
if (model.includes('nano-banana')) limit = 10;
|
|
182
|
-
|
|
183
|
-
return items
|
|
184
|
-
.map((entry) => String(entry?.url || '').trim())
|
|
185
|
-
.filter((value) => value.length > 0 && /^https?:\/\//i.test(value))
|
|
186
|
-
.slice(0, limit);
|
|
168
|
+
if (!raw || typeof raw !== 'object') return [];
|
|
169
|
+
const collection = raw as { reference?: Array<{ url?: string }> };
|
|
170
|
+
const items = Array.isArray(collection.reference) ? collection.reference : [];
|
|
171
|
+
let limit = 8;
|
|
172
|
+
if (model === 'seedream') limit = 14;
|
|
173
|
+
if (model === 'gpt4o-image') limit = 16;
|
|
174
|
+
if (model.includes('nano-banana')) limit = 10;
|
|
175
|
+
return items.map((entry) => String(entry?.url || '').trim()).filter((v) => v.length > 0 && /^https?:\/\//i.test(v)).slice(0, limit);
|
|
187
176
|
};
|
|
188
177
|
|
|
189
178
|
export class CommandosImage implements INodeType {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
name: 'url',
|
|
179
|
+
description: INodeTypeDescription = {
|
|
180
|
+
displayName: 'Commandos Image',
|
|
181
|
+
name: 'commandosImage',
|
|
182
|
+
group: ['transform'],
|
|
183
|
+
version: 1,
|
|
184
|
+
description: 'Create image tasks in Commandos API',
|
|
185
|
+
defaults: { name: 'Commandos Image' },
|
|
186
|
+
icon: 'file:Image.png',
|
|
187
|
+
inputs: ['main'],
|
|
188
|
+
outputs: ['main'],
|
|
189
|
+
credentials: [{ name: 'commandosApi', required: true }],
|
|
190
|
+
properties: [
|
|
191
|
+
{
|
|
192
|
+
displayName: 'Operation',
|
|
193
|
+
name: 'operation',
|
|
194
|
+
type: 'options',
|
|
195
|
+
noDataExpression: true,
|
|
196
|
+
options: [
|
|
197
|
+
{ name: 'Create Task', value: 'create' },
|
|
198
|
+
{ name: 'Check Status', value: 'status' },
|
|
199
|
+
],
|
|
200
|
+
default: 'create',
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
displayName: 'Model',
|
|
204
|
+
name: 'model',
|
|
205
|
+
type: 'options',
|
|
206
|
+
options: [
|
|
207
|
+
{ name: 'Seedream 4.5', value: 'seedream' },
|
|
208
|
+
{ name: 'Flux Pro', value: 'flux-pro' },
|
|
209
|
+
{ name: 'GPT-4o Image', value: 'gpt4o-image' },
|
|
210
|
+
{ name: 'Nano Banana', value: 'nano-banana' },
|
|
211
|
+
{ name: 'Nano Banana Pro', value: 'nano-banana-pro' },
|
|
212
|
+
{ name: 'Midjourney', value: 'midjourney' },
|
|
213
|
+
],
|
|
214
|
+
default: 'seedream',
|
|
215
|
+
displayOptions: { show: { operation: ['create'] } },
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
displayName: 'Prompt',
|
|
219
|
+
name: 'prompt',
|
|
220
|
+
type: 'string',
|
|
221
|
+
default: '',
|
|
222
|
+
typeOptions: { rows: 4 },
|
|
223
|
+
displayOptions: { show: { operation: ['create'] } },
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
displayName: 'Ratio',
|
|
227
|
+
name: 'ratio',
|
|
228
|
+
type: 'options',
|
|
229
|
+
options: [
|
|
230
|
+
{ name: '1:1', value: '1:1' },
|
|
231
|
+
{ name: '2:3', value: '2:3' },
|
|
232
|
+
{ name: '3:2', value: '3:2' },
|
|
233
|
+
{ name: '4:5', value: '4:5' },
|
|
234
|
+
{ name: '16:9', value: '16:9' },
|
|
235
|
+
{ name: '9:16', value: '9:16' },
|
|
236
|
+
{ name: '4:3', value: '4:3' },
|
|
237
|
+
{ name: '3:4', value: '3:4' },
|
|
238
|
+
{ name: '21:9', value: '21:9' },
|
|
239
|
+
],
|
|
240
|
+
default: '2:3',
|
|
241
|
+
displayOptions: {
|
|
242
|
+
show: { operation: ['create'] },
|
|
243
|
+
hide: { model: ['gpt4o-image'] },
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
displayName: 'Ratio',
|
|
248
|
+
name: 'ratioGpt',
|
|
249
|
+
type: 'options',
|
|
250
|
+
options: [
|
|
251
|
+
{ name: '1:1', value: '1:1' },
|
|
252
|
+
{ name: '2:3', value: '2:3' },
|
|
253
|
+
{ name: '3:2', value: '3:2' },
|
|
254
|
+
],
|
|
255
|
+
default: '2:3',
|
|
256
|
+
displayOptions: {
|
|
257
|
+
show: { operation: ['create'], model: ['gpt4o-image'] },
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
displayName: 'Output Format',
|
|
262
|
+
name: 'outputFormatBananaPro',
|
|
263
|
+
type: 'options',
|
|
264
|
+
options: [
|
|
265
|
+
{ name: 'PNG', value: 'png' },
|
|
266
|
+
{ name: 'JPG', value: 'jpg' },
|
|
267
|
+
],
|
|
268
|
+
default: 'jpg',
|
|
269
|
+
displayOptions: {
|
|
270
|
+
show: { operation: ['create'], model: ['nano-banana-pro'] },
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
displayName: 'Quality',
|
|
275
|
+
name: 'quality',
|
|
276
|
+
type: 'options',
|
|
277
|
+
options: [
|
|
278
|
+
{ name: 'Basic (2K)', value: 'basic' },
|
|
279
|
+
{ name: 'High (4K)', value: 'high' },
|
|
280
|
+
{ name: 'Medium (Balanced)', value: 'medium' },
|
|
281
|
+
],
|
|
282
|
+
default: 'basic',
|
|
283
|
+
displayOptions: {
|
|
284
|
+
show: { operation: ['create'], model: ['seedream', 'gpt4o-image', 'flux-pro'] },
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
displayName: 'Resolution',
|
|
289
|
+
name: 'resolution',
|
|
290
|
+
type: 'options',
|
|
291
|
+
options: [
|
|
292
|
+
{ name: '1K', value: '1K' },
|
|
293
|
+
{ name: '2K', value: '2K' },
|
|
294
|
+
{ name: '4K', value: '4K' },
|
|
295
|
+
],
|
|
296
|
+
default: '1K',
|
|
297
|
+
displayOptions: {
|
|
298
|
+
show: { operation: ['create'], model: ['flux-pro', 'nano-banana-pro'] },
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
displayName: 'References',
|
|
303
|
+
name: 'references',
|
|
304
|
+
type: 'fixedCollection',
|
|
305
|
+
default: {},
|
|
306
|
+
typeOptions: { multipleValues: true },
|
|
307
|
+
options: [
|
|
308
|
+
{
|
|
309
|
+
name: 'reference',
|
|
310
|
+
displayName: 'Reference',
|
|
311
|
+
values: [{ displayName: 'Reference URL', name: 'url', type: 'string', default: '' }],
|
|
312
|
+
},
|
|
313
|
+
],
|
|
314
|
+
displayOptions: { show: { operation: ['create'] } },
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
displayName: 'Task ID',
|
|
318
|
+
name: 'taskId',
|
|
331
319
|
type: 'string',
|
|
332
320
|
default: '',
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
},
|
|
321
|
+
displayOptions: { show: { operation: ['status'] } },
|
|
322
|
+
},
|
|
336
323
|
],
|
|
337
|
-
|
|
338
|
-
show: {
|
|
339
|
-
operation: ['create'],
|
|
340
|
-
},
|
|
341
|
-
},
|
|
342
|
-
},
|
|
343
|
-
{
|
|
344
|
-
displayName: 'Task ID',
|
|
345
|
-
name: 'taskId',
|
|
346
|
-
type: 'string',
|
|
347
|
-
default: '',
|
|
348
|
-
displayOptions: {
|
|
349
|
-
show: {
|
|
350
|
-
operation: ['status'],
|
|
351
|
-
},
|
|
352
|
-
},
|
|
353
|
-
},
|
|
354
|
-
],
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
358
|
-
const items = this.getInputData();
|
|
359
|
-
const operation = this.getNodeParameter('operation', 0) as string;
|
|
360
|
-
const credentials = await this.getCredentials('commandosApi');
|
|
361
|
-
const licenseKey = String(credentials.licenseKey || '').trim();
|
|
362
|
-
|
|
363
|
-
if (!licenseKey) {
|
|
364
|
-
throw new NodeOperationError(this.getNode(), 'License key is required');
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
const results: INodeExecutionData[] = [];
|
|
368
|
-
|
|
369
|
-
for (let i = 0; i < items.length; i += 1) {
|
|
370
|
-
try {
|
|
371
|
-
if (operation === 'create') {
|
|
372
|
-
const model = String(this.getNodeParameter('model', i));
|
|
373
|
-
const prompt = String(this.getNodeParameter('prompt', i) || '').trim();
|
|
374
|
-
const ratio = String(this.getNodeParameter('ratio', i));
|
|
375
|
-
const references = extractReferences(this.getNodeParameter('references', i, {}), model);
|
|
376
|
-
|
|
377
|
-
const quality = ['seedream', 'gpt4o-image', 'flux-pro'].includes(model)
|
|
378
|
-
? String(this.getNodeParameter('quality', i, 'basic'))
|
|
379
|
-
: 'basic';
|
|
380
|
-
|
|
381
|
-
const resolution = ['flux-pro', 'nano-banana-pro'].includes(model)
|
|
382
|
-
? String(this.getNodeParameter('resolution', i, '1K'))
|
|
383
|
-
: '1K';
|
|
384
|
-
|
|
385
|
-
if (!prompt) {
|
|
386
|
-
throw new NodeOperationError(this.getNode(), 'Prompt is required', { itemIndex: i });
|
|
387
|
-
}
|
|
324
|
+
};
|
|
388
325
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
'URL генерации не настроен. Проверь переменные окружения COMMANDOS_IMAGE_URL_DEFAULT/COMMANDOS_IMAGE_URL_GPT4O/COMMANDOS_IMAGE_URL_MJ',
|
|
402
|
-
{ itemIndex: i },
|
|
403
|
-
);
|
|
404
|
-
}
|
|
326
|
+
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
327
|
+
const items = this.getInputData();
|
|
328
|
+
const operation = this.getNodeParameter('operation', 0) as string;
|
|
329
|
+
const credentials = await this.getCredentials('commandosApi');
|
|
330
|
+
const licenseKey = String(credentials.licenseKey || '').trim();
|
|
331
|
+
if (!licenseKey) throw new NodeOperationError(this.getNode(), 'License key is required');
|
|
332
|
+
const results: INodeExecutionData[] = [];
|
|
333
|
+
for (let i = 0; i < items.length; i += 1) {
|
|
334
|
+
try {
|
|
335
|
+
if (operation === 'create') {
|
|
336
|
+
const model = String(this.getNodeParameter('model', i));
|
|
337
|
+
const prompt = String(this.getNodeParameter('prompt', i) || '').trim();
|
|
405
338
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
references: request.references,
|
|
413
|
-
hasReferences: request.hasReferences,
|
|
414
|
-
_v: '0.1.8'
|
|
415
|
-
};
|
|
339
|
+
let ratio = '';
|
|
340
|
+
if (model === 'gpt4o-image') {
|
|
341
|
+
ratio = String(this.getNodeParameter('ratioGpt', i));
|
|
342
|
+
} else {
|
|
343
|
+
ratio = String(this.getNodeParameter('ratio', i));
|
|
344
|
+
}
|
|
416
345
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
'Content-Type': 'application/json',
|
|
422
|
-
'X-License-Key': licenseKey,
|
|
423
|
-
},
|
|
424
|
-
body: {
|
|
425
|
-
process_type: 'IMAGE_GENERATION',
|
|
426
|
-
payload,
|
|
427
|
-
},
|
|
428
|
-
json: true,
|
|
429
|
-
});
|
|
346
|
+
const references = extractReferences(this.getNodeParameter('references', i, {}), model);
|
|
347
|
+
const quality = ['seedream', 'gpt4o-image', 'flux-pro'].includes(model) ? String(this.getNodeParameter('quality', i, 'basic')) : 'basic';
|
|
348
|
+
const resolution = ['flux-pro', 'nano-banana-pro'].includes(model) ? String(this.getNodeParameter('resolution', i, '1K')) : '1K';
|
|
349
|
+
const outputFormat = (model === 'nano-banana-pro') ? String(this.getNodeParameter('outputFormatBananaPro', i, 'jpg')) : undefined;
|
|
430
350
|
|
|
431
|
-
|
|
432
|
-
} else if (operation === 'status') {
|
|
433
|
-
const taskId = String(this.getNodeParameter('taskId', i) || '').trim();
|
|
434
|
-
if (!taskId) {
|
|
435
|
-
throw new NodeOperationError(this.getNode(), 'Task ID is required', { itemIndex: i });
|
|
436
|
-
}
|
|
351
|
+
if (!prompt) throw new NodeOperationError(this.getNode(), 'Prompt is required', { itemIndex: i });
|
|
437
352
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
353
|
+
const request = buildGenerationRequest({ model, prompt, ratio, references, quality, resolution, outputFormat });
|
|
354
|
+
const payload = { ...request, licenseKey, _v: '0.1.10' };
|
|
355
|
+
const response = await this.helpers.request({
|
|
356
|
+
method: 'POST',
|
|
357
|
+
url: `${COMMANDOS_API_URL}/tasks`,
|
|
358
|
+
headers: { 'Content-Type': 'application/json', 'X-License-Key': licenseKey },
|
|
359
|
+
body: { process_type: 'IMAGE_GENERATION', payload },
|
|
360
|
+
json: true,
|
|
361
|
+
});
|
|
362
|
+
results.push({ json: response as IDataObject });
|
|
363
|
+
} else if (operation === 'status') {
|
|
364
|
+
const taskId = String(this.getNodeParameter('taskId', i) || '').trim();
|
|
365
|
+
if (!taskId) throw new NodeOperationError(this.getNode(), 'Task ID is required', { itemIndex: i });
|
|
366
|
+
const response = await this.helpers.request({
|
|
367
|
+
method: 'GET',
|
|
368
|
+
url: `${COMMANDOS_API_URL}/tasks/${encodeURIComponent(taskId)}`,
|
|
369
|
+
headers: { 'X-License-Key': licenseKey },
|
|
370
|
+
json: true,
|
|
371
|
+
});
|
|
372
|
+
results.push({ json: response as IDataObject });
|
|
373
|
+
}
|
|
374
|
+
} catch (error) {
|
|
375
|
+
if (error instanceof NodeOperationError) throw error;
|
|
376
|
+
const message = error instanceof Error ? error.message : 'Request failed';
|
|
377
|
+
throw new NodeOperationError(this.getNode(), message, { itemIndex: i });
|
|
378
|
+
}
|
|
452
379
|
}
|
|
453
|
-
|
|
454
|
-
throw new NodeOperationError(this.getNode(), message, { itemIndex: i });
|
|
455
|
-
}
|
|
380
|
+
return [results];
|
|
456
381
|
}
|
|
457
|
-
|
|
458
|
-
return [results];
|
|
459
|
-
}
|
|
460
382
|
}
|