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