@aiteza/n8n-nodes-aiteza 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nodes/Aiteza/Aiteza.node.js +273 -41
- package/dist/nodes/Aiteza/AitezaProgress.node.d.ts +5 -0
- package/dist/nodes/Aiteza/AitezaProgress.node.js +271 -0
- package/dist/nodes/Aiteza/AitezaTrigger.node.js +21 -10
- package/dist/nodes/Aiteza/AitezaWorkflowResult.node.d.ts +5 -0
- package/dist/nodes/Aiteza/AitezaWorkflowResult.node.js +261 -0
- package/dist/nodes/Aiteza/GenericFunctions.d.ts +6 -0
- package/dist/nodes/Aiteza/GenericFunctions.js +82 -2
- package/package.json +4 -2
|
@@ -46,7 +46,7 @@ class Aiteza {
|
|
|
46
46
|
{
|
|
47
47
|
name: 'From AITEZA Trigger',
|
|
48
48
|
value: 'trigger',
|
|
49
|
-
description: 'Use the auth token (and optionally base URL) passed by an upstream AITEZA Trigger node (acts on behalf of the calling user)',
|
|
49
|
+
description: 'Use the auth token (and optionally base URL) passed by an upstream AITEZA Trigger node (acts on behalf of the calling user). The node never falls back to configured credentials – those would belong to a different identity.',
|
|
50
50
|
},
|
|
51
51
|
],
|
|
52
52
|
default: 'credentials',
|
|
@@ -252,23 +252,25 @@ class Aiteza {
|
|
|
252
252
|
},
|
|
253
253
|
// Dataroom → Delete
|
|
254
254
|
{
|
|
255
|
-
displayName: 'Dataroom IDs',
|
|
255
|
+
displayName: 'Dataroom Names or IDs',
|
|
256
256
|
name: 'dataroomIds',
|
|
257
|
-
type: '
|
|
257
|
+
type: 'multiOptions',
|
|
258
|
+
typeOptions: { loadOptionsMethod: 'getDatarooms' },
|
|
258
259
|
required: true,
|
|
259
|
-
default:
|
|
260
|
+
default: [],
|
|
260
261
|
displayOptions: { show: { resource: ['dataroom'], operation: ['delete'] } },
|
|
261
|
-
description: '
|
|
262
|
+
description: 'Datarooms to delete. Choose from the list (prepopulated with recently used), or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
262
263
|
},
|
|
263
264
|
// Dataroom → Set Connected
|
|
264
265
|
{
|
|
265
|
-
displayName: 'Connected Dataroom IDs',
|
|
266
|
+
displayName: 'Connected Dataroom Names or IDs',
|
|
266
267
|
name: 'connectedDataroomIds',
|
|
267
|
-
type: '
|
|
268
|
+
type: 'multiOptions',
|
|
269
|
+
typeOptions: { loadOptionsMethod: 'getDatarooms' },
|
|
268
270
|
required: true,
|
|
269
|
-
default:
|
|
271
|
+
default: [],
|
|
270
272
|
displayOptions: { show: { resource: ['dataroom'], operation: ['setConnected'] } },
|
|
271
|
-
description: '
|
|
273
|
+
description: 'Datarooms to set as default connected datarooms. Choose from the list (prepopulated with recently used), or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
272
274
|
},
|
|
273
275
|
// Dataroom → Get Chats pagination
|
|
274
276
|
{
|
|
@@ -480,6 +482,9 @@ class Aiteza {
|
|
|
480
482
|
displayOptions: { show: { resource: ['file'] } },
|
|
481
483
|
options: [
|
|
482
484
|
{ name: 'Assign to Dataroom', value: 'assign', action: 'Assign a standalone file to a dataroom' },
|
|
485
|
+
{ name: 'Bulk Delete', value: 'bulkDelete', action: 'Delete multiple files from a dataroom' },
|
|
486
|
+
{ name: 'Bulk Move', value: 'bulkMove', action: 'Move multiple files to another dataroom' },
|
|
487
|
+
{ name: 'Bulk Patch', value: 'bulkPatch', action: 'Bulk patch or reprocess files in a dataroom' },
|
|
483
488
|
{ name: 'Delete', value: 'delete', action: 'Delete a file from a dataroom' },
|
|
484
489
|
{ name: 'Delete All (Dataroom)', value: 'deleteAllDataroom', action: 'Delete all files from a dataroom' },
|
|
485
490
|
{ name: 'Delete Standalone', value: 'deleteStandalone', action: 'Delete a standalone file' },
|
|
@@ -509,7 +514,7 @@ class Aiteza {
|
|
|
509
514
|
required: true,
|
|
510
515
|
default: '',
|
|
511
516
|
displayOptions: {
|
|
512
|
-
show: { resource: ['file'], operation: ['getMany', 'get', 'delete', 'upload', 'deleteAllDataroom', 'reprocess', 'move', 'getOriginalUrlDataroom', 'getChunksDataroom'] },
|
|
517
|
+
show: { resource: ['file'], operation: ['getMany', 'get', 'delete', 'upload', 'deleteAllDataroom', 'reprocess', 'move', 'getOriginalUrlDataroom', 'getChunksDataroom', 'bulkDelete', 'bulkMove', 'bulkPatch'] },
|
|
513
518
|
},
|
|
514
519
|
description: 'The dataroom. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
515
520
|
},
|
|
@@ -537,6 +542,48 @@ class Aiteza {
|
|
|
537
542
|
},
|
|
538
543
|
description: 'The standalone file. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
539
544
|
},
|
|
545
|
+
// File → Bulk: file IDs (dataroom-scoped)
|
|
546
|
+
{
|
|
547
|
+
displayName: 'File Names or IDs',
|
|
548
|
+
name: 'fileIds',
|
|
549
|
+
type: 'multiOptions',
|
|
550
|
+
typeOptions: { loadOptionsMethod: 'getFilesInDataroom', loadOptionsDependsOn: ['dataroomId'] },
|
|
551
|
+
required: true,
|
|
552
|
+
default: [],
|
|
553
|
+
displayOptions: { show: { resource: ['file'], operation: ['bulkDelete', 'bulkMove', 'bulkPatch'] } },
|
|
554
|
+
description: 'Files to operate on. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
555
|
+
},
|
|
556
|
+
// File → Bulk Patch options
|
|
557
|
+
{
|
|
558
|
+
displayName: 'Reprocess',
|
|
559
|
+
name: 'reprocess',
|
|
560
|
+
type: 'boolean',
|
|
561
|
+
default: false,
|
|
562
|
+
displayOptions: { show: { resource: ['file'], operation: ['bulkPatch'] } },
|
|
563
|
+
description: 'Whether to trigger reprocessing of the selected files',
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
displayName: 'Processing Pipeline',
|
|
567
|
+
name: 'processingPipeline',
|
|
568
|
+
type: 'options',
|
|
569
|
+
default: 'DOCLING',
|
|
570
|
+
displayOptions: { show: { resource: ['file'], operation: ['bulkPatch'], reprocess: [true] } },
|
|
571
|
+
options: [
|
|
572
|
+
{ name: 'TIKA', value: 'TIKA' },
|
|
573
|
+
{ name: 'DOCLING', value: 'DOCLING' },
|
|
574
|
+
{ name: 'DOCLING Force OCR', value: 'DOCLING_FORCE_OCR' },
|
|
575
|
+
{ name: 'VLM', value: 'VLM' },
|
|
576
|
+
],
|
|
577
|
+
description: 'Processing pipeline to use (required when Reprocess is enabled)',
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
displayName: 'VLM Model',
|
|
581
|
+
name: 'vlmModel',
|
|
582
|
+
type: 'string',
|
|
583
|
+
default: '',
|
|
584
|
+
displayOptions: { show: { resource: ['file'], operation: ['bulkPatch'], reprocess: [true], processingPipeline: ['VLM'] } },
|
|
585
|
+
description: 'VLM model name to use (required when pipeline is VLM)',
|
|
586
|
+
},
|
|
540
587
|
// File → Assign: target dataroom
|
|
541
588
|
{
|
|
542
589
|
displayName: 'Target Dataroom Name or ID',
|
|
@@ -545,8 +592,8 @@ class Aiteza {
|
|
|
545
592
|
typeOptions: { loadOptionsMethod: 'getDatarooms' },
|
|
546
593
|
required: true,
|
|
547
594
|
default: '',
|
|
548
|
-
displayOptions: { show: { resource: ['file'], operation: ['assign', 'move'] } },
|
|
549
|
-
description: 'The dataroom to assign/move the file to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
595
|
+
displayOptions: { show: { resource: ['file'], operation: ['assign', 'move', 'bulkMove'] } },
|
|
596
|
+
description: 'The dataroom to assign/move the file(s) to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
550
597
|
},
|
|
551
598
|
// File → Get Many (pagination)
|
|
552
599
|
{
|
|
@@ -666,6 +713,9 @@ class Aiteza {
|
|
|
666
713
|
displayOptions: { show: { resource: ['image'] } },
|
|
667
714
|
options: [
|
|
668
715
|
{ name: 'Assign to Dataroom', value: 'assign', action: 'Assign a standalone image to a dataroom' },
|
|
716
|
+
{ name: 'Bulk Delete (Dataroom)', value: 'bulkDelete', action: 'Delete multiple images from a dataroom' },
|
|
717
|
+
{ name: 'Bulk Move', value: 'bulkMove', action: 'Move multiple images to another dataroom' },
|
|
718
|
+
{ name: 'Bulk Patch', value: 'bulkPatch', action: 'Bulk patch or reprocess images in a dataroom' },
|
|
669
719
|
{ name: 'Delete (Dataroom)', value: 'deleteDataroom', action: 'Delete an image from a dataroom' },
|
|
670
720
|
{ name: 'Delete All (Dataroom)', value: 'deleteAllDataroom', action: 'Delete all images from a dataroom' },
|
|
671
721
|
{ name: 'Delete Standalone', value: 'deleteStandalone', action: 'Delete a standalone image' },
|
|
@@ -691,7 +741,7 @@ class Aiteza {
|
|
|
691
741
|
required: true,
|
|
692
742
|
default: '',
|
|
693
743
|
displayOptions: {
|
|
694
|
-
show: { resource: ['image'], operation: ['getManyDataroom', 'getDataroom', 'deleteDataroom', 'uploadDataroom', 'deleteAllDataroom', 'move', 'getOriginalUrlDataroom'] },
|
|
744
|
+
show: { resource: ['image'], operation: ['getManyDataroom', 'getDataroom', 'deleteDataroom', 'uploadDataroom', 'deleteAllDataroom', 'move', 'getOriginalUrlDataroom', 'bulkDelete', 'bulkMove', 'bulkPatch'] },
|
|
695
745
|
},
|
|
696
746
|
description: 'The dataroom. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
697
747
|
},
|
|
@@ -727,8 +777,39 @@ class Aiteza {
|
|
|
727
777
|
typeOptions: { loadOptionsMethod: 'getDatarooms' },
|
|
728
778
|
required: true,
|
|
729
779
|
default: '',
|
|
730
|
-
displayOptions: { show: { resource: ['image'], operation: ['assign', 'move'] } },
|
|
731
|
-
description: 'The dataroom to assign/move the image to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
780
|
+
displayOptions: { show: { resource: ['image'], operation: ['assign', 'move', 'bulkMove'] } },
|
|
781
|
+
description: 'The dataroom to assign/move the image(s) to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
782
|
+
},
|
|
783
|
+
// Image → Bulk: image IDs (dataroom-scoped)
|
|
784
|
+
{
|
|
785
|
+
displayName: 'Image Names or IDs',
|
|
786
|
+
name: 'imageIds',
|
|
787
|
+
type: 'multiOptions',
|
|
788
|
+
typeOptions: { loadOptionsMethod: 'getImagesInDataroom', loadOptionsDependsOn: ['dataroomId'] },
|
|
789
|
+
required: true,
|
|
790
|
+
default: [],
|
|
791
|
+
displayOptions: { show: { resource: ['image'], operation: ['bulkDelete', 'bulkMove', 'bulkPatch'] } },
|
|
792
|
+
description: 'Images to operate on. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
793
|
+
},
|
|
794
|
+
// Image → Bulk Patch options
|
|
795
|
+
{
|
|
796
|
+
displayName: 'Reprocess',
|
|
797
|
+
name: 'reprocess',
|
|
798
|
+
type: 'boolean',
|
|
799
|
+
default: false,
|
|
800
|
+
displayOptions: { show: { resource: ['image'], operation: ['bulkPatch'] } },
|
|
801
|
+
description: 'Whether to trigger reprocessing of the selected images',
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
displayName: 'Patch Options',
|
|
805
|
+
name: 'patchOptions',
|
|
806
|
+
type: 'collection',
|
|
807
|
+
placeholder: 'Add Option',
|
|
808
|
+
default: {},
|
|
809
|
+
displayOptions: { show: { resource: ['image'], operation: ['bulkPatch'] } },
|
|
810
|
+
options: [
|
|
811
|
+
{ displayName: 'Name', name: 'name', type: 'string', default: '', description: 'Override display name for all items' },
|
|
812
|
+
],
|
|
732
813
|
},
|
|
733
814
|
// Image → Get Many pagination
|
|
734
815
|
{
|
|
@@ -799,10 +880,14 @@ class Aiteza {
|
|
|
799
880
|
displayOptions: { show: { resource: ['webSource'] } },
|
|
800
881
|
options: [
|
|
801
882
|
{ name: 'Add', value: 'add', action: 'Add a web source to a dataroom' },
|
|
883
|
+
{ name: 'Bulk Delete', value: 'bulkDelete', action: 'Delete multiple web sources from a dataroom' },
|
|
884
|
+
{ name: 'Bulk Move', value: 'bulkMove', action: 'Move multiple web sources to another dataroom' },
|
|
885
|
+
{ name: 'Bulk Patch', value: 'bulkPatch', action: 'Bulk patch or rescan web sources in a dataroom' },
|
|
802
886
|
{ name: 'Delete', value: 'delete', action: 'Delete a web source' },
|
|
803
887
|
{ name: 'Delete All', value: 'deleteAll', action: 'Delete all web sources from a dataroom' },
|
|
804
888
|
{ name: 'Get', value: 'get', action: 'Get a web source' },
|
|
805
889
|
{ name: 'Get Many', value: 'getMany', action: 'Get web sources in a dataroom' },
|
|
890
|
+
{ name: 'Get URLs', value: 'getUrls', action: 'Get crawled URLs of a web source' },
|
|
806
891
|
{ name: 'Move', value: 'move', action: 'Move a web source to another dataroom' },
|
|
807
892
|
{ name: 'Rescan', value: 'rescan', action: 'Rescan a web source' },
|
|
808
893
|
],
|
|
@@ -826,7 +911,7 @@ class Aiteza {
|
|
|
826
911
|
typeOptions: { loadOptionsMethod: 'getWebSourcesInDataroom', loadOptionsDependsOn: ['dataroomId'] },
|
|
827
912
|
required: true,
|
|
828
913
|
default: '',
|
|
829
|
-
displayOptions: { show: { resource: ['webSource'], operation: ['get', 'delete', 'rescan', 'move'] } },
|
|
914
|
+
displayOptions: { show: { resource: ['webSource'], operation: ['get', 'delete', 'rescan', 'move', 'getUrls'] } },
|
|
830
915
|
description: 'The web source. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
831
916
|
},
|
|
832
917
|
// Web Source → Move target
|
|
@@ -837,9 +922,43 @@ class Aiteza {
|
|
|
837
922
|
typeOptions: { loadOptionsMethod: 'getDatarooms' },
|
|
838
923
|
required: true,
|
|
839
924
|
default: '',
|
|
840
|
-
displayOptions: { show: { resource: ['webSource'], operation: ['move'] } },
|
|
925
|
+
displayOptions: { show: { resource: ['webSource'], operation: ['move', 'bulkMove'] } },
|
|
841
926
|
description: 'The target dataroom. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
842
927
|
},
|
|
928
|
+
// Web Source → Bulk: IDs
|
|
929
|
+
{
|
|
930
|
+
displayName: 'Web Source Names or IDs',
|
|
931
|
+
name: 'webSourceIds',
|
|
932
|
+
type: 'multiOptions',
|
|
933
|
+
typeOptions: { loadOptionsMethod: 'getWebSourcesInDataroom', loadOptionsDependsOn: ['dataroomId'] },
|
|
934
|
+
required: true,
|
|
935
|
+
default: [],
|
|
936
|
+
displayOptions: { show: { resource: ['webSource'], operation: ['bulkDelete', 'bulkMove', 'bulkPatch'] } },
|
|
937
|
+
description: 'Web sources to operate on. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
938
|
+
},
|
|
939
|
+
// Web Source → Bulk Patch options
|
|
940
|
+
{
|
|
941
|
+
displayName: 'Reprocess',
|
|
942
|
+
name: 'reprocess',
|
|
943
|
+
type: 'boolean',
|
|
944
|
+
default: false,
|
|
945
|
+
displayOptions: { show: { resource: ['webSource'], operation: ['bulkPatch'] } },
|
|
946
|
+
description: 'Whether to trigger rescanning of the selected web sources',
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
displayName: 'Patch Options',
|
|
950
|
+
name: 'patchOptions',
|
|
951
|
+
type: 'collection',
|
|
952
|
+
placeholder: 'Add Option',
|
|
953
|
+
default: {},
|
|
954
|
+
displayOptions: { show: { resource: ['webSource'], operation: ['bulkPatch'] } },
|
|
955
|
+
options: [
|
|
956
|
+
{ displayName: 'Name', name: 'name', type: 'string', default: '', description: 'Override display name for all items' },
|
|
957
|
+
{ displayName: 'Include Subpages', name: 'includeSubpages', type: 'boolean', default: false },
|
|
958
|
+
{ displayName: 'Max Subpages', name: 'maxSubpages', type: 'number', default: 10 },
|
|
959
|
+
{ displayName: 'Max Depth', name: 'maxDepth', type: 'number', default: 1 },
|
|
960
|
+
],
|
|
961
|
+
},
|
|
843
962
|
// Web Source → Add
|
|
844
963
|
{
|
|
845
964
|
displayName: 'URL',
|
|
@@ -946,6 +1065,31 @@ class Aiteza {
|
|
|
946
1065
|
},
|
|
947
1066
|
],
|
|
948
1067
|
},
|
|
1068
|
+
// Web Source → Get URLs pagination
|
|
1069
|
+
{
|
|
1070
|
+
displayName: 'Additional Fields',
|
|
1071
|
+
name: 'additionalFields',
|
|
1072
|
+
type: 'collection',
|
|
1073
|
+
placeholder: 'Add Field',
|
|
1074
|
+
default: {},
|
|
1075
|
+
displayOptions: { show: { resource: ['webSource'], operation: ['getUrls'] } },
|
|
1076
|
+
options: [
|
|
1077
|
+
{
|
|
1078
|
+
displayName: 'Page',
|
|
1079
|
+
name: 'page',
|
|
1080
|
+
type: 'number',
|
|
1081
|
+
default: 0,
|
|
1082
|
+
description: 'Page number (0-based)',
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
displayName: 'Size',
|
|
1086
|
+
name: 'size',
|
|
1087
|
+
type: 'number',
|
|
1088
|
+
default: 20,
|
|
1089
|
+
description: 'Number of items per page',
|
|
1090
|
+
},
|
|
1091
|
+
],
|
|
1092
|
+
},
|
|
949
1093
|
// ==================================================================
|
|
950
1094
|
// CHAT
|
|
951
1095
|
// ==================================================================
|
|
@@ -1073,11 +1217,12 @@ class Aiteza {
|
|
|
1073
1217
|
displayOptions: { show: { resource: ['chat'], operation: ['generate', 'estimate'] } },
|
|
1074
1218
|
options: [
|
|
1075
1219
|
{
|
|
1076
|
-
displayName: 'Connected Dataroom IDs',
|
|
1220
|
+
displayName: 'Connected Dataroom Names or IDs',
|
|
1077
1221
|
name: 'connectedDatarooms',
|
|
1078
|
-
type: '
|
|
1079
|
-
|
|
1080
|
-
|
|
1222
|
+
type: 'multiOptions',
|
|
1223
|
+
typeOptions: { loadOptionsMethod: 'getDatarooms' },
|
|
1224
|
+
default: [],
|
|
1225
|
+
description: 'Datarooms for context. Choose from the list (prepopulated with recently used), or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
1081
1226
|
},
|
|
1082
1227
|
{
|
|
1083
1228
|
displayName: 'File IDs',
|
|
@@ -1150,11 +1295,12 @@ class Aiteza {
|
|
|
1150
1295
|
displayOptions: { show: { resource: ['chat'], operation: ['tempChat'] } },
|
|
1151
1296
|
options: [
|
|
1152
1297
|
{
|
|
1153
|
-
displayName: 'Connected Dataroom IDs',
|
|
1298
|
+
displayName: 'Connected Dataroom Names or IDs',
|
|
1154
1299
|
name: 'connectedDatarooms',
|
|
1155
|
-
type: '
|
|
1156
|
-
|
|
1157
|
-
|
|
1300
|
+
type: 'multiOptions',
|
|
1301
|
+
typeOptions: { loadOptionsMethod: 'getDatarooms' },
|
|
1302
|
+
default: [],
|
|
1303
|
+
description: 'Datarooms to search. Choose from the list (prepopulated with recently used), or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
1158
1304
|
},
|
|
1159
1305
|
{
|
|
1160
1306
|
displayName: 'File IDs',
|
|
@@ -1248,12 +1394,13 @@ class Aiteza {
|
|
|
1248
1394
|
description: 'The search query text',
|
|
1249
1395
|
},
|
|
1250
1396
|
{
|
|
1251
|
-
displayName: 'Dataroom IDs',
|
|
1397
|
+
displayName: 'Dataroom Names or IDs',
|
|
1252
1398
|
name: 'dataroomIds',
|
|
1253
|
-
type: '
|
|
1254
|
-
|
|
1399
|
+
type: 'multiOptions',
|
|
1400
|
+
typeOptions: { loadOptionsMethod: 'getDatarooms' },
|
|
1401
|
+
default: [],
|
|
1255
1402
|
displayOptions: { show: { resource: ['search'], operation: ['hybridSearch'] } },
|
|
1256
|
-
description: '
|
|
1403
|
+
description: 'Datarooms to search across. Choose from the list (prepopulated with recently used), or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
1257
1404
|
},
|
|
1258
1405
|
{
|
|
1259
1406
|
displayName: 'Additional Fields',
|
|
@@ -1370,7 +1517,12 @@ class Aiteza {
|
|
|
1370
1517
|
const triggerToken = (0, GenericFunctions_1.getUpstreamAuthToken)(this);
|
|
1371
1518
|
if (!triggerToken) {
|
|
1372
1519
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Authentication is set to "From AITEZA Trigger" but no auth token was found in the input data. ' +
|
|
1373
|
-
'Make sure this node is connected to an AITEZA Trigger node.');
|
|
1520
|
+
'Make sure this node is connected (directly or indirectly) to an AITEZA Trigger node so its _authToken is forwarded.');
|
|
1521
|
+
}
|
|
1522
|
+
if ((0, GenericFunctions_1.isJwtExpired)(triggerToken)) {
|
|
1523
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'The auth token forwarded by the AITEZA Trigger has expired. ' +
|
|
1524
|
+
'User tokens are short-lived (typically ~5 minutes) and cannot be refreshed from inside the workflow. ' +
|
|
1525
|
+
'Re-trigger the workflow, shorten its runtime, or have AITEZA mint a longer-lived token for delegated workflow execution.');
|
|
1374
1526
|
}
|
|
1375
1527
|
const paramBaseUrl = this.getNodeParameter('baseUrl', 0, '').replace(/\/+$/, '');
|
|
1376
1528
|
const baseUrl = paramBaseUrl || (0, GenericFunctions_1.getUpstreamBaseUrl)(this) || '';
|
|
@@ -1433,6 +1585,15 @@ class Aiteza {
|
|
|
1433
1585
|
return url;
|
|
1434
1586
|
};
|
|
1435
1587
|
const splitCsv = (val) => val.split(',').map((s) => s.trim()).filter(Boolean);
|
|
1588
|
+
// Accepts a value from a multiOptions field (array), a comma-separated string,
|
|
1589
|
+
// or an expression-supplied array/string. Returns a clean list of IDs.
|
|
1590
|
+
const toIdArray = (val) => {
|
|
1591
|
+
if (Array.isArray(val))
|
|
1592
|
+
return val.map((v) => String(v).trim()).filter(Boolean);
|
|
1593
|
+
if (typeof val === 'string')
|
|
1594
|
+
return splitCsv(val);
|
|
1595
|
+
return [];
|
|
1596
|
+
};
|
|
1436
1597
|
for (let i = 0; i < items.length; i++) {
|
|
1437
1598
|
try {
|
|
1438
1599
|
let responseData;
|
|
@@ -1471,7 +1632,7 @@ class Aiteza {
|
|
|
1471
1632
|
}
|
|
1472
1633
|
else if (operation === 'delete') {
|
|
1473
1634
|
const idsRaw = this.getNodeParameter('dataroomIds', i);
|
|
1474
|
-
const dataroomIds =
|
|
1635
|
+
const dataroomIds = toIdArray(idsRaw);
|
|
1475
1636
|
(0, GenericFunctions_1.validateRequiredField)(this, dataroomIds.length > 0 ? 'ok' : '', 'Dataroom IDs');
|
|
1476
1637
|
responseData = await apiReq('DELETE', '/api/dataroom/delete', { dataroomIds });
|
|
1477
1638
|
}
|
|
@@ -1496,7 +1657,7 @@ class Aiteza {
|
|
|
1496
1657
|
else if (operation === 'setConnected') {
|
|
1497
1658
|
const id = this.getNodeParameter('dataroomId', i);
|
|
1498
1659
|
const idsRaw = this.getNodeParameter('connectedDataroomIds', i);
|
|
1499
|
-
const connectedDataroomIds =
|
|
1660
|
+
const connectedDataroomIds = toIdArray(idsRaw);
|
|
1500
1661
|
responseData = await apiReq('PUT', `/api/dataroom/${id}/connected`, { connectedDataroomIds });
|
|
1501
1662
|
}
|
|
1502
1663
|
else if (operation === 'getChats') {
|
|
@@ -1666,6 +1827,33 @@ class Aiteza {
|
|
|
1666
1827
|
const url = await buildFileUploadUrl(i, '/api/files');
|
|
1667
1828
|
responseData = await doUpload(i, url, 'files', 'file');
|
|
1668
1829
|
}
|
|
1830
|
+
else if (operation === 'bulkDelete') {
|
|
1831
|
+
const dataroomId = this.getNodeParameter('dataroomId', i);
|
|
1832
|
+
const ids = toIdArray(this.getNodeParameter('fileIds', i, []));
|
|
1833
|
+
(0, GenericFunctions_1.validateRequiredField)(this, ids.length > 0 ? 'ok' : '', 'File IDs');
|
|
1834
|
+
responseData = await apiReq('DELETE', `/api/dataroom/${dataroomId}/files/bulk`, { ids });
|
|
1835
|
+
}
|
|
1836
|
+
else if (operation === 'bulkMove') {
|
|
1837
|
+
const dataroomId = this.getNodeParameter('dataroomId', i);
|
|
1838
|
+
const ids = toIdArray(this.getNodeParameter('fileIds', i, []));
|
|
1839
|
+
const targetDataroom = this.getNodeParameter('targetDataroomId', i);
|
|
1840
|
+
(0, GenericFunctions_1.validateRequiredField)(this, ids.length > 0 ? 'ok' : '', 'File IDs');
|
|
1841
|
+
responseData = await apiReq('PUT', `/api/dataroom/${dataroomId}/files/bulk`, { ids, targetDataroom });
|
|
1842
|
+
}
|
|
1843
|
+
else if (operation === 'bulkPatch') {
|
|
1844
|
+
const dataroomId = this.getNodeParameter('dataroomId', i);
|
|
1845
|
+
const ids = toIdArray(this.getNodeParameter('fileIds', i, []));
|
|
1846
|
+
const reprocess = this.getNodeParameter('reprocess', i, false);
|
|
1847
|
+
(0, GenericFunctions_1.validateRequiredField)(this, ids.length > 0 ? 'ok' : '', 'File IDs');
|
|
1848
|
+
const body = { ids, reprocess };
|
|
1849
|
+
if (reprocess) {
|
|
1850
|
+
body.processingPipeline = this.getNodeParameter('processingPipeline', i);
|
|
1851
|
+
const vlmModel = this.getNodeParameter('vlmModel', i, '');
|
|
1852
|
+
if (vlmModel)
|
|
1853
|
+
body.vlmModel = vlmModel;
|
|
1854
|
+
}
|
|
1855
|
+
responseData = await apiReq('PATCH', `/api/dataroom/${dataroomId}/files/bulk`, body);
|
|
1856
|
+
}
|
|
1669
1857
|
}
|
|
1670
1858
|
// ── IMAGE ────────────────────────────────────────────────
|
|
1671
1859
|
else if (resource === 'image') {
|
|
@@ -1743,6 +1931,27 @@ class Aiteza {
|
|
|
1743
1931
|
const baseUrl = await getBaseUrl();
|
|
1744
1932
|
responseData = await doUpload(i, `${baseUrl}/api/images`, 'images', 'image');
|
|
1745
1933
|
}
|
|
1934
|
+
else if (operation === 'bulkDelete') {
|
|
1935
|
+
const dataroomId = this.getNodeParameter('dataroomId', i);
|
|
1936
|
+
const ids = toIdArray(this.getNodeParameter('imageIds', i, []));
|
|
1937
|
+
(0, GenericFunctions_1.validateRequiredField)(this, ids.length > 0 ? 'ok' : '', 'Image IDs');
|
|
1938
|
+
responseData = await apiReq('DELETE', `/api/dataroom/${dataroomId}/images/bulk`, { ids });
|
|
1939
|
+
}
|
|
1940
|
+
else if (operation === 'bulkMove') {
|
|
1941
|
+
const dataroomId = this.getNodeParameter('dataroomId', i);
|
|
1942
|
+
const ids = toIdArray(this.getNodeParameter('imageIds', i, []));
|
|
1943
|
+
const targetDataroom = this.getNodeParameter('targetDataroomId', i);
|
|
1944
|
+
(0, GenericFunctions_1.validateRequiredField)(this, ids.length > 0 ? 'ok' : '', 'Image IDs');
|
|
1945
|
+
responseData = await apiReq('PUT', `/api/dataroom/${dataroomId}/images/bulk`, { ids, targetDataroom });
|
|
1946
|
+
}
|
|
1947
|
+
else if (operation === 'bulkPatch') {
|
|
1948
|
+
const dataroomId = this.getNodeParameter('dataroomId', i);
|
|
1949
|
+
const ids = toIdArray(this.getNodeParameter('imageIds', i, []));
|
|
1950
|
+
const reprocess = this.getNodeParameter('reprocess', i, false);
|
|
1951
|
+
const patchOptions = this.getNodeParameter('patchOptions', i, {});
|
|
1952
|
+
(0, GenericFunctions_1.validateRequiredField)(this, ids.length > 0 ? 'ok' : '', 'Image IDs');
|
|
1953
|
+
responseData = await apiReq('PATCH', `/api/dataroom/${dataroomId}/images/bulk`, { ids, reprocess, ...patchOptions });
|
|
1954
|
+
}
|
|
1746
1955
|
}
|
|
1747
1956
|
// ── WEB SOURCE ───────────────────────────────────────────
|
|
1748
1957
|
else if (resource === 'webSource') {
|
|
@@ -1754,6 +1963,10 @@ class Aiteza {
|
|
|
1754
1963
|
const webSourceId = this.getNodeParameter('webSourceId', i);
|
|
1755
1964
|
responseData = await apiReq('GET', `/api/dataroom/${dataroomId}/websites/${webSourceId}`);
|
|
1756
1965
|
}
|
|
1966
|
+
else if (operation === 'getUrls') {
|
|
1967
|
+
const webSourceId = this.getNodeParameter('webSourceId', i);
|
|
1968
|
+
responseData = await apiReq('GET', `/api/dataroom/${dataroomId}/websites/${webSourceId}/urls`, {}, buildPaginationQs(i));
|
|
1969
|
+
}
|
|
1757
1970
|
else if (operation === 'add') {
|
|
1758
1971
|
const url = this.getNodeParameter('url', i);
|
|
1759
1972
|
const additionalFields = this.getNodeParameter('additionalFields', i, {});
|
|
@@ -1777,6 +1990,24 @@ class Aiteza {
|
|
|
1777
1990
|
const targetDataroomId = this.getNodeParameter('targetDataroomId', i);
|
|
1778
1991
|
responseData = await apiReq('PATCH', `/api/dataroom/${dataroomId}/websites/${webSourceId}/move`, {}, { targetDataroom: targetDataroomId });
|
|
1779
1992
|
}
|
|
1993
|
+
else if (operation === 'bulkDelete') {
|
|
1994
|
+
const ids = toIdArray(this.getNodeParameter('webSourceIds', i, []));
|
|
1995
|
+
(0, GenericFunctions_1.validateRequiredField)(this, ids.length > 0 ? 'ok' : '', 'Web Source IDs');
|
|
1996
|
+
responseData = await apiReq('DELETE', `/api/dataroom/${dataroomId}/websites/bulk`, { ids });
|
|
1997
|
+
}
|
|
1998
|
+
else if (operation === 'bulkMove') {
|
|
1999
|
+
const ids = toIdArray(this.getNodeParameter('webSourceIds', i, []));
|
|
2000
|
+
const targetDataroom = this.getNodeParameter('targetDataroomId', i);
|
|
2001
|
+
(0, GenericFunctions_1.validateRequiredField)(this, ids.length > 0 ? 'ok' : '', 'Web Source IDs');
|
|
2002
|
+
responseData = await apiReq('PUT', `/api/dataroom/${dataroomId}/websites/bulk`, { ids, targetDataroom });
|
|
2003
|
+
}
|
|
2004
|
+
else if (operation === 'bulkPatch') {
|
|
2005
|
+
const ids = toIdArray(this.getNodeParameter('webSourceIds', i, []));
|
|
2006
|
+
const reprocess = this.getNodeParameter('reprocess', i, false);
|
|
2007
|
+
const patchOptions = this.getNodeParameter('patchOptions', i, {});
|
|
2008
|
+
(0, GenericFunctions_1.validateRequiredField)(this, ids.length > 0 ? 'ok' : '', 'Web Source IDs');
|
|
2009
|
+
responseData = await apiReq('PATCH', `/api/dataroom/${dataroomId}/websites/bulk`, { ids, reprocess, ...patchOptions });
|
|
2010
|
+
}
|
|
1780
2011
|
}
|
|
1781
2012
|
// ── CHAT ─────────────────────────────────────────────────
|
|
1782
2013
|
else if (resource === 'chat') {
|
|
@@ -1826,8 +2057,8 @@ class Aiteza {
|
|
|
1826
2057
|
form.append('prompt', prompt);
|
|
1827
2058
|
form.append('model', model);
|
|
1828
2059
|
for (const key of ['connectedDatarooms', 'fileIds', 'imageIds']) {
|
|
1829
|
-
if (additionalFields[key]) {
|
|
1830
|
-
for (const val of
|
|
2060
|
+
if (additionalFields[key] !== undefined && additionalFields[key] !== '') {
|
|
2061
|
+
for (const val of toIdArray(additionalFields[key])) {
|
|
1831
2062
|
form.append(key, val);
|
|
1832
2063
|
}
|
|
1833
2064
|
}
|
|
@@ -1857,8 +2088,8 @@ class Aiteza {
|
|
|
1857
2088
|
form.append('prompt', prompt);
|
|
1858
2089
|
form.append('model', model);
|
|
1859
2090
|
for (const key of ['connectedDatarooms', 'fileIds', 'imageIds']) {
|
|
1860
|
-
if (additionalFields[key]) {
|
|
1861
|
-
for (const val of
|
|
2091
|
+
if (additionalFields[key] !== undefined && additionalFields[key] !== '') {
|
|
2092
|
+
for (const val of toIdArray(additionalFields[key])) {
|
|
1862
2093
|
form.append(key, val);
|
|
1863
2094
|
}
|
|
1864
2095
|
}
|
|
@@ -1882,8 +2113,8 @@ class Aiteza {
|
|
|
1882
2113
|
form.append('prompt', prompt);
|
|
1883
2114
|
form.append('model', model);
|
|
1884
2115
|
for (const key of ['connectedDatarooms', 'fileIds', 'imageIds']) {
|
|
1885
|
-
if (additionalFields[key]) {
|
|
1886
|
-
for (const val of
|
|
2116
|
+
if (additionalFields[key] !== undefined && additionalFields[key] !== '') {
|
|
2117
|
+
for (const val of toIdArray(additionalFields[key])) {
|
|
1887
2118
|
form.append(key, val);
|
|
1888
2119
|
}
|
|
1889
2120
|
}
|
|
@@ -1923,11 +2154,12 @@ class Aiteza {
|
|
|
1923
2154
|
else if (resource === 'search') {
|
|
1924
2155
|
if (operation === 'hybridSearch') {
|
|
1925
2156
|
const query = this.getNodeParameter('searchQuery', i);
|
|
1926
|
-
const dataroomIdsRaw = this.getNodeParameter('dataroomIds', i,
|
|
2157
|
+
const dataroomIdsRaw = this.getNodeParameter('dataroomIds', i, []);
|
|
1927
2158
|
const additionalFields = this.getNodeParameter('additionalFields', i, {});
|
|
1928
2159
|
const body = { query };
|
|
1929
|
-
|
|
1930
|
-
|
|
2160
|
+
const dataroomIds = toIdArray(dataroomIdsRaw);
|
|
2161
|
+
if (dataroomIds.length > 0)
|
|
2162
|
+
body.dataroomIds = dataroomIds;
|
|
1931
2163
|
for (const key of ['topK', 'vectorThreshold', 'enableReranking', 'enableFullTextSearch']) {
|
|
1932
2164
|
if (additionalFields[key] !== undefined)
|
|
1933
2165
|
body[key] = additionalFields[key];
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class AitezaProgress implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AitezaProgress = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Helpers for reading the AITEZA Trigger's webhook body
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
function isAitezaTriggerType(type) {
|
|
9
|
+
return typeof type === 'string' && /(^|\.)aitezaTrigger$/i.test(type);
|
|
10
|
+
}
|
|
11
|
+
function findAitezaTriggerParentName(ef) {
|
|
12
|
+
try {
|
|
13
|
+
const parents = ef.getParentNodes(ef.getNode().name);
|
|
14
|
+
return parents.find((p) => isAitezaTriggerType(p.type) && !p.disabled)?.name;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Look up a value first on the current input item, then on the original
|
|
22
|
+
* AITEZA Trigger output (so intermediate Set/IF/SplitOut nodes that strip
|
|
23
|
+
* fields don't break callbacks).
|
|
24
|
+
*/
|
|
25
|
+
function readFromTrigger(ef, itemIndex, picker) {
|
|
26
|
+
try {
|
|
27
|
+
const items = ef.getInputData();
|
|
28
|
+
const direct = picker(items[itemIndex]?.json);
|
|
29
|
+
if (direct !== undefined && direct !== null && direct !== '')
|
|
30
|
+
return direct;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// ignore
|
|
34
|
+
}
|
|
35
|
+
const triggerName = findAitezaTriggerParentName(ef);
|
|
36
|
+
if (!triggerName)
|
|
37
|
+
return undefined;
|
|
38
|
+
try {
|
|
39
|
+
const proxy = ef.getWorkflowDataProxy(itemIndex);
|
|
40
|
+
const triggerItems = proxy.$items(triggerName) ?? [];
|
|
41
|
+
for (const item of triggerItems) {
|
|
42
|
+
const value = picker(item?.json);
|
|
43
|
+
if (value !== undefined && value !== null && value !== '')
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// ignore
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
function readTriggerBodyField(ef, itemIndex, field) {
|
|
53
|
+
return readFromTrigger(ef, itemIndex, (json) => {
|
|
54
|
+
const body = json?.body ?? {};
|
|
55
|
+
const fromBody = body[field];
|
|
56
|
+
if (fromBody !== undefined && fromBody !== null && fromBody !== '') {
|
|
57
|
+
return String(fromBody);
|
|
58
|
+
}
|
|
59
|
+
// Also allow callers to put the value at the top level of an item
|
|
60
|
+
// (e.g. after a Set node).
|
|
61
|
+
const fromTop = json?.[field];
|
|
62
|
+
if (fromTop !== undefined && fromTop !== null && fromTop !== '') {
|
|
63
|
+
return String(fromTop);
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Node
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
class AitezaProgress {
|
|
72
|
+
description = {
|
|
73
|
+
displayName: 'AITEZA Progress',
|
|
74
|
+
name: 'aitezaProgress',
|
|
75
|
+
icon: 'file:aiteza.svg',
|
|
76
|
+
group: ['transform'],
|
|
77
|
+
version: 1,
|
|
78
|
+
subtitle: '={{$parameter["step"]}}',
|
|
79
|
+
description: 'Reports workflow progress back to the AITEZA backend that triggered ' +
|
|
80
|
+
'this workflow. Reads callbackUrl / executionId / callbackToken from ' +
|
|
81
|
+
'the upstream AITEZA Trigger webhook body and POSTs to ' +
|
|
82
|
+
'{callbackUrl}/api/internal/workflow/{executionId}/progress.',
|
|
83
|
+
defaults: { name: 'AITEZA Progress' },
|
|
84
|
+
inputs: ['main'],
|
|
85
|
+
outputs: ['main'],
|
|
86
|
+
credentials: [],
|
|
87
|
+
properties: [
|
|
88
|
+
{
|
|
89
|
+
displayName: 'Step',
|
|
90
|
+
name: 'step',
|
|
91
|
+
type: 'string',
|
|
92
|
+
default: '',
|
|
93
|
+
required: true,
|
|
94
|
+
placeholder: 'Fetching files',
|
|
95
|
+
description: 'Short, human-readable label for the current step',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
displayName: 'Step Index',
|
|
99
|
+
name: 'stepIndex',
|
|
100
|
+
type: 'number',
|
|
101
|
+
default: 1,
|
|
102
|
+
typeOptions: { minValue: 0 },
|
|
103
|
+
description: '1-based index of the current step',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
displayName: 'Total Steps',
|
|
107
|
+
name: 'totalSteps',
|
|
108
|
+
type: 'number',
|
|
109
|
+
default: 1,
|
|
110
|
+
typeOptions: { minValue: 0 },
|
|
111
|
+
description: 'Total number of steps in this workflow',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
displayName: 'Detail',
|
|
115
|
+
name: 'detail',
|
|
116
|
+
type: 'string',
|
|
117
|
+
default: '',
|
|
118
|
+
typeOptions: { rows: 2 },
|
|
119
|
+
description: 'Optional longer description of what is happening right now',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
displayName: 'Additional Fields (JSON)',
|
|
123
|
+
name: 'additionalFields',
|
|
124
|
+
type: 'json',
|
|
125
|
+
default: '',
|
|
126
|
+
placeholder: '{ "percent": 42 }',
|
|
127
|
+
description: 'Optional JSON object that will be merged into the progress payload (e.g. percent, items processed, etc.)',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
displayName: 'Options',
|
|
131
|
+
name: 'options',
|
|
132
|
+
type: 'collection',
|
|
133
|
+
placeholder: 'Add Option',
|
|
134
|
+
default: {},
|
|
135
|
+
options: [
|
|
136
|
+
{
|
|
137
|
+
displayName: 'Callback URL Override',
|
|
138
|
+
name: 'callbackUrlOverride',
|
|
139
|
+
type: 'string',
|
|
140
|
+
default: '',
|
|
141
|
+
description: 'Override the callback URL from the AITEZA Trigger. Without trailing slash.',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
displayName: 'Execution ID Override',
|
|
145
|
+
name: 'executionIdOverride',
|
|
146
|
+
type: 'string',
|
|
147
|
+
default: '',
|
|
148
|
+
description: 'Override the execution ID from the AITEZA Trigger',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
displayName: 'Callback Token Override',
|
|
152
|
+
name: 'callbackTokenOverride',
|
|
153
|
+
type: 'string',
|
|
154
|
+
typeOptions: { password: true },
|
|
155
|
+
default: '',
|
|
156
|
+
description: 'Override the callback token from the AITEZA Trigger',
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
displayName: 'Timeout (ms)',
|
|
160
|
+
name: 'timeout',
|
|
161
|
+
type: 'number',
|
|
162
|
+
default: 5000,
|
|
163
|
+
typeOptions: { minValue: 100 },
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
displayName: 'Fail Workflow on Error',
|
|
167
|
+
name: 'failOnError',
|
|
168
|
+
type: 'boolean',
|
|
169
|
+
default: false,
|
|
170
|
+
description: 'Whether to fail the workflow if the progress callback fails. Defaults to false so progress reports never break the main flow.',
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
};
|
|
176
|
+
async execute() {
|
|
177
|
+
const items = this.getInputData();
|
|
178
|
+
const returnData = [];
|
|
179
|
+
for (let i = 0; i < items.length; i++) {
|
|
180
|
+
const step = this.getNodeParameter('step', i);
|
|
181
|
+
const stepIndex = this.getNodeParameter('stepIndex', i, 0);
|
|
182
|
+
const totalSteps = this.getNodeParameter('totalSteps', i, 0);
|
|
183
|
+
const detail = this.getNodeParameter('detail', i, '');
|
|
184
|
+
const additionalFieldsRaw = this.getNodeParameter('additionalFields', i, '');
|
|
185
|
+
const options = this.getNodeParameter('options', i, {});
|
|
186
|
+
const callbackUrlOverride = options.callbackUrlOverride || '';
|
|
187
|
+
const executionIdOverride = options.executionIdOverride || '';
|
|
188
|
+
const callbackTokenOverride = options.callbackTokenOverride || '';
|
|
189
|
+
const timeout = options.timeout ?? 5000;
|
|
190
|
+
const failOnError = options.failOnError ?? false;
|
|
191
|
+
const callbackUrl = (callbackUrlOverride || readTriggerBodyField(this, i, 'callbackUrl') || '').replace(/\/+$/, '');
|
|
192
|
+
const executionId = executionIdOverride || readTriggerBodyField(this, i, 'executionId') || '';
|
|
193
|
+
const callbackToken = callbackTokenOverride || readTriggerBodyField(this, i, 'callbackToken') || '';
|
|
194
|
+
// Build payload
|
|
195
|
+
const payload = { step };
|
|
196
|
+
if (Number.isFinite(stepIndex) && stepIndex > 0)
|
|
197
|
+
payload.stepIndex = stepIndex;
|
|
198
|
+
if (Number.isFinite(totalSteps) && totalSteps > 0)
|
|
199
|
+
payload.totalSteps = totalSteps;
|
|
200
|
+
if (detail)
|
|
201
|
+
payload.detail = detail;
|
|
202
|
+
if (additionalFieldsRaw) {
|
|
203
|
+
let parsed;
|
|
204
|
+
if (typeof additionalFieldsRaw === 'string') {
|
|
205
|
+
try {
|
|
206
|
+
parsed = JSON.parse(additionalFieldsRaw);
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
if (failOnError) {
|
|
210
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `"Additional Fields" is not valid JSON: ${err.message}`, { itemIndex: i });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
else if (typeof additionalFieldsRaw === 'object') {
|
|
215
|
+
parsed = additionalFieldsRaw;
|
|
216
|
+
}
|
|
217
|
+
if (parsed && typeof parsed === 'object')
|
|
218
|
+
Object.assign(payload, parsed);
|
|
219
|
+
}
|
|
220
|
+
const reportMeta = {
|
|
221
|
+
sent: false,
|
|
222
|
+
callbackUrl,
|
|
223
|
+
executionId,
|
|
224
|
+
payload,
|
|
225
|
+
};
|
|
226
|
+
if (!callbackUrl || !executionId) {
|
|
227
|
+
reportMeta.skipped = true;
|
|
228
|
+
reportMeta.reason = !callbackUrl
|
|
229
|
+
? 'No callbackUrl on AITEZA Trigger body'
|
|
230
|
+
: 'No executionId on AITEZA Trigger body';
|
|
231
|
+
if (failOnError) {
|
|
232
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Cannot send progress: ${reportMeta.reason}`, { itemIndex: i });
|
|
233
|
+
}
|
|
234
|
+
returnData.push({
|
|
235
|
+
json: { ...items[i].json, _progressReport: reportMeta },
|
|
236
|
+
pairedItem: { item: i },
|
|
237
|
+
});
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
const requestOptions = {
|
|
241
|
+
method: 'POST',
|
|
242
|
+
url: `${callbackUrl}/api/internal/workflow/${encodeURIComponent(executionId)}/progress`,
|
|
243
|
+
headers: {
|
|
244
|
+
'Content-Type': 'application/json',
|
|
245
|
+
...(callbackToken ? { 'X-Callback-Token': callbackToken } : {}),
|
|
246
|
+
},
|
|
247
|
+
body: payload,
|
|
248
|
+
json: true,
|
|
249
|
+
timeout,
|
|
250
|
+
};
|
|
251
|
+
try {
|
|
252
|
+
await this.helpers.httpRequest(requestOptions);
|
|
253
|
+
reportMeta.sent = true;
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
reportMeta.sent = false;
|
|
257
|
+
reportMeta.error =
|
|
258
|
+
error?.message ?? error?.response?.statusText ?? 'Unknown error';
|
|
259
|
+
if (failOnError) {
|
|
260
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Progress callback failed: ${reportMeta.error}`, { itemIndex: i });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
returnData.push({
|
|
264
|
+
json: { ...items[i].json, _progressReport: reportMeta },
|
|
265
|
+
pairedItem: { item: i },
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return [returnData];
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
exports.AitezaProgress = AitezaProgress;
|
|
@@ -9,7 +9,8 @@ class AitezaTrigger {
|
|
|
9
9
|
group: ['trigger'],
|
|
10
10
|
version: 1,
|
|
11
11
|
description: 'Starts the workflow when an AITEZA event is received via webhook. ' +
|
|
12
|
-
'
|
|
12
|
+
'Forwards the calling user\'s bearer token (from body.user.bearerToken) to downstream AITEZA nodes ' +
|
|
13
|
+
'so they act on behalf of the triggering user. Falls back to the request\'s Authorization header if no user token is present.',
|
|
13
14
|
defaults: { name: 'AITEZA Trigger' },
|
|
14
15
|
inputs: [],
|
|
15
16
|
outputs: ['main'],
|
|
@@ -47,21 +48,18 @@ class AitezaTrigger {
|
|
|
47
48
|
{
|
|
48
49
|
name: 'Immediately',
|
|
49
50
|
value: 'onReceived',
|
|
50
|
-
description: 'Respond immediately with 200 OK as soon as the webhook is received'
|
|
51
|
+
description: 'Respond immediately with 200 OK as soon as the webhook is received. ' +
|
|
52
|
+
'Use the "AITEZA Workflow Result" node at the end of your workflow to send results back.',
|
|
51
53
|
},
|
|
52
54
|
{
|
|
53
55
|
name: 'When Last Node Finishes',
|
|
54
56
|
value: 'lastNode',
|
|
55
57
|
description: 'Respond with data from the last node in the workflow',
|
|
56
58
|
},
|
|
57
|
-
{
|
|
58
|
-
name: 'Using \'Respond to Webhook\' Node',
|
|
59
|
-
value: 'responseNode',
|
|
60
|
-
description: 'Respond using the data from a downstream "Respond to Webhook" node',
|
|
61
|
-
},
|
|
62
59
|
],
|
|
63
|
-
default: '
|
|
64
|
-
description: 'When and how to respond to the webhook'
|
|
60
|
+
default: 'onReceived',
|
|
61
|
+
description: 'When and how to respond to the webhook. Use "Immediately" together with the ' +
|
|
62
|
+
'"AITEZA Workflow Result" node to send results asynchronously.',
|
|
65
63
|
},
|
|
66
64
|
{
|
|
67
65
|
displayName: 'AITEZA Base URL',
|
|
@@ -77,8 +75,17 @@ class AitezaTrigger {
|
|
|
77
75
|
const body = this.getBodyData();
|
|
78
76
|
const headers = this.getHeaderData();
|
|
79
77
|
const query = this.getQueryData();
|
|
78
|
+
// AITEZA places the calling user's bearer token inside the body (body.user.bearerToken)
|
|
79
|
+
// so that downstream nodes can act on behalf of the user. The request's own
|
|
80
|
+
// Authorization header is typically the service-account token used by AITEZA to
|
|
81
|
+
// call the webhook itself – not what we want for delegated calls.
|
|
82
|
+
const user = body?.user ?? {};
|
|
83
|
+
const userBearer = user.bearerToken ?? '';
|
|
80
84
|
const authHeader = headers.authorization || headers.Authorization || '';
|
|
81
|
-
const
|
|
85
|
+
const headerToken = authHeader.replace(/^Bearer\s+/i, '');
|
|
86
|
+
// Prefer the user-context token. Fall back to the header token (e.g. when the
|
|
87
|
+
// webhook is called by something other than AITEZA).
|
|
88
|
+
const authToken = userBearer || headerToken;
|
|
82
89
|
const baseUrl = this.getNodeParameter('baseUrl', '').replace(/\/+$/, '');
|
|
83
90
|
const outputData = {
|
|
84
91
|
body,
|
|
@@ -87,6 +94,10 @@ class AitezaTrigger {
|
|
|
87
94
|
};
|
|
88
95
|
if (authToken)
|
|
89
96
|
outputData._authToken = authToken;
|
|
97
|
+
// Expose the service-account token separately in case a workflow explicitly
|
|
98
|
+
// needs to call AITEZA with elevated/service privileges.
|
|
99
|
+
if (headerToken && headerToken !== authToken)
|
|
100
|
+
outputData._serviceAuthToken = headerToken;
|
|
90
101
|
if (baseUrl)
|
|
91
102
|
outputData._baseUrl = baseUrl;
|
|
92
103
|
return {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class AitezaWorkflowResult implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AitezaWorkflowResult = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Helpers for reading the AITEZA Trigger's webhook body
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
function isAitezaTriggerType(type) {
|
|
9
|
+
return typeof type === 'string' && /(^|\.)aitezaTrigger$/i.test(type);
|
|
10
|
+
}
|
|
11
|
+
function findAitezaTriggerParentName(ef) {
|
|
12
|
+
try {
|
|
13
|
+
const parents = ef.getParentNodes(ef.getNode().name);
|
|
14
|
+
return parents.find((p) => isAitezaTriggerType(p.type) && !p.disabled)?.name;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Look up a value first on the current input item, then on the original
|
|
22
|
+
* AITEZA Trigger output (so intermediate Set/IF/SplitOut nodes that strip
|
|
23
|
+
* fields don't break callbacks).
|
|
24
|
+
*/
|
|
25
|
+
function readFromTrigger(ef, itemIndex, picker) {
|
|
26
|
+
try {
|
|
27
|
+
const items = ef.getInputData();
|
|
28
|
+
const direct = picker(items[itemIndex]?.json);
|
|
29
|
+
if (direct !== undefined && direct !== null && direct !== '')
|
|
30
|
+
return direct;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// ignore
|
|
34
|
+
}
|
|
35
|
+
const triggerName = findAitezaTriggerParentName(ef);
|
|
36
|
+
if (!triggerName)
|
|
37
|
+
return undefined;
|
|
38
|
+
try {
|
|
39
|
+
const proxy = ef.getWorkflowDataProxy(itemIndex);
|
|
40
|
+
const triggerItems = proxy.$items(triggerName) ?? [];
|
|
41
|
+
for (const item of triggerItems) {
|
|
42
|
+
const value = picker(item?.json);
|
|
43
|
+
if (value !== undefined && value !== null && value !== '')
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// ignore
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
function readTriggerBodyField(ef, itemIndex, field) {
|
|
53
|
+
return readFromTrigger(ef, itemIndex, (json) => {
|
|
54
|
+
const body = json?.body ?? {};
|
|
55
|
+
const fromBody = body[field];
|
|
56
|
+
if (fromBody !== undefined && fromBody !== null && fromBody !== '') {
|
|
57
|
+
return String(fromBody);
|
|
58
|
+
}
|
|
59
|
+
const fromTop = json?.[field];
|
|
60
|
+
if (fromTop !== undefined && fromTop !== null && fromTop !== '') {
|
|
61
|
+
return String(fromTop);
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Node
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
class AitezaWorkflowResult {
|
|
70
|
+
description = {
|
|
71
|
+
displayName: 'AITEZA Workflow Result',
|
|
72
|
+
name: 'aitezaWorkflowResult',
|
|
73
|
+
icon: 'file:aiteza.svg',
|
|
74
|
+
group: ['output'],
|
|
75
|
+
version: 1,
|
|
76
|
+
subtitle: 'Send result to AITEZA',
|
|
77
|
+
description: 'Sends the final workflow result back to the AITEZA backend. ' +
|
|
78
|
+
'Reads callbackUrl / executionId / callbackToken from the upstream ' +
|
|
79
|
+
'AITEZA Trigger webhook body and POSTs to ' +
|
|
80
|
+
'{callbackUrl}/api/internal/workflow/{executionId}/result. ' +
|
|
81
|
+
'Use this instead of "Respond to Webhook" which is not compatible ' +
|
|
82
|
+
'with custom trigger nodes.',
|
|
83
|
+
defaults: { name: 'AITEZA Workflow Result' },
|
|
84
|
+
inputs: ['main'],
|
|
85
|
+
outputs: ['main'],
|
|
86
|
+
credentials: [],
|
|
87
|
+
properties: [
|
|
88
|
+
{
|
|
89
|
+
displayName: 'Result Data',
|
|
90
|
+
name: 'resultData',
|
|
91
|
+
type: 'json',
|
|
92
|
+
default: '',
|
|
93
|
+
placeholder: '={{ $json }}',
|
|
94
|
+
description: 'The result payload to send back to AITEZA. Can be a JSON object or an expression that resolves to one. If empty, the entire input item JSON is sent.',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
displayName: 'Status',
|
|
98
|
+
name: 'status',
|
|
99
|
+
type: 'options',
|
|
100
|
+
options: [
|
|
101
|
+
{
|
|
102
|
+
name: 'Success',
|
|
103
|
+
value: 'success',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'Error',
|
|
107
|
+
value: 'error',
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
default: 'success',
|
|
111
|
+
description: 'The result status to report back to AITEZA',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
displayName: 'Message',
|
|
115
|
+
name: 'message',
|
|
116
|
+
type: 'string',
|
|
117
|
+
default: '',
|
|
118
|
+
placeholder: 'Workflow completed successfully',
|
|
119
|
+
description: 'Optional human-readable message to include with the result',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
displayName: 'Options',
|
|
123
|
+
name: 'options',
|
|
124
|
+
type: 'collection',
|
|
125
|
+
placeholder: 'Add Option',
|
|
126
|
+
default: {},
|
|
127
|
+
options: [
|
|
128
|
+
{
|
|
129
|
+
displayName: 'Callback URL Override',
|
|
130
|
+
name: 'callbackUrlOverride',
|
|
131
|
+
type: 'string',
|
|
132
|
+
default: '',
|
|
133
|
+
description: 'Override the callback URL from the AITEZA Trigger. Without trailing slash.',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
displayName: 'Execution ID Override',
|
|
137
|
+
name: 'executionIdOverride',
|
|
138
|
+
type: 'string',
|
|
139
|
+
default: '',
|
|
140
|
+
description: 'Override the execution ID from the AITEZA Trigger',
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
displayName: 'Callback Token Override',
|
|
144
|
+
name: 'callbackTokenOverride',
|
|
145
|
+
type: 'string',
|
|
146
|
+
typeOptions: { password: true },
|
|
147
|
+
default: '',
|
|
148
|
+
description: 'Override the callback token from the AITEZA Trigger',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
displayName: 'Timeout (ms)',
|
|
152
|
+
name: 'timeout',
|
|
153
|
+
type: 'number',
|
|
154
|
+
default: 10000,
|
|
155
|
+
typeOptions: { minValue: 100 },
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
displayName: 'Fail Workflow on Error',
|
|
159
|
+
name: 'failOnError',
|
|
160
|
+
type: 'boolean',
|
|
161
|
+
default: true,
|
|
162
|
+
description: 'Whether to fail the workflow if the result callback fails. Defaults to true since the result is typically critical.',
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
};
|
|
168
|
+
async execute() {
|
|
169
|
+
const items = this.getInputData();
|
|
170
|
+
const returnData = [];
|
|
171
|
+
for (let i = 0; i < items.length; i++) {
|
|
172
|
+
const resultDataRaw = this.getNodeParameter('resultData', i, '');
|
|
173
|
+
const status = this.getNodeParameter('status', i, 'success');
|
|
174
|
+
const message = this.getNodeParameter('message', i, '');
|
|
175
|
+
const options = this.getNodeParameter('options', i, {});
|
|
176
|
+
const callbackUrlOverride = options.callbackUrlOverride || '';
|
|
177
|
+
const executionIdOverride = options.executionIdOverride || '';
|
|
178
|
+
const callbackTokenOverride = options.callbackTokenOverride || '';
|
|
179
|
+
const timeout = options.timeout ?? 10000;
|
|
180
|
+
const failOnError = options.failOnError ?? true;
|
|
181
|
+
const callbackUrl = (callbackUrlOverride || readTriggerBodyField(this, i, 'callbackUrl') || '').replace(/\/+$/, '');
|
|
182
|
+
const executionId = executionIdOverride || readTriggerBodyField(this, i, 'executionId') || '';
|
|
183
|
+
const callbackToken = callbackTokenOverride || readTriggerBodyField(this, i, 'callbackToken') || '';
|
|
184
|
+
// Build result payload
|
|
185
|
+
let resultPayload;
|
|
186
|
+
if (!resultDataRaw || (typeof resultDataRaw === 'string' && resultDataRaw.trim() === '')) {
|
|
187
|
+
// Default: send the entire input item JSON as the result data
|
|
188
|
+
resultPayload = { ...items[i].json };
|
|
189
|
+
}
|
|
190
|
+
else if (typeof resultDataRaw === 'string') {
|
|
191
|
+
try {
|
|
192
|
+
resultPayload = JSON.parse(resultDataRaw);
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
if (failOnError) {
|
|
196
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `"Result Data" is not valid JSON: ${err.message}`, { itemIndex: i });
|
|
197
|
+
}
|
|
198
|
+
resultPayload = { raw: resultDataRaw };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
resultPayload = resultDataRaw;
|
|
203
|
+
}
|
|
204
|
+
const payload = {
|
|
205
|
+
status,
|
|
206
|
+
data: resultPayload,
|
|
207
|
+
};
|
|
208
|
+
if (message)
|
|
209
|
+
payload.message = message;
|
|
210
|
+
const reportMeta = {
|
|
211
|
+
sent: false,
|
|
212
|
+
callbackUrl,
|
|
213
|
+
executionId,
|
|
214
|
+
payload,
|
|
215
|
+
};
|
|
216
|
+
if (!callbackUrl || !executionId) {
|
|
217
|
+
reportMeta.skipped = true;
|
|
218
|
+
reportMeta.reason = !callbackUrl
|
|
219
|
+
? 'No callbackUrl on AITEZA Trigger body'
|
|
220
|
+
: 'No executionId on AITEZA Trigger body';
|
|
221
|
+
if (failOnError) {
|
|
222
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Cannot send workflow result: ${reportMeta.reason}`, { itemIndex: i });
|
|
223
|
+
}
|
|
224
|
+
returnData.push({
|
|
225
|
+
json: { ...items[i].json, _workflowResult: reportMeta },
|
|
226
|
+
pairedItem: { item: i },
|
|
227
|
+
});
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const requestOptions = {
|
|
231
|
+
method: 'POST',
|
|
232
|
+
url: `${callbackUrl}/api/internal/workflow/${encodeURIComponent(executionId)}/result`,
|
|
233
|
+
headers: {
|
|
234
|
+
'Content-Type': 'application/json',
|
|
235
|
+
...(callbackToken ? { 'X-Callback-Token': callbackToken } : {}),
|
|
236
|
+
},
|
|
237
|
+
body: payload,
|
|
238
|
+
json: true,
|
|
239
|
+
timeout,
|
|
240
|
+
};
|
|
241
|
+
try {
|
|
242
|
+
await this.helpers.httpRequest(requestOptions);
|
|
243
|
+
reportMeta.sent = true;
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
reportMeta.sent = false;
|
|
247
|
+
reportMeta.error =
|
|
248
|
+
error?.message ?? error?.response?.statusText ?? 'Unknown error';
|
|
249
|
+
if (failOnError) {
|
|
250
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Workflow result callback failed: ${reportMeta.error}`, { itemIndex: i });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
returnData.push({
|
|
254
|
+
json: { ...items[i].json, _workflowResult: reportMeta },
|
|
255
|
+
pairedItem: { item: i },
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return [returnData];
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
exports.AitezaWorkflowResult = AitezaWorkflowResult;
|
|
@@ -5,6 +5,12 @@ export interface AitezaAuthContext {
|
|
|
5
5
|
}
|
|
6
6
|
export declare function getUpstreamAuthToken(ef: IExecuteFunctions): string | undefined;
|
|
7
7
|
export declare function getUpstreamBaseUrl(ef: IExecuteFunctions): string | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Returns true when the token is a JWT with an `exp` claim that is in the past
|
|
10
|
+
* (with a small skew). Returns false when the token is not a parsable JWT or
|
|
11
|
+
* has no `exp` (we assume valid in that case – the server is the source of truth).
|
|
12
|
+
*/
|
|
13
|
+
export declare function isJwtExpired(token: string | undefined, skewSeconds?: number): boolean;
|
|
8
14
|
export declare function aitezaApiRequest(this: IExecuteFunctions | ILoadOptionsFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject | IDataObject[], qs?: IDataObject, extraOpts?: Partial<IHttpRequestOptions>, authCtx?: AitezaAuthContext): Promise<any>;
|
|
9
15
|
export declare function aitezaApiRequestFullResponse(this: IExecuteFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject, qs?: IDataObject, extraOpts?: Partial<IHttpRequestOptions>, authCtx?: AitezaAuthContext): Promise<{
|
|
10
16
|
body: any;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getUpstreamAuthToken = getUpstreamAuthToken;
|
|
4
4
|
exports.getUpstreamBaseUrl = getUpstreamBaseUrl;
|
|
5
|
+
exports.isJwtExpired = isJwtExpired;
|
|
5
6
|
exports.aitezaApiRequest = aitezaApiRequest;
|
|
6
7
|
exports.aitezaApiRequestFullResponse = aitezaApiRequestFullResponse;
|
|
7
8
|
exports.loadDatarooms = loadDatarooms;
|
|
@@ -15,7 +16,44 @@ exports.loadStandaloneFiles = loadStandaloneFiles;
|
|
|
15
16
|
exports.loadStandaloneImages = loadStandaloneImages;
|
|
16
17
|
exports.validateRequiredField = validateRequiredField;
|
|
17
18
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
19
|
+
// Match the AITEZA Trigger node by its type name. The full node-type string
|
|
20
|
+
// includes the package namespace (e.g. "@aiteza/n8n-nodes-aiteza.aitezaTrigger"),
|
|
21
|
+
// so we match on the suffix.
|
|
22
|
+
function isAitezaTriggerType(type) {
|
|
23
|
+
return typeof type === 'string' && /(^|\.)aitezaTrigger$/i.test(type);
|
|
24
|
+
}
|
|
25
|
+
function findAitezaTriggerParentName(ef) {
|
|
26
|
+
try {
|
|
27
|
+
const currentNodeName = ef.getNode().name;
|
|
28
|
+
const parents = ef.getParentNodes(currentNodeName);
|
|
29
|
+
const trigger = parents.find((p) => isAitezaTriggerType(p.type) && !p.disabled);
|
|
30
|
+
return trigger?.name;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function readFromTriggerItems(ef, picker) {
|
|
37
|
+
const triggerName = findAitezaTriggerParentName(ef);
|
|
38
|
+
if (!triggerName)
|
|
39
|
+
return undefined;
|
|
40
|
+
try {
|
|
41
|
+
const proxy = ef.getWorkflowDataProxy(0);
|
|
42
|
+
const triggerItems = proxy.$items(triggerName) ?? [];
|
|
43
|
+
for (const item of triggerItems) {
|
|
44
|
+
const value = picker(item?.json);
|
|
45
|
+
if (value !== undefined && value !== null && value !== '')
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// proxy/items unavailable – ignore
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
18
54
|
function getUpstreamAuthToken(ef) {
|
|
55
|
+
// 1. Prefer the token on the immediate input items (cheap and matches the
|
|
56
|
+
// documented contract of forwarding `_authToken`).
|
|
19
57
|
try {
|
|
20
58
|
const items = ef.getInputData();
|
|
21
59
|
for (const item of items) {
|
|
@@ -27,7 +65,10 @@ function getUpstreamAuthToken(ef) {
|
|
|
27
65
|
catch {
|
|
28
66
|
// getInputData may not be available in every context
|
|
29
67
|
}
|
|
30
|
-
|
|
68
|
+
// 2. Fall back to the AITEZA Trigger node's original output. This makes the
|
|
69
|
+
// node resilient to intermediate transformations (Set/Edit Fields, Split
|
|
70
|
+
// Out, IF, etc.) that strip the `_authToken` field from items.
|
|
71
|
+
return readFromTriggerItems(ef, (json) => json?._authToken);
|
|
31
72
|
}
|
|
32
73
|
function getUpstreamBaseUrl(ef) {
|
|
33
74
|
try {
|
|
@@ -41,7 +82,46 @@ function getUpstreamBaseUrl(ef) {
|
|
|
41
82
|
catch {
|
|
42
83
|
// ignore
|
|
43
84
|
}
|
|
44
|
-
|
|
85
|
+
const fromTrigger = readFromTriggerItems(ef, (json) => json?._baseUrl);
|
|
86
|
+
return fromTrigger ? fromTrigger.replace(/\/+$/, '') : undefined;
|
|
87
|
+
}
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// JWT helpers
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
function decodeJwtPayload(token) {
|
|
92
|
+
try {
|
|
93
|
+
const payload = token.split('.')[1];
|
|
94
|
+
if (!payload)
|
|
95
|
+
return undefined;
|
|
96
|
+
const normalized = payload.replace(/-/g, '+').replace(/_/g, '/');
|
|
97
|
+
const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4);
|
|
98
|
+
// atob is available in Node 16+ and the browser; decodes base64 to a
|
|
99
|
+
// binary string. JWT payloads are UTF-8 JSON; for the `exp` claim a
|
|
100
|
+
// binary-string decode is sufficient.
|
|
101
|
+
const decoded = globalThis.atob
|
|
102
|
+
? globalThis.atob(padded)
|
|
103
|
+
: '';
|
|
104
|
+
if (!decoded)
|
|
105
|
+
return undefined;
|
|
106
|
+
return JSON.parse(decoded);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Returns true when the token is a JWT with an `exp` claim that is in the past
|
|
114
|
+
* (with a small skew). Returns false when the token is not a parsable JWT or
|
|
115
|
+
* has no `exp` (we assume valid in that case – the server is the source of truth).
|
|
116
|
+
*/
|
|
117
|
+
function isJwtExpired(token, skewSeconds = 30) {
|
|
118
|
+
if (!token)
|
|
119
|
+
return true;
|
|
120
|
+
const payload = decodeJwtPayload(token);
|
|
121
|
+
if (!payload?.exp)
|
|
122
|
+
return false;
|
|
123
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
124
|
+
return payload.exp <= nowSeconds + skewSeconds;
|
|
45
125
|
}
|
|
46
126
|
// ---------------------------------------------------------------------------
|
|
47
127
|
// Internal helpers
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiteza/n8n-nodes-aiteza",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "n8n Community Node for the AITEZA REST API (Datarooms, Files, Chat, Search, Workflows)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
],
|
|
35
35
|
"nodes": [
|
|
36
36
|
"dist/nodes/Aiteza/Aiteza.node.js",
|
|
37
|
-
"dist/nodes/Aiteza/AitezaTrigger.node.js"
|
|
37
|
+
"dist/nodes/Aiteza/AitezaTrigger.node.js",
|
|
38
|
+
"dist/nodes/Aiteza/AitezaProgress.node.js",
|
|
39
|
+
"dist/nodes/Aiteza/AitezaWorkflowResult.node.js"
|
|
38
40
|
]
|
|
39
41
|
},
|
|
40
42
|
"dependencies": {
|