n8n-nodes-commandos-image 0.1.9 → 0.1.11

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,7 +1,3 @@
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
- */
5
1
  import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
6
2
  export declare class CommandosImage implements INodeType {
7
3
  description: INodeTypeDescription;
@@ -16,12 +16,22 @@ const resolveGenerationUrl = (resolvedModel) => {
16
16
  return GENERATION_URL_DEFAULT;
17
17
  };
18
18
  const mapRatio = (model, ratio) => {
19
- if (model.includes('seedream')) {
19
+ const isSeedream = model.includes('seedream');
20
+ const isGpt = model === 'gpt4o-image';
21
+ if (isSeedream) {
20
22
  if (ratio === '2:3')
21
23
  return 'portrait_3_2';
22
24
  if (ratio === '3:2')
23
25
  return 'landscape_2_3';
24
26
  }
27
+ if (isGpt) {
28
+ if (ratio === '1:1')
29
+ return '1024x1024';
30
+ if (ratio === '2:3')
31
+ return '1024x1792';
32
+ if (ratio === '3:2')
33
+ return '1792x1024';
34
+ }
25
35
  return ratio;
26
36
  };
27
37
  const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'basic', resolution = '1K', }) => {
@@ -50,9 +60,9 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
50
60
  body = {
51
61
  filesUrl: references,
52
62
  prompt,
53
- size: ratio,
54
- callBackUrl: 'https://api.comandos.ai/internal/kie/callback',
63
+ size: mapRatio(resolvedModel, ratio),
55
64
  fallbackModel: 'FLUX_MAX',
65
+ // callBackUrl is handled by server automatically
56
66
  };
57
67
  }
58
68
  else if (resolvedModel.includes('flux-2/')) {
@@ -62,7 +72,7 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
62
72
  prompt,
63
73
  aspect_ratio: ratio,
64
74
  resolution,
65
- output_format: 'jpeg', // Force jpeg
75
+ output_format: 'jpeg', // Force lightweight
66
76
  ...(hasReferences ? { input_urls: references } : {}),
67
77
  },
68
78
  };
@@ -74,7 +84,7 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
74
84
  prompt,
75
85
  aspect_ratio: mapRatio(resolvedModel, ratio),
76
86
  quality,
77
- output_format: 'jpeg', // Force jpeg
87
+ output_format: 'jpeg', // Force lightweight
78
88
  ...(hasReferences ? { image_urls: references } : {}),
79
89
  },
80
90
  };
@@ -86,7 +96,7 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
86
96
  prompt,
87
97
  aspect_ratio: ratio,
88
98
  resolution,
89
- output_format: 'jpg', // Force jpg for pro
99
+ output_format: 'jpg', // Force lightweight
90
100
  ...(hasReferences ? { image_input: references } : {}),
91
101
  },
92
102
  };
@@ -97,7 +107,7 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
97
107
  input: {
98
108
  prompt,
99
109
  image_size: ratio,
100
- output_format: 'jpeg', // Force jpeg
110
+ output_format: 'jpeg', // Force lightweight
101
111
  ...(hasReferences ? { image_urls: references } : {}),
102
112
  },
103
113
  };
@@ -119,7 +129,7 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
119
129
  image_urls: references,
120
130
  prompt,
121
131
  image_size: mapRatio('seedream', ratio),
122
- output_format: 'jpeg', // Default jpeg
132
+ output_format: 'jpeg',
123
133
  },
124
134
  };
125
135
  }
@@ -134,9 +144,8 @@ const buildGenerationRequest = ({ model, prompt, ratio, references, quality = 'b
134
144
  };
135
145
  };
136
146
  const extractReferences = (raw, model) => {
137
- if (!raw || typeof raw !== 'object') {
147
+ if (!raw || typeof raw !== 'object')
138
148
  return [];
139
- }
140
149
  const collection = raw;
141
150
  const items = Array.isArray(collection.reference) ? collection.reference : [];
142
151
  let limit = 8;
@@ -146,10 +155,7 @@ const extractReferences = (raw, model) => {
146
155
  limit = 16;
147
156
  if (model.includes('nano-banana'))
148
157
  limit = 10;
149
- return items
150
- .map((entry) => String((entry === null || entry === void 0 ? void 0 : entry.url) || '').trim())
151
- .filter((value) => value.length > 0 && /^https?:\/\//i.test(value))
152
- .slice(0, limit);
158
+ 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);
153
159
  };
154
160
  class CommandosImage {
155
161
  constructor() {
@@ -159,18 +165,11 @@ class CommandosImage {
159
165
  group: ['transform'],
160
166
  version: 1,
161
167
  description: 'Create image tasks in Commandos API',
162
- defaults: {
163
- name: 'Commandos Image',
164
- },
168
+ defaults: { name: 'Commandos Image' },
165
169
  icon: 'file:Image.png',
166
170
  inputs: ['main'],
167
171
  outputs: ['main'],
168
- credentials: [
169
- {
170
- name: 'commandosApi',
171
- required: true,
172
- },
173
- ],
172
+ credentials: [{ name: 'commandosApi', required: true }],
174
173
  properties: [
175
174
  {
176
175
  displayName: 'Operation',
@@ -222,7 +221,24 @@ class CommandosImage {
222
221
  { name: '21:9', value: '21:9' },
223
222
  ],
224
223
  default: '2:3',
225
- displayOptions: { show: { operation: ['create'] } },
224
+ displayOptions: {
225
+ show: { operation: ['create'] },
226
+ hide: { model: ['gpt4o-image'] },
227
+ },
228
+ },
229
+ {
230
+ displayName: 'Ratio',
231
+ name: 'ratioGpt',
232
+ type: 'options',
233
+ options: [
234
+ { name: '1:1', value: '1:1' },
235
+ { name: '2:3', value: '2:3' },
236
+ { name: '3:2', value: '3:2' },
237
+ ],
238
+ default: '2:3',
239
+ displayOptions: {
240
+ show: { operation: ['create'], model: ['gpt4o-image'] },
241
+ },
226
242
  },
227
243
  {
228
244
  displayName: 'Quality',
@@ -235,10 +251,7 @@ class CommandosImage {
235
251
  ],
236
252
  default: 'basic',
237
253
  displayOptions: {
238
- show: {
239
- operation: ['create'],
240
- model: ['seedream', 'gpt4o-image', 'flux-pro'],
241
- },
254
+ show: { operation: ['create'], model: ['seedream', 'gpt4o-image', 'flux-pro'] },
242
255
  },
243
256
  },
244
257
  {
@@ -252,10 +265,7 @@ class CommandosImage {
252
265
  ],
253
266
  default: '1K',
254
267
  displayOptions: {
255
- show: {
256
- operation: ['create'],
257
- model: ['flux-pro', 'nano-banana-pro'],
258
- },
268
+ show: { operation: ['create'], model: ['flux-pro', 'nano-banana-pro'] },
259
269
  },
260
270
  },
261
271
  {
@@ -268,14 +278,7 @@ class CommandosImage {
268
278
  {
269
279
  name: 'reference',
270
280
  displayName: 'Reference',
271
- values: [
272
- {
273
- displayName: 'Reference URL',
274
- name: 'url',
275
- type: 'string',
276
- default: '',
277
- },
278
- ],
281
+ values: [{ displayName: 'Reference URL', name: 'url', type: 'string', default: '' }],
279
282
  },
280
283
  ],
281
284
  displayOptions: { show: { operation: ['create'] } },
@@ -295,44 +298,32 @@ class CommandosImage {
295
298
  const operation = this.getNodeParameter('operation', 0);
296
299
  const credentials = await this.getCredentials('commandosApi');
297
300
  const licenseKey = String(credentials.licenseKey || '').trim();
298
- if (!licenseKey) {
301
+ if (!licenseKey)
299
302
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'License key is required');
300
- }
301
303
  const results = [];
302
304
  for (let i = 0; i < items.length; i += 1) {
303
305
  try {
304
306
  if (operation === 'create') {
305
307
  const model = String(this.getNodeParameter('model', i));
306
308
  const prompt = String(this.getNodeParameter('prompt', i) || '').trim();
307
- const ratio = String(this.getNodeParameter('ratio', i));
309
+ let ratio = '';
310
+ if (model === 'gpt4o-image') {
311
+ ratio = String(this.getNodeParameter('ratioGpt', i));
312
+ }
313
+ else {
314
+ ratio = String(this.getNodeParameter('ratio', i));
315
+ }
308
316
  const references = extractReferences(this.getNodeParameter('references', i, {}), model);
309
- const quality = ['seedream', 'gpt4o-image', 'flux-pro'].includes(model)
310
- ? String(this.getNodeParameter('quality', i, 'basic'))
311
- : 'basic';
312
- const resolution = ['flux-pro', 'nano-banana-pro'].includes(model)
313
- ? String(this.getNodeParameter('resolution', i, '1K'))
314
- : '1K';
315
- if (!prompt) {
317
+ const quality = ['seedream', 'gpt4o-image', 'flux-pro'].includes(model) ? String(this.getNodeParameter('quality', i, 'basic')) : 'basic';
318
+ const resolution = ['flux-pro', 'nano-banana-pro'].includes(model) ? String(this.getNodeParameter('resolution', i, '1K')) : '1K';
319
+ if (!prompt)
316
320
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Prompt is required', { itemIndex: i });
317
- }
318
321
  const request = buildGenerationRequest({ model, prompt, ratio, references, quality, resolution });
319
- const payload = {
320
- url: request.url,
321
- body: request.body,
322
- requestedModel: request.requestedModel,
323
- resolvedModel: request.resolvedModel,
324
- ratio: request.ratio,
325
- references: request.references,
326
- hasReferences: request.hasReferences,
327
- _v: '0.1.9'
328
- };
322
+ const payload = { ...request, licenseKey, _v: '0.1.11' };
329
323
  const response = await this.helpers.request({
330
324
  method: 'POST',
331
325
  url: `${COMMANDOS_API_URL}/tasks`,
332
- headers: {
333
- 'Content-Type': 'application/json',
334
- 'X-License-Key': licenseKey,
335
- },
326
+ headers: { 'Content-Type': 'application/json', 'X-License-Key': licenseKey },
336
327
  body: { process_type: 'IMAGE_GENERATION', payload },
337
328
  json: true,
338
329
  });
@@ -340,9 +331,8 @@ class CommandosImage {
340
331
  }
341
332
  else if (operation === 'status') {
342
333
  const taskId = String(this.getNodeParameter('taskId', i) || '').trim();
343
- if (!taskId) {
334
+ if (!taskId)
344
335
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Task ID is required', { itemIndex: i });
345
- }
346
336
  const response = await this.helpers.request({
347
337
  method: 'GET',
348
338
  url: `${COMMANDOS_API_URL}/tasks/${encodeURIComponent(taskId)}`,
@@ -1,7 +1,3 @@
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
- */
5
1
  import type {
6
2
  IDataObject,
7
3
  IExecuteFunctions,
@@ -46,10 +42,20 @@ const resolveGenerationUrl = (resolvedModel: string): string => {
46
42
  };
47
43
 
48
44
  const mapRatio = (model: string, ratio: string): string => {
49
- if (model.includes('seedream')) {
45
+ const isSeedream = model.includes('seedream');
46
+ const isGpt = model === 'gpt4o-image';
47
+
48
+ if (isSeedream) {
50
49
  if (ratio === '2:3') return 'portrait_3_2';
51
50
  if (ratio === '3:2') return 'landscape_2_3';
52
51
  }
52
+
53
+ if (isGpt) {
54
+ if (ratio === '1:1') return '1024x1024';
55
+ if (ratio === '2:3') return '1024x1792';
56
+ if (ratio === '3:2') return '1792x1024';
57
+ }
58
+
53
59
  return ratio;
54
60
  };
55
61
 
@@ -87,9 +93,9 @@ const buildGenerationRequest = ({
87
93
  body = {
88
94
  filesUrl: references,
89
95
  prompt,
90
- size: ratio,
91
- callBackUrl: 'https://api.comandos.ai/internal/kie/callback',
96
+ size: mapRatio(resolvedModel, ratio),
92
97
  fallbackModel: 'FLUX_MAX',
98
+ // callBackUrl is handled by server automatically
93
99
  };
94
100
  } else if (resolvedModel.includes('flux-2/')) {
95
101
  body = {
@@ -98,7 +104,7 @@ const buildGenerationRequest = ({
98
104
  prompt,
99
105
  aspect_ratio: ratio,
100
106
  resolution,
101
- output_format: 'jpeg', // Force jpeg
107
+ output_format: 'jpeg', // Force lightweight
102
108
  ...(hasReferences ? { input_urls: references } : {}),
103
109
  },
104
110
  };
@@ -109,7 +115,7 @@ const buildGenerationRequest = ({
109
115
  prompt,
110
116
  aspect_ratio: mapRatio(resolvedModel, ratio),
111
117
  quality,
112
- output_format: 'jpeg', // Force jpeg
118
+ output_format: 'jpeg', // Force lightweight
113
119
  ...(hasReferences ? { image_urls: references } : {}),
114
120
  },
115
121
  };
@@ -120,7 +126,7 @@ const buildGenerationRequest = ({
120
126
  prompt,
121
127
  aspect_ratio: ratio,
122
128
  resolution,
123
- output_format: 'jpg', // Force jpg for pro
129
+ output_format: 'jpg', // Force lightweight
124
130
  ...(hasReferences ? { image_input: references } : {}),
125
131
  },
126
132
  };
@@ -130,7 +136,7 @@ const buildGenerationRequest = ({
130
136
  input: {
131
137
  prompt,
132
138
  image_size: ratio,
133
- output_format: 'jpeg', // Force jpeg
139
+ output_format: 'jpeg', // Force lightweight
134
140
  ...(hasReferences ? { image_urls: references } : {}),
135
141
  },
136
142
  };
@@ -150,7 +156,7 @@ const buildGenerationRequest = ({
150
156
  image_urls: references,
151
157
  prompt,
152
158
  image_size: mapRatio('seedream', ratio),
153
- output_format: 'jpeg', // Default jpeg
159
+ output_format: 'jpeg',
154
160
  },
155
161
  };
156
162
  }
@@ -167,21 +173,14 @@ const buildGenerationRequest = ({
167
173
  };
168
174
 
169
175
  const extractReferences = (raw: unknown, model: string): string[] => {
170
- if (!raw || typeof raw !== 'object') {
171
- return [];
172
- }
176
+ if (!raw || typeof raw !== 'object') return [];
173
177
  const collection = raw as { reference?: Array<{ url?: string }> };
174
178
  const items = Array.isArray(collection.reference) ? collection.reference : [];
175
-
176
179
  let limit = 8;
177
180
  if (model === 'seedream') limit = 14;
178
181
  if (model === 'gpt4o-image') limit = 16;
179
182
  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);
183
+ return items.map((entry) => String(entry?.url || '').trim()).filter((v) => v.length > 0 && /^https?:\/\//i.test(v)).slice(0, limit);
185
184
  };
186
185
 
187
186
  export class CommandosImage implements INodeType {
@@ -191,18 +190,11 @@ export class CommandosImage implements INodeType {
191
190
  group: ['transform'],
192
191
  version: 1,
193
192
  description: 'Create image tasks in Commandos API',
194
- defaults: {
195
- name: 'Commandos Image',
196
- },
193
+ defaults: { name: 'Commandos Image' },
197
194
  icon: 'file:Image.png',
198
195
  inputs: ['main'],
199
196
  outputs: ['main'],
200
- credentials: [
201
- {
202
- name: 'commandosApi',
203
- required: true,
204
- },
205
- ],
197
+ credentials: [{ name: 'commandosApi', required: true }],
206
198
  properties: [
207
199
  {
208
200
  displayName: 'Operation',
@@ -254,7 +246,24 @@ export class CommandosImage implements INodeType {
254
246
  { name: '21:9', value: '21:9' },
255
247
  ],
256
248
  default: '2:3',
257
- displayOptions: { show: { operation: ['create'] } },
249
+ displayOptions: {
250
+ show: { operation: ['create'] },
251
+ hide: { model: ['gpt4o-image'] },
252
+ },
253
+ },
254
+ {
255
+ displayName: 'Ratio',
256
+ name: 'ratioGpt',
257
+ type: 'options',
258
+ options: [
259
+ { name: '1:1', value: '1:1' },
260
+ { name: '2:3', value: '2:3' },
261
+ { name: '3:2', value: '3:2' },
262
+ ],
263
+ default: '2:3',
264
+ displayOptions: {
265
+ show: { operation: ['create'], model: ['gpt4o-image'] },
266
+ },
258
267
  },
259
268
  {
260
269
  displayName: 'Quality',
@@ -267,10 +276,7 @@ export class CommandosImage implements INodeType {
267
276
  ],
268
277
  default: 'basic',
269
278
  displayOptions: {
270
- show: {
271
- operation: ['create'],
272
- model: ['seedream', 'gpt4o-image', 'flux-pro'],
273
- },
279
+ show: { operation: ['create'], model: ['seedream', 'gpt4o-image', 'flux-pro'] },
274
280
  },
275
281
  },
276
282
  {
@@ -284,10 +290,7 @@ export class CommandosImage implements INodeType {
284
290
  ],
285
291
  default: '1K',
286
292
  displayOptions: {
287
- show: {
288
- operation: ['create'],
289
- model: ['flux-pro', 'nano-banana-pro'],
290
- },
293
+ show: { operation: ['create'], model: ['flux-pro', 'nano-banana-pro'] },
291
294
  },
292
295
  },
293
296
  {
@@ -300,14 +303,7 @@ export class CommandosImage implements INodeType {
300
303
  {
301
304
  name: 'reference',
302
305
  displayName: 'Reference',
303
- values: [
304
- {
305
- displayName: 'Reference URL',
306
- name: 'url',
307
- type: 'string',
308
- default: '',
309
- },
310
- ],
306
+ values: [{ displayName: 'Reference URL', name: 'url', type: 'string', default: '' }],
311
307
  },
312
308
  ],
313
309
  displayOptions: { show: { operation: ['create'] } },
@@ -327,71 +323,46 @@ export class CommandosImage implements INodeType {
327
323
  const operation = this.getNodeParameter('operation', 0) as string;
328
324
  const credentials = await this.getCredentials('commandosApi');
329
325
  const licenseKey = String(credentials.licenseKey || '').trim();
330
-
331
- if (!licenseKey) {
332
- throw new NodeOperationError(this.getNode(), 'License key is required');
333
- }
334
-
326
+ if (!licenseKey) throw new NodeOperationError(this.getNode(), 'License key is required');
335
327
  const results: INodeExecutionData[] = [];
336
-
337
328
  for (let i = 0; i < items.length; i += 1) {
338
329
  try {
339
330
  if (operation === 'create') {
340
331
  const model = String(this.getNodeParameter('model', i));
341
332
  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
333
 
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 });
334
+ let ratio = '';
335
+ if (model === 'gpt4o-image') {
336
+ ratio = String(this.getNodeParameter('ratioGpt', i));
337
+ } else {
338
+ ratio = String(this.getNodeParameter('ratio', i));
355
339
  }
356
340
 
357
- const request = buildGenerationRequest({ model, prompt, ratio, references, quality, resolution });
341
+ const references = extractReferences(this.getNodeParameter('references', i, {}), model);
342
+ const quality = ['seedream', 'gpt4o-image', 'flux-pro'].includes(model) ? String(this.getNodeParameter('quality', i, 'basic')) : 'basic';
343
+ const resolution = ['flux-pro', 'nano-banana-pro'].includes(model) ? String(this.getNodeParameter('resolution', i, '1K')) : '1K';
358
344
 
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
- };
345
+ if (!prompt) throw new NodeOperationError(this.getNode(), 'Prompt is required', { itemIndex: i });
369
346
 
347
+ const request = buildGenerationRequest({ model, prompt, ratio, references, quality, resolution });
348
+ const payload = { ...request, licenseKey, _v: '0.1.11' };
370
349
  const response = await this.helpers.request({
371
350
  method: 'POST',
372
351
  url: `${COMMANDOS_API_URL}/tasks`,
373
- headers: {
374
- 'Content-Type': 'application/json',
375
- 'X-License-Key': licenseKey,
376
- },
352
+ headers: { 'Content-Type': 'application/json', 'X-License-Key': licenseKey },
377
353
  body: { process_type: 'IMAGE_GENERATION', payload },
378
354
  json: true,
379
355
  });
380
-
381
356
  results.push({ json: response as IDataObject });
382
357
  } else if (operation === 'status') {
383
358
  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
-
359
+ if (!taskId) throw new NodeOperationError(this.getNode(), 'Task ID is required', { itemIndex: i });
388
360
  const response = await this.helpers.request({
389
361
  method: 'GET',
390
362
  url: `${COMMANDOS_API_URL}/tasks/${encodeURIComponent(taskId)}`,
391
363
  headers: { 'X-License-Key': licenseKey },
392
364
  json: true,
393
365
  });
394
-
395
366
  results.push({ json: response as IDataObject });
396
367
  }
397
368
  } catch (error) {
@@ -400,7 +371,6 @@ export class CommandosImage implements INodeType {
400
371
  throw new NodeOperationError(this.getNode(), message, { itemIndex: i });
401
372
  }
402
373
  }
403
-
404
374
  return [results];
405
375
  }
406
376
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-commandos-image",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Commandos Image custom node",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",