n8n-nodes-commandos-image 0.1.8 → 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')
|
|
@@ -31,7 +28,6 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
|
|
|
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', // Force 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', // Force 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: 'jpg', // Force jpg for pro
|
|
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', // Force 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', // Default jpeg
|
|
129
123
|
},
|
|
130
124
|
};
|
|
131
125
|
}
|
|
@@ -145,7 +139,6 @@ const extractReferences = (raw, model) => {
|
|
|
145
139
|
}
|
|
146
140
|
const collection = raw;
|
|
147
141
|
const items = Array.isArray(collection.reference) ? collection.reference : [];
|
|
148
|
-
// Dynamic limit based on model docs
|
|
149
142
|
let limit = 8;
|
|
150
143
|
if (model === 'seedream')
|
|
151
144
|
limit = 14;
|
|
@@ -185,14 +178,8 @@ class CommandosImage {
|
|
|
185
178
|
type: 'options',
|
|
186
179
|
noDataExpression: true,
|
|
187
180
|
options: [
|
|
188
|
-
{
|
|
189
|
-
|
|
190
|
-
value: 'create',
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
name: 'Check Status',
|
|
194
|
-
value: 'status',
|
|
195
|
-
},
|
|
181
|
+
{ name: 'Create Task', value: 'create' },
|
|
182
|
+
{ name: 'Check Status', value: 'status' },
|
|
196
183
|
],
|
|
197
184
|
default: 'create',
|
|
198
185
|
},
|
|
@@ -209,25 +196,15 @@ class CommandosImage {
|
|
|
209
196
|
{ name: 'Midjourney', value: 'midjourney' },
|
|
210
197
|
],
|
|
211
198
|
default: 'seedream',
|
|
212
|
-
displayOptions: {
|
|
213
|
-
show: {
|
|
214
|
-
operation: ['create'],
|
|
215
|
-
},
|
|
216
|
-
},
|
|
199
|
+
displayOptions: { show: { operation: ['create'] } },
|
|
217
200
|
},
|
|
218
201
|
{
|
|
219
202
|
displayName: 'Prompt',
|
|
220
203
|
name: 'prompt',
|
|
221
204
|
type: 'string',
|
|
222
205
|
default: '',
|
|
223
|
-
typeOptions: {
|
|
224
|
-
|
|
225
|
-
},
|
|
226
|
-
displayOptions: {
|
|
227
|
-
show: {
|
|
228
|
-
operation: ['create'],
|
|
229
|
-
},
|
|
230
|
-
},
|
|
206
|
+
typeOptions: { rows: 4 },
|
|
207
|
+
displayOptions: { show: { operation: ['create'] } },
|
|
231
208
|
},
|
|
232
209
|
{
|
|
233
210
|
displayName: 'Ratio',
|
|
@@ -245,11 +222,7 @@ class CommandosImage {
|
|
|
245
222
|
{ name: '21:9', value: '21:9' },
|
|
246
223
|
],
|
|
247
224
|
default: '2:3',
|
|
248
|
-
displayOptions: {
|
|
249
|
-
show: {
|
|
250
|
-
operation: ['create'],
|
|
251
|
-
},
|
|
252
|
-
},
|
|
225
|
+
displayOptions: { show: { operation: ['create'] } },
|
|
253
226
|
},
|
|
254
227
|
{
|
|
255
228
|
displayName: 'Quality',
|
|
@@ -290,9 +263,7 @@ class CommandosImage {
|
|
|
290
263
|
name: 'references',
|
|
291
264
|
type: 'fixedCollection',
|
|
292
265
|
default: {},
|
|
293
|
-
typeOptions: {
|
|
294
|
-
multipleValues: true,
|
|
295
|
-
},
|
|
266
|
+
typeOptions: { multipleValues: true },
|
|
296
267
|
options: [
|
|
297
268
|
{
|
|
298
269
|
name: 'reference',
|
|
@@ -307,22 +278,14 @@ class CommandosImage {
|
|
|
307
278
|
],
|
|
308
279
|
},
|
|
309
280
|
],
|
|
310
|
-
displayOptions: {
|
|
311
|
-
show: {
|
|
312
|
-
operation: ['create'],
|
|
313
|
-
},
|
|
314
|
-
},
|
|
281
|
+
displayOptions: { show: { operation: ['create'] } },
|
|
315
282
|
},
|
|
316
283
|
{
|
|
317
284
|
displayName: 'Task ID',
|
|
318
285
|
name: 'taskId',
|
|
319
286
|
type: 'string',
|
|
320
287
|
default: '',
|
|
321
|
-
displayOptions: {
|
|
322
|
-
show: {
|
|
323
|
-
operation: ['status'],
|
|
324
|
-
},
|
|
325
|
-
},
|
|
288
|
+
displayOptions: { show: { operation: ['status'] } },
|
|
326
289
|
},
|
|
327
290
|
],
|
|
328
291
|
};
|
|
@@ -352,17 +315,7 @@ class CommandosImage {
|
|
|
352
315
|
if (!prompt) {
|
|
353
316
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Prompt is required', { itemIndex: i });
|
|
354
317
|
}
|
|
355
|
-
const request = buildGenerationRequest({
|
|
356
|
-
model,
|
|
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 });
|
|
365
|
-
}
|
|
318
|
+
const request = buildGenerationRequest({ model, prompt, ratio, references, quality, resolution });
|
|
366
319
|
const payload = {
|
|
367
320
|
url: request.url,
|
|
368
321
|
body: request.body,
|
|
@@ -371,7 +324,7 @@ class CommandosImage {
|
|
|
371
324
|
ratio: request.ratio,
|
|
372
325
|
references: request.references,
|
|
373
326
|
hasReferences: request.hasReferences,
|
|
374
|
-
_v: '0.1.
|
|
327
|
+
_v: '0.1.9'
|
|
375
328
|
};
|
|
376
329
|
const response = await this.helpers.request({
|
|
377
330
|
method: 'POST',
|
|
@@ -380,10 +333,7 @@ class CommandosImage {
|
|
|
380
333
|
'Content-Type': 'application/json',
|
|
381
334
|
'X-License-Key': licenseKey,
|
|
382
335
|
},
|
|
383
|
-
body: {
|
|
384
|
-
process_type: 'IMAGE_GENERATION',
|
|
385
|
-
payload,
|
|
386
|
-
},
|
|
336
|
+
body: { process_type: 'IMAGE_GENERATION', payload },
|
|
387
337
|
json: true,
|
|
388
338
|
});
|
|
389
339
|
results.push({ json: response });
|
|
@@ -396,18 +346,15 @@ class CommandosImage {
|
|
|
396
346
|
const response = await this.helpers.request({
|
|
397
347
|
method: 'GET',
|
|
398
348
|
url: `${COMMANDOS_API_URL}/tasks/${encodeURIComponent(taskId)}`,
|
|
399
|
-
headers: {
|
|
400
|
-
'X-License-Key': licenseKey,
|
|
401
|
-
},
|
|
349
|
+
headers: { 'X-License-Key': licenseKey },
|
|
402
350
|
json: true,
|
|
403
351
|
});
|
|
404
352
|
results.push({ json: response });
|
|
405
353
|
}
|
|
406
354
|
}
|
|
407
355
|
catch (error) {
|
|
408
|
-
if (error instanceof n8n_workflow_1.NodeOperationError)
|
|
356
|
+
if (error instanceof n8n_workflow_1.NodeOperationError)
|
|
409
357
|
throw error;
|
|
410
|
-
}
|
|
411
358
|
const message = error instanceof Error ? error.message : 'Request failed';
|
|
412
359
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), message, { itemIndex: i });
|
|
413
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,448 +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
|
-
|
|
20
|
+
model: string;
|
|
21
|
+
prompt: string;
|
|
22
|
+
ratio: string;
|
|
23
|
+
references: string[];
|
|
24
|
+
quality?: string;
|
|
25
|
+
resolution?: string;
|
|
22
26
|
};
|
|
23
27
|
|
|
24
28
|
type BuildResult = {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
url: string;
|
|
30
|
+
body: Record<string, unknown>;
|
|
31
|
+
requestedModel: string;
|
|
32
|
+
resolvedModel: string;
|
|
33
|
+
ratio: string;
|
|
34
|
+
references: string[];
|
|
35
|
+
hasReferences: boolean;
|
|
32
36
|
};
|
|
33
37
|
|
|
34
38
|
const resolveGenerationUrl = (resolvedModel: string): string => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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;
|
|
42
46
|
};
|
|
43
47
|
|
|
44
|
-
/**
|
|
45
|
-
* Maps standard ratios to specific model requirements
|
|
46
|
-
*/
|
|
47
48
|
const mapRatio = (model: string, ratio: string): string => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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;
|
|
53
54
|
};
|
|
54
55
|
|
|
55
56
|
const buildGenerationRequest = ({
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
model,
|
|
58
|
+
prompt,
|
|
59
|
+
ratio,
|
|
60
|
+
references,
|
|
61
|
+
quality = 'basic',
|
|
62
|
+
resolution = '1K',
|
|
62
63
|
}: BuildParams): BuildResult => {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
const hasReferences = references.length > 0;
|
|
65
|
+
const requestedModel = model;
|
|
66
|
+
let resolvedModel = model;
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
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
|
+
}
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
} else if (requestedModel === 'midjourney') {
|
|
83
|
-
resolvedModel = 'midjourney';
|
|
78
|
+
if (!hasReferences && (requestedModel === 'seedream' || requestedModel === 'midjourney')) {
|
|
79
|
+
if (requestedModel === 'midjourney') {
|
|
80
|
+
resolvedModel = 'midjourney';
|
|
81
|
+
}
|
|
84
82
|
}
|
|
85
|
-
}
|
|
86
83
|
|
|
87
|
-
|
|
84
|
+
let body: Record<string, unknown> = {};
|
|
88
85
|
|
|
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
|
-
|
|
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
|
+
}
|
|
158
157
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
158
|
+
return {
|
|
159
|
+
url: resolveGenerationUrl(resolvedModel),
|
|
160
|
+
body,
|
|
161
|
+
requestedModel,
|
|
162
|
+
resolvedModel,
|
|
163
|
+
ratio,
|
|
164
|
+
references,
|
|
165
|
+
hasReferences,
|
|
166
|
+
};
|
|
168
167
|
};
|
|
169
168
|
|
|
170
169
|
const extractReferences = (raw: unknown, model: string): string[] => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// Dynamic limit based on model docs
|
|
178
|
-
let limit = 8;
|
|
179
|
-
if (model === 'seedream') limit = 14;
|
|
180
|
-
if (model === 'gpt4o-image') limit = 16;
|
|
181
|
-
if (model.includes('nano-banana')) limit = 10;
|
|
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 : [];
|
|
182
175
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
.
|
|
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);
|
|
187
185
|
};
|
|
188
186
|
|
|
189
187
|
export class CommandosImage implements INodeType {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
},
|
|
199
|
-
icon: 'file:Image.png',
|
|
200
|
-
inputs: ['main'],
|
|
201
|
-
outputs: ['main'],
|
|
202
|
-
credentials: [
|
|
203
|
-
{
|
|
204
|
-
name: 'commandosApi',
|
|
205
|
-
required: true,
|
|
206
|
-
},
|
|
207
|
-
],
|
|
208
|
-
properties: [
|
|
209
|
-
{
|
|
210
|
-
displayName: 'Operation',
|
|
211
|
-
name: 'operation',
|
|
212
|
-
type: 'options',
|
|
213
|
-
noDataExpression: true,
|
|
214
|
-
options: [
|
|
215
|
-
{
|
|
216
|
-
name: 'Create Task',
|
|
217
|
-
value: 'create',
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
name: 'Check Status',
|
|
221
|
-
value: 'status',
|
|
222
|
-
},
|
|
223
|
-
],
|
|
224
|
-
default: 'create',
|
|
225
|
-
},
|
|
226
|
-
{
|
|
227
|
-
displayName: 'Model',
|
|
228
|
-
name: 'model',
|
|
229
|
-
type: 'options',
|
|
230
|
-
options: [
|
|
231
|
-
{ name: 'Seedream 4.5', value: 'seedream' },
|
|
232
|
-
{ name: 'Flux Pro', value: 'flux-pro' },
|
|
233
|
-
{ name: 'GPT-4o Image', value: 'gpt4o-image' },
|
|
234
|
-
{ name: 'Nano Banana', value: 'nano-banana' },
|
|
235
|
-
{ name: 'Nano Banana Pro', value: 'nano-banana-pro' },
|
|
236
|
-
{ name: 'Midjourney', value: 'midjourney' },
|
|
237
|
-
],
|
|
238
|
-
default: 'seedream',
|
|
239
|
-
displayOptions: {
|
|
240
|
-
show: {
|
|
241
|
-
operation: ['create'],
|
|
242
|
-
},
|
|
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',
|
|
243
196
|
},
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
},
|
|
253
|
-
displayOptions: {
|
|
254
|
-
show: {
|
|
255
|
-
operation: ['create'],
|
|
256
|
-
},
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
{
|
|
260
|
-
displayName: 'Ratio',
|
|
261
|
-
name: 'ratio',
|
|
262
|
-
type: 'options',
|
|
263
|
-
options: [
|
|
264
|
-
{ name: '1:1', value: '1:1' },
|
|
265
|
-
{ name: '2:3', value: '2:3' },
|
|
266
|
-
{ name: '3:2', value: '3:2' },
|
|
267
|
-
{ name: '4:5', value: '4:5' },
|
|
268
|
-
{ name: '16:9', value: '16:9' },
|
|
269
|
-
{ name: '9:16', value: '9:16' },
|
|
270
|
-
{ name: '4:3', value: '4:3' },
|
|
271
|
-
{ name: '3:4', value: '3:4' },
|
|
272
|
-
{ name: '21:9', value: '21:9' },
|
|
273
|
-
],
|
|
274
|
-
default: '2:3',
|
|
275
|
-
displayOptions: {
|
|
276
|
-
show: {
|
|
277
|
-
operation: ['create'],
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
displayName: 'Quality',
|
|
283
|
-
name: 'quality',
|
|
284
|
-
type: 'options',
|
|
285
|
-
options: [
|
|
286
|
-
{ name: 'Basic (2K)', value: 'basic' },
|
|
287
|
-
{ name: 'High (4K)', value: 'high' },
|
|
288
|
-
{ name: 'Medium (Balanced)', value: 'medium' },
|
|
289
|
-
],
|
|
290
|
-
default: 'basic',
|
|
291
|
-
displayOptions: {
|
|
292
|
-
show: {
|
|
293
|
-
operation: ['create'],
|
|
294
|
-
model: ['seedream', 'gpt4o-image', 'flux-pro'],
|
|
295
|
-
},
|
|
296
|
-
},
|
|
297
|
-
},
|
|
298
|
-
{
|
|
299
|
-
displayName: 'Resolution',
|
|
300
|
-
name: 'resolution',
|
|
301
|
-
type: 'options',
|
|
302
|
-
options: [
|
|
303
|
-
{ name: '1K', value: '1K' },
|
|
304
|
-
{ name: '2K', value: '2K' },
|
|
305
|
-
{ 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
|
+
},
|
|
306
205
|
],
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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',
|
|
236
|
+
type: 'string',
|
|
237
|
+
default: '',
|
|
238
|
+
typeOptions: { rows: 4 },
|
|
239
|
+
displayOptions: { show: { operation: ['create'] } },
|
|
240
|
+
},
|
|
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'] } },
|
|
258
|
+
},
|
|
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'] } },
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
displayName: 'Task ID',
|
|
317
|
+
name: 'taskId',
|
|
331
318
|
type: 'string',
|
|
332
319
|
default: '',
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
},
|
|
320
|
+
displayOptions: { show: { operation: ['status'] } },
|
|
321
|
+
},
|
|
336
322
|
],
|
|
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
|
-
};
|
|
323
|
+
};
|
|
356
324
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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();
|
|
362
330
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
331
|
+
if (!licenseKey) {
|
|
332
|
+
throw new NodeOperationError(this.getNode(), 'License key is required');
|
|
333
|
+
}
|
|
366
334
|
|
|
367
|
-
|
|
335
|
+
const results: INodeExecutionData[] = [];
|
|
368
336
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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);
|
|
376
344
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
345
|
+
const quality = ['seedream', 'gpt4o-image', 'flux-pro'].includes(model)
|
|
346
|
+
? String(this.getNodeParameter('quality', i, 'basic'))
|
|
347
|
+
: 'basic';
|
|
380
348
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
349
|
+
const resolution = ['flux-pro', 'nano-banana-pro'].includes(model)
|
|
350
|
+
? String(this.getNodeParameter('resolution', i, '1K'))
|
|
351
|
+
: '1K';
|
|
384
352
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
353
|
+
if (!prompt) {
|
|
354
|
+
throw new NodeOperationError(this.getNode(), 'Prompt is required', { itemIndex: i });
|
|
355
|
+
}
|
|
388
356
|
|
|
389
|
-
|
|
390
|
-
model,
|
|
391
|
-
prompt,
|
|
392
|
-
ratio,
|
|
393
|
-
references,
|
|
394
|
-
quality,
|
|
395
|
-
resolution
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
if (!request.url) {
|
|
399
|
-
throw new NodeOperationError(
|
|
400
|
-
this.getNode(),
|
|
401
|
-
'URL генерации не настроен. Проверь переменные окружения COMMANDOS_IMAGE_URL_DEFAULT/COMMANDOS_IMAGE_URL_GPT4O/COMMANDOS_IMAGE_URL_MJ',
|
|
402
|
-
{ itemIndex: i },
|
|
403
|
-
);
|
|
404
|
-
}
|
|
357
|
+
const request = buildGenerationRequest({ model, prompt, ratio, references, quality, resolution });
|
|
405
358
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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
|
+
};
|
|
416
369
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
},
|
|
428
|
-
json: true,
|
|
429
|
-
});
|
|
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
|
+
});
|
|
430
380
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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
|
+
}
|
|
437
387
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
json: true,
|
|
445
|
-
});
|
|
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
|
+
});
|
|
446
394
|
|
|
447
|
-
|
|
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
|
+
}
|
|
448
402
|
}
|
|
449
|
-
} catch (error) {
|
|
450
|
-
if (error instanceof NodeOperationError) {
|
|
451
|
-
throw error;
|
|
452
|
-
}
|
|
453
|
-
const message = error instanceof Error ? error.message : 'Request failed';
|
|
454
|
-
throw new NodeOperationError(this.getNode(), message, { itemIndex: i });
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
403
|
|
|
458
|
-
|
|
459
|
-
|
|
404
|
+
return [results];
|
|
405
|
+
}
|
|
460
406
|
}
|