n8n-nodes-supermachine 0.7.2 → 0.7.3

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.
@@ -1011,374 +1011,6 @@ class Supermachine {
1011
1011
  }
1012
1012
  });
1013
1013
  }
1014
- else if (operation === 'upscale') {
1015
- const imageSource = this.getNodeParameter('imageSource', i);
1016
- const upscaler = this.getNodeParameter('upscaler', i);
1017
- const factor = this.getNodeParameter('factor', i);
1018
- const additionalOptions = this.getNodeParameter('additionalOptions', i, {});
1019
- let imageUrl = '';
1020
- // Step 1: Get image URL
1021
- if (imageSource === 'imageId') {
1022
- const imageId = this.getNodeParameter('imageId', i);
1023
- const imageDetails = await this.helpers.requestWithAuthentication.call(this, 'supermachineApi', {
1024
- method: 'GET',
1025
- url: `https://dev.supermachine.art/v1/images/${imageId}`,
1026
- json: true,
1027
- });
1028
- imageUrl = imageDetails.url;
1029
- if (!imageUrl) {
1030
- throw new Error(`Image ID ${imageId} does not have a valid URL`);
1031
- }
1032
- }
1033
- else if (imageSource === 'imageUrl') {
1034
- imageUrl = this.getNodeParameter('imageUrl', i);
1035
- }
1036
- // Step 2: Download image as binary
1037
- const imageBuffer = await this.helpers.httpRequest({
1038
- method: 'GET',
1039
- url: imageUrl,
1040
- encoding: 'arraybuffer',
1041
- returnFullResponse: false,
1042
- });
1043
- // Step 3: Convert to base64
1044
- const base64Image = Buffer.from(imageBuffer).toString('base64');
1045
- let mimeType = 'image/jpeg';
1046
- if (imageUrl.toLowerCase().includes('.png')) {
1047
- mimeType = 'image/png';
1048
- }
1049
- else if (imageUrl.toLowerCase().includes('.webp')) {
1050
- mimeType = 'image/webp';
1051
- }
1052
- const dataUri = `data:${mimeType};base64,${base64Image}`;
1053
- // Step 4: Submit to upscale API
1054
- const body = {
1055
- image: dataUri,
1056
- upscaler: upscaler,
1057
- factor: factor,
1058
- faceEnhance: additionalOptions.faceEnhance || false,
1059
- };
1060
- if (additionalOptions.folderId) {
1061
- body.folderId = parseInt(additionalOptions.folderId, 10) || 0;
1062
- }
1063
- let upscaleResponse;
1064
- try {
1065
- upscaleResponse = await this.helpers.requestWithAuthentication.call(this, 'supermachineApi', {
1066
- method: 'POST',
1067
- url: 'https://dev.supermachine.art/v1/tools/upscale',
1068
- json: true,
1069
- body,
1070
- });
1071
- }
1072
- catch (error) {
1073
- throw new Error(`Failed to upscale image: ${error.message}`);
1074
- }
1075
- const batchId = upscaleResponse.batchId;
1076
- if (!batchId) {
1077
- if (upscaleResponse.url || upscaleResponse.imageUrl) {
1078
- returnData.push({
1079
- json: {
1080
- success: true,
1081
- imageUrl: upscaleResponse.url || upscaleResponse.imageUrl,
1082
- imageId: upscaleResponse.id || upscaleResponse.imageId,
1083
- creditsCost: upscaleResponse.creditsCost,
1084
- creditsRemaining: upscaleResponse.creditsRemaining,
1085
- upscaler,
1086
- factor,
1087
- faceEnhance: body.faceEnhance,
1088
- processedAt: new Date().toISOString(),
1089
- ...upscaleResponse,
1090
- }
1091
- });
1092
- continue;
1093
- }
1094
- throw new Error('No batchId or result returned from upscale API');
1095
- }
1096
- const skipPolling = additionalOptions.skipPolling;
1097
- if (skipPolling) {
1098
- returnData.push({
1099
- json: {
1100
- batchId,
1101
- tool: upscaleResponse.tool || 'upscale',
1102
- creditsCost: upscaleResponse.creditsCost,
1103
- creditsRemaining: upscaleResponse.creditsRemaining,
1104
- upscaler,
1105
- factor,
1106
- faceEnhance: body.faceEnhance,
1107
- status: 'submitted',
1108
- message: 'Image upscaling submitted. Use List Images with this batchId to check status.',
1109
- submittedAt: new Date().toISOString(),
1110
- ...upscaleResponse,
1111
- }
1112
- });
1113
- continue;
1114
- }
1115
- // Step 5: Poll for result
1116
- const pollingInterval = additionalOptions.pollingInterval || 2;
1117
- const maxPollingTime = additionalOptions.maxPollingTime || 120;
1118
- const startTime = Date.now();
1119
- let completedResult = null;
1120
- await new Promise((resolve) => setTimeout(resolve, 2000));
1121
- while (!completedResult) {
1122
- if ((Date.now() - startTime) / 1000 > maxPollingTime) {
1123
- throw new Error(`Image upscaling timed out after ${maxPollingTime} seconds. Batch ID: ${batchId}`);
1124
- }
1125
- let pollResponse;
1126
- try {
1127
- pollResponse = await this.helpers.requestWithAuthentication.call(this, 'supermachineApi', {
1128
- method: 'GET',
1129
- url: `https://dev.supermachine.art/v1/images?batchId=${batchId}`,
1130
- json: true,
1131
- });
1132
- }
1133
- catch (error) {
1134
- await new Promise((resolve) => setTimeout(resolve, pollingInterval * 1000));
1135
- continue;
1136
- }
1137
- const pollItems = pollResponse.items;
1138
- if (Array.isArray(pollItems) && pollItems.length > 0) {
1139
- const firstItem = pollItems[0];
1140
- const status = firstItem.status;
1141
- if (status === 'completed') {
1142
- completedResult = firstItem;
1143
- break;
1144
- }
1145
- else if (status === 'failed') {
1146
- throw new Error(`Image upscaling failed. Batch ID: ${batchId}. Error: ${firstItem.error || 'Unknown error'}`);
1147
- }
1148
- }
1149
- await new Promise((resolve) => setTimeout(resolve, pollingInterval * 1000));
1150
- }
1151
- returnData.push({
1152
- json: {
1153
- batchId,
1154
- success: true,
1155
- operation: 'upscale',
1156
- tool: 'upscale',
1157
- upscaler,
1158
- factor,
1159
- faceEnhance: body.faceEnhance,
1160
- creditsCost: upscaleResponse.creditsCost,
1161
- creditsRemaining: upscaleResponse.creditsRemaining,
1162
- processedAt: new Date().toISOString(),
1163
- ...completedResult,
1164
- }
1165
- });
1166
- }
1167
- else if (operation === 'instructEdit') {
1168
- const imageSource = this.getNodeParameter('imageSource', i);
1169
- const instruction = this.getNodeParameter('instruction', i);
1170
- const additionalOptions = this.getNodeParameter('additionalOptions', i, {});
1171
- let imageUrl = '';
1172
- // Step 1: Get image URL
1173
- if (imageSource === 'imageId') {
1174
- const imageId = this.getNodeParameter('imageId', i);
1175
- const imageDetails = await this.helpers.requestWithAuthentication.call(this, 'supermachineApi', {
1176
- method: 'GET',
1177
- url: `https://dev.supermachine.art/v1/images/${imageId}`,
1178
- json: true,
1179
- });
1180
- imageUrl = imageDetails.url;
1181
- if (!imageUrl) {
1182
- throw new Error(`Image ID ${imageId} does not have a valid URL`);
1183
- }
1184
- }
1185
- else if (imageSource === 'imageUrl') {
1186
- imageUrl = this.getNodeParameter('imageUrl', i);
1187
- }
1188
- // Step 2: Download image as binary
1189
- const imageBuffer = await this.helpers.httpRequest({
1190
- method: 'GET',
1191
- url: imageUrl,
1192
- encoding: 'arraybuffer',
1193
- returnFullResponse: false,
1194
- });
1195
- // Step 3: Convert to base64
1196
- const base64Image = Buffer.from(imageBuffer).toString('base64');
1197
- let mimeType = 'image/jpeg';
1198
- if (imageUrl.toLowerCase().includes('.png')) {
1199
- mimeType = 'image/png';
1200
- }
1201
- else if (imageUrl.toLowerCase().includes('.webp')) {
1202
- mimeType = 'image/webp';
1203
- }
1204
- const dataUri = `data:${mimeType};base64,${base64Image}`;
1205
- // Step 4: Submit to instruct edit API
1206
- const body = {
1207
- image: dataUri,
1208
- instruction: instruction,
1209
- };
1210
- if (additionalOptions.folderId) {
1211
- body.folderId = parseInt(additionalOptions.folderId, 10) || 0;
1212
- }
1213
- let instructResponse;
1214
- try {
1215
- instructResponse = await this.helpers.requestWithAuthentication.call(this, 'supermachineApi', {
1216
- method: 'POST',
1217
- url: 'https://dev.supermachine.art/v1/tools/instruct',
1218
- json: true,
1219
- body,
1220
- });
1221
- }
1222
- catch (error) {
1223
- throw new Error(`Failed to edit image with instruction: ${error.message}`);
1224
- }
1225
- const batchId = instructResponse.batchId;
1226
- if (!batchId) {
1227
- if (instructResponse.url || instructResponse.imageUrl) {
1228
- returnData.push({
1229
- json: {
1230
- success: true,
1231
- imageUrl: instructResponse.url || instructResponse.imageUrl,
1232
- imageId: instructResponse.id || instructResponse.imageId,
1233
- creditsCost: instructResponse.creditsCost,
1234
- creditsRemaining: instructResponse.creditsRemaining,
1235
- instruction,
1236
- processedAt: new Date().toISOString(),
1237
- ...instructResponse,
1238
- }
1239
- });
1240
- continue;
1241
- }
1242
- throw new Error('No batchId or result returned from instruct edit API');
1243
- }
1244
- const skipPolling = additionalOptions.skipPolling;
1245
- if (skipPolling) {
1246
- returnData.push({
1247
- json: {
1248
- batchId,
1249
- tool: instructResponse.tool || 'instruct',
1250
- creditsCost: instructResponse.creditsCost,
1251
- creditsRemaining: instructResponse.creditsRemaining,
1252
- instruction,
1253
- status: 'submitted',
1254
- message: 'Instruct edit submitted. Use List Images with this batchId to check status.',
1255
- submittedAt: new Date().toISOString(),
1256
- ...instructResponse,
1257
- }
1258
- });
1259
- continue;
1260
- }
1261
- // Step 5: Poll for result
1262
- const pollingInterval = additionalOptions.pollingInterval || 2;
1263
- const maxPollingTime = additionalOptions.maxPollingTime || 120;
1264
- const startTime = Date.now();
1265
- let completedResult = null;
1266
- await new Promise((resolve) => setTimeout(resolve, 2000));
1267
- while (!completedResult) {
1268
- if ((Date.now() - startTime) / 1000 > maxPollingTime) {
1269
- throw new Error(`Instruct edit timed out after ${maxPollingTime} seconds. Batch ID: ${batchId}`);
1270
- }
1271
- let pollResponse;
1272
- try {
1273
- pollResponse = await this.helpers.requestWithAuthentication.call(this, 'supermachineApi', {
1274
- method: 'GET',
1275
- url: `https://dev.supermachine.art/v1/images?batchId=${batchId}`,
1276
- json: true,
1277
- });
1278
- }
1279
- catch (error) {
1280
- await new Promise((resolve) => setTimeout(resolve, pollingInterval * 1000));
1281
- continue;
1282
- }
1283
- const pollItems = pollResponse.items;
1284
- if (Array.isArray(pollItems) && pollItems.length > 0) {
1285
- const firstItem = pollItems[0];
1286
- const status = firstItem.status;
1287
- if (status === 'completed') {
1288
- completedResult = firstItem;
1289
- break;
1290
- }
1291
- else if (status === 'failed') {
1292
- throw new Error(`Instruct edit failed. Batch ID: ${batchId}. Error: ${firstItem.error || 'Unknown error'}`);
1293
- }
1294
- }
1295
- await new Promise((resolve) => setTimeout(resolve, pollingInterval * 1000));
1296
- }
1297
- returnData.push({
1298
- json: {
1299
- batchId,
1300
- success: true,
1301
- operation: 'instructEdit',
1302
- tool: 'instruct',
1303
- instruction,
1304
- creditsCost: instructResponse.creditsCost,
1305
- creditsRemaining: instructResponse.creditsRemaining,
1306
- processedAt: new Date().toISOString(),
1307
- ...completedResult,
1308
- }
1309
- });
1310
- }
1311
- else if (operation === 'imageToPrompt') {
1312
- const imageSource = this.getNodeParameter('imageSource', i);
1313
- let imageUrl = '';
1314
- // Step 1: Get image URL
1315
- if (imageSource === 'imageId') {
1316
- const imageId = this.getNodeParameter('imageId', i);
1317
- const imageDetails = await this.helpers.requestWithAuthentication.call(this, 'supermachineApi', {
1318
- method: 'GET',
1319
- url: `https://dev.supermachine.art/v1/images/${imageId}`,
1320
- json: true,
1321
- });
1322
- imageUrl = imageDetails.url;
1323
- if (!imageUrl) {
1324
- throw new Error(`Image ID ${imageId} does not have a valid URL`);
1325
- }
1326
- }
1327
- else if (imageSource === 'imageUrl') {
1328
- imageUrl = this.getNodeParameter('imageUrl', i);
1329
- }
1330
- // Step 2: Download image as binary
1331
- const imageBuffer = await this.helpers.httpRequest({
1332
- method: 'GET',
1333
- url: imageUrl,
1334
- encoding: 'arraybuffer',
1335
- returnFullResponse: false,
1336
- });
1337
- // Step 3: Convert to base64
1338
- const base64Image = Buffer.from(imageBuffer).toString('base64');
1339
- let mimeType = 'image/jpeg';
1340
- if (imageUrl.toLowerCase().includes('.png')) {
1341
- mimeType = 'image/png';
1342
- }
1343
- else if (imageUrl.toLowerCase().includes('.webp')) {
1344
- mimeType = 'image/webp';
1345
- }
1346
- const dataUri = `data:${mimeType};base64,${base64Image}`;
1347
- // Step 4: Submit to image-to-prompt API
1348
- const body = {
1349
- image: dataUri,
1350
- };
1351
- let promptResponse;
1352
- try {
1353
- promptResponse = await this.helpers.requestWithAuthentication.call(this, 'supermachineApi', {
1354
- method: 'POST',
1355
- url: 'https://dev.supermachine.art/v1/tools/image-to-prompt',
1356
- json: true,
1357
- body,
1358
- });
1359
- }
1360
- catch (error) {
1361
- throw new Error(`Failed to generate prompt from image: ${error.message}`);
1362
- }
1363
- // Image to Prompt returns immediately with the prompt (no polling needed)
1364
- const generatedPrompt = promptResponse.prompt;
1365
- if (!generatedPrompt) {
1366
- throw new Error('No prompt returned from image-to-prompt API');
1367
- }
1368
- returnData.push({
1369
- json: {
1370
- success: true,
1371
- prompt: generatedPrompt,
1372
- creditsCost: promptResponse.creditsCost || 0.4,
1373
- creditsRemaining: promptResponse.creditsRemaining,
1374
- operation: 'imageToPrompt',
1375
- tool: 'image-to-prompt',
1376
- sourceImage: imageUrl,
1377
- processedAt: new Date().toISOString(),
1378
- ...promptResponse,
1379
- }
1380
- });
1381
- }
1382
1014
  else if (operation === 'faceSwap') {
1383
1015
  const faceImageSource = this.getNodeParameter('faceImageSource', i);
1384
1016
  const targetImageSource = this.getNodeParameter('targetImageSource', i);
@@ -1489,7 +1121,8 @@ class Supermachine {
1489
1121
  operation: 'faceSwap',
1490
1122
  faceImage: faceImageUrl,
1491
1123
  targetImage: targetImageUrl,
1492
- message: 'Face swap job submitted. Use "Get Batch Status" to check results.',
1124
+ message: 'Face swap job submitted. Use List Images with batchId to check results.',
1125
+ submittedAt: new Date().toISOString(),
1493
1126
  ...faceSwapResponse,
1494
1127
  },
1495
1128
  });
@@ -1501,24 +1134,39 @@ class Supermachine {
1501
1134
  const startTime = Date.now();
1502
1135
  let completed = false;
1503
1136
  let finalResult = null;
1137
+ // Initial wait before polling
1138
+ await new Promise((resolve) => setTimeout(resolve, 2000));
1504
1139
  while (!completed && (Date.now() - startTime) / 1000 < maxPollingTime) {
1505
- await new Promise((resolve) => setTimeout(resolve, pollingInterval * 1000));
1506
- const statusResponse = await this.helpers.requestWithAuthentication.call(this, 'supermachineApi', {
1507
- method: 'GET',
1508
- url: `https://dev.supermachine.art/v1/batches/${batchId}`,
1509
- json: true,
1510
- });
1511
- const status = statusResponse.status;
1512
- if (status === 'completed') {
1513
- completed = true;
1514
- finalResult = statusResponse;
1140
+ let statusResponse;
1141
+ try {
1142
+ statusResponse = await this.helpers.requestWithAuthentication.call(this, 'supermachineApi', {
1143
+ method: 'GET',
1144
+ url: `https://dev.supermachine.art/v1/images?batchId=${batchId}`,
1145
+ json: true,
1146
+ });
1147
+ const items = statusResponse.items;
1148
+ if (Array.isArray(items) && items.length > 0) {
1149
+ const firstItem = items[0];
1150
+ const status = firstItem.status;
1151
+ if (status === 'completed') {
1152
+ completed = true;
1153
+ finalResult = firstItem;
1154
+ break;
1155
+ }
1156
+ else if (status === 'failed') {
1157
+ throw new Error(`Face swap failed: ${firstItem.error || 'Unknown error'}`);
1158
+ }
1159
+ }
1515
1160
  }
1516
- else if (status === 'failed') {
1517
- throw new Error(`Face swap failed: ${statusResponse.error || 'Unknown error'}`);
1161
+ catch (error) {
1162
+ // Continue polling on transient errors
1163
+ }
1164
+ if (!completed) {
1165
+ await new Promise((resolve) => setTimeout(resolve, pollingInterval * 1000));
1518
1166
  }
1519
1167
  }
1520
1168
  if (!completed) {
1521
- throw new Error(`Face swap timed out after ${maxPollingTime} seconds`);
1169
+ throw new Error(`Face swap timed out after ${maxPollingTime} seconds. Check batchId: ${batchId}`);
1522
1170
  }
1523
1171
  returnData.push({
1524
1172
  json: {
@@ -1536,7 +1184,7 @@ class Supermachine {
1536
1184
  }
1537
1185
  }
1538
1186
  else {
1539
- throw new Error(`⚠️ Operation "${operation}" is not yet implemented.`);
1187
+ throw new Error(`⚠️ Operation "${operation}" is coming soon.`);
1540
1188
  }
1541
1189
  }
1542
1190
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-supermachine",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "n8n community node for Supermachine AI Image API — Generate images, manage models, LoRAs, and characters",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",