@aiteza/n8n-nodes-aiteza 0.2.0 → 0.2.2

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,11 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.Aiteza = void 0;
4
7
  const n8n_workflow_1 = require("n8n-workflow");
8
+ const form_data_1 = __importDefault(require("form-data"));
5
9
  const GenericFunctions_1 = require("./GenericFunctions");
6
10
  class Aiteza {
7
11
  description = {
@@ -20,7 +24,7 @@ class Aiteza {
20
24
  name: 'aitezaOAuth2Api',
21
25
  required: true,
22
26
  displayOptions: {
23
- show: { authentication: ['credentials'] },
27
+ show: { authSource: ['credentials'] },
24
28
  },
25
29
  },
26
30
  ],
@@ -30,8 +34,9 @@ class Aiteza {
30
34
  // ------------------------------------------------------------------
31
35
  {
32
36
  displayName: 'Authentication',
33
- name: 'authentication',
37
+ name: 'authSource',
34
38
  type: 'options',
39
+ noDataExpression: true,
35
40
  options: [
36
41
  {
37
42
  name: 'OAuth2 Credentials',
@@ -41,7 +46,7 @@ class Aiteza {
41
46
  {
42
47
  name: 'From AITEZA Trigger',
43
48
  value: 'trigger',
44
- description: 'Use the auth token 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)',
45
50
  },
46
51
  ],
47
52
  default: 'credentials',
@@ -50,11 +55,10 @@ class Aiteza {
50
55
  displayName: 'AITEZA Base URL',
51
56
  name: 'baseUrl',
52
57
  type: 'string',
53
- required: true,
54
58
  default: '',
55
59
  placeholder: 'https://aiteza.example.com',
56
- displayOptions: { show: { authentication: ['trigger'] } },
57
- description: 'Base URL of the AITEZA backend (without trailing slash). Required when using trigger authentication since no credentials are configured.',
60
+ displayOptions: { show: { authSource: ['trigger'] } },
61
+ description: 'Base URL of the AITEZA backend (without trailing slash). Leave empty to use the URL forwarded by the upstream AITEZA Trigger node (_baseUrl in its output). Required if the trigger does not provide one.',
58
62
  },
59
63
  // ------------------------------------------------------------------
60
64
  // Resource
@@ -1107,13 +1111,6 @@ class Aiteza {
1107
1111
  type: 'boolean',
1108
1112
  default: false,
1109
1113
  },
1110
- {
1111
- displayName: 'Agentic Mode',
1112
- name: 'agenticMode',
1113
- type: 'boolean',
1114
- default: true,
1115
- description: 'Whether the LLM autonomously decides which datarooms and files to search',
1116
- },
1117
1114
  {
1118
1115
  displayName: 'Workflow ID',
1119
1116
  name: 'workflowId',
@@ -1367,7 +1364,7 @@ class Aiteza {
1367
1364
  const returnData = [];
1368
1365
  const resource = this.getNodeParameter('resource', 0);
1369
1366
  const operation = this.getNodeParameter('operation', 0);
1370
- const authMode = this.getNodeParameter('authentication', 0, 'credentials');
1367
+ const authMode = this.getNodeParameter('authSource', 0, 'credentials');
1371
1368
  let authCtx;
1372
1369
  if (authMode === 'trigger') {
1373
1370
  const triggerToken = (0, GenericFunctions_1.getUpstreamAuthToken)(this);
@@ -1375,9 +1372,10 @@ class Aiteza {
1375
1372
  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. ' +
1376
1373
  'Make sure this node is connected to an AITEZA Trigger node.');
1377
1374
  }
1378
- const baseUrl = this.getNodeParameter('baseUrl', 0).replace(/\/+$/, '');
1375
+ const paramBaseUrl = this.getNodeParameter('baseUrl', 0, '').replace(/\/+$/, '');
1376
+ const baseUrl = paramBaseUrl || (0, GenericFunctions_1.getUpstreamBaseUrl)(this) || '';
1379
1377
  if (!baseUrl) {
1380
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'AITEZA Base URL is required when using trigger authentication.');
1378
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'AITEZA Base URL is required when using trigger authentication. Either set it on this node, or configure it on the upstream AITEZA Trigger node so it gets forwarded as _baseUrl.');
1381
1379
  }
1382
1380
  authCtx = { baseUrl, triggerToken };
1383
1381
  }
@@ -1391,6 +1389,12 @@ class Aiteza {
1391
1389
  const uploadRequest = async (reqOpts) => {
1392
1390
  if (authCtx?.triggerToken) {
1393
1391
  reqOpts.headers = { ...(reqOpts.headers ?? {}), Authorization: `Bearer ${authCtx.triggerToken}` };
1392
+ }
1393
+ // If body is FormData, merge its headers (boundary etc.)
1394
+ if (reqOpts.body instanceof form_data_1.default) {
1395
+ reqOpts.headers = { ...(reqOpts.headers ?? {}), ...reqOpts.body.getHeaders() };
1396
+ }
1397
+ if (authCtx?.triggerToken) {
1394
1398
  return this.helpers.httpRequest(reqOpts);
1395
1399
  }
1396
1400
  return this.helpers.httpRequestWithAuthentication.call(this, 'aitezaOAuth2Api', reqOpts);
@@ -1408,18 +1412,15 @@ class Aiteza {
1408
1412
  const binaryPropertyName = this.getNodeParameter('binaryPropertyName', idx);
1409
1413
  const binaryData = this.helpers.assertBinaryData(idx, binaryPropertyName);
1410
1414
  const buffer = await this.helpers.getBinaryDataBuffer(idx, binaryPropertyName);
1415
+ const form = new form_data_1.default();
1416
+ form.append(fieldName, buffer, {
1417
+ filename: binaryData.fileName ?? defaultFileName,
1418
+ contentType: binaryData.mimeType,
1419
+ });
1411
1420
  return uploadRequest({
1412
1421
  method: 'POST',
1413
1422
  url,
1414
- formData: {
1415
- [fieldName]: {
1416
- value: buffer,
1417
- options: {
1418
- filename: binaryData.fileName ?? defaultFileName,
1419
- contentType: binaryData.mimeType,
1420
- },
1421
- },
1422
- },
1423
+ body: form,
1423
1424
  });
1424
1425
  };
1425
1426
  const buildFileUploadUrl = async (idx, basePath) => {
@@ -1821,22 +1822,26 @@ class Aiteza {
1821
1822
  const prompt = this.getNodeParameter('prompt', i);
1822
1823
  const model = this.getNodeParameter('model', i);
1823
1824
  const additionalFields = this.getNodeParameter('additionalFields', i, {});
1824
- const formData = { prompt, model };
1825
+ const form = new form_data_1.default();
1826
+ form.append('prompt', prompt);
1827
+ form.append('model', model);
1825
1828
  for (const key of ['connectedDatarooms', 'fileIds', 'imageIds']) {
1826
1829
  if (additionalFields[key]) {
1827
- formData[key] = splitCsv(additionalFields[key]);
1830
+ for (const val of splitCsv(additionalFields[key])) {
1831
+ form.append(key, val);
1832
+ }
1828
1833
  }
1829
1834
  }
1830
- for (const key of ['parentDatarooms', 'subDatarooms', 'webSearchEnabled', 'agenticMode', 'workflowId']) {
1835
+ for (const key of ['parentDatarooms', 'subDatarooms', 'webSearchEnabled', 'workflowId']) {
1831
1836
  if (additionalFields[key] !== undefined && additionalFields[key] !== '')
1832
- formData[key] = additionalFields[key];
1837
+ form.append(key, String(additionalFields[key]));
1833
1838
  }
1834
1839
  const baseUrl = await getBaseUrl();
1835
1840
  const response = await uploadRequest({
1836
1841
  method: 'POST',
1837
1842
  url: `${baseUrl}/api/chat/${chatId}/generate`,
1838
- formData,
1839
- resolveWithFullResponse: true,
1843
+ body: form,
1844
+ returnFullResponse: true,
1840
1845
  });
1841
1846
  responseData = typeof response === 'object' && response.body !== undefined ? response.body : response;
1842
1847
  const xChatId = response?.headers?.['x-chat-id'];
@@ -1848,42 +1853,50 @@ class Aiteza {
1848
1853
  const prompt = this.getNodeParameter('prompt', i);
1849
1854
  const model = this.getNodeParameter('model', i);
1850
1855
  const additionalFields = this.getNodeParameter('additionalFields', i, {});
1851
- const formData = { prompt, model };
1856
+ const form = new form_data_1.default();
1857
+ form.append('prompt', prompt);
1858
+ form.append('model', model);
1852
1859
  for (const key of ['connectedDatarooms', 'fileIds', 'imageIds']) {
1853
1860
  if (additionalFields[key]) {
1854
- formData[key] = splitCsv(additionalFields[key]);
1861
+ for (const val of splitCsv(additionalFields[key])) {
1862
+ form.append(key, val);
1863
+ }
1855
1864
  }
1856
1865
  }
1857
- for (const key of ['parentDatarooms', 'subDatarooms', 'webSearchEnabled', 'agenticMode', 'workflowId']) {
1866
+ for (const key of ['parentDatarooms', 'subDatarooms', 'webSearchEnabled', 'workflowId']) {
1858
1867
  if (additionalFields[key] !== undefined && additionalFields[key] !== '')
1859
- formData[key] = additionalFields[key];
1868
+ form.append(key, String(additionalFields[key]));
1860
1869
  }
1861
1870
  const baseUrl = await getBaseUrl();
1862
1871
  responseData = await uploadRequest({
1863
1872
  method: 'POST',
1864
1873
  url: `${baseUrl}/api/chat/estimate`,
1865
- formData,
1874
+ body: form,
1866
1875
  });
1867
1876
  }
1868
1877
  else if (operation === 'tempChat') {
1869
1878
  const prompt = this.getNodeParameter('prompt', i);
1870
1879
  const model = this.getNodeParameter('model', i);
1871
1880
  const additionalFields = this.getNodeParameter('additionalFields', i, {});
1872
- const formData = { prompt, model };
1881
+ const form = new form_data_1.default();
1882
+ form.append('prompt', prompt);
1883
+ form.append('model', model);
1873
1884
  for (const key of ['connectedDatarooms', 'fileIds', 'imageIds']) {
1874
1885
  if (additionalFields[key]) {
1875
- formData[key] = splitCsv(additionalFields[key]);
1886
+ for (const val of splitCsv(additionalFields[key])) {
1887
+ form.append(key, val);
1888
+ }
1876
1889
  }
1877
1890
  }
1878
1891
  for (const key of ['webSearchEnabled', 'subDatarooms', 'parentDatarooms', 'workflowId']) {
1879
1892
  if (additionalFields[key] !== undefined && additionalFields[key] !== '')
1880
- formData[key] = additionalFields[key];
1893
+ form.append(key, String(additionalFields[key]));
1881
1894
  }
1882
1895
  const baseUrl = await getBaseUrl();
1883
1896
  responseData = await uploadRequest({
1884
1897
  method: 'POST',
1885
1898
  url: `${baseUrl}/api/chat/temp`,
1886
- formData,
1899
+ body: form,
1887
1900
  });
1888
1901
  if (typeof responseData === 'string') {
1889
1902
  responseData = { result: responseData };
@@ -9,19 +9,21 @@ 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
- 'Passes the caller\'s auth token to all downstream AITEZA nodes so they act on behalf of the triggering user.',
12
+ 'Passes the caller\'s auth token (and optionally the AITEZA Base URL) to all downstream AITEZA nodes so they act on behalf of the triggering user.',
13
13
  defaults: { name: 'AITEZA Trigger' },
14
14
  inputs: [],
15
15
  outputs: ['main'],
16
16
  // No credentials required – the trigger just receives webhooks.
17
- // Downstream AITEZA nodes still need credentials (for baseUrl), but will
18
- // use the token from this trigger for actual authentication.
17
+ // Downstream AITEZA nodes still need a base URL (taken from this trigger
18
+ // or from configured credentials) and will use the token from this
19
+ // trigger for actual authentication.
19
20
  credentials: [],
20
21
  webhooks: [
21
22
  {
22
23
  name: 'default',
23
24
  httpMethod: 'POST',
24
25
  responseMode: '={{$parameter["responseMode"]}}',
26
+ responseData: '={{$parameter["responseMode"] === "lastNode" ? "allEntries" : undefined}}',
25
27
  path: '={{$parameter["path"]}}',
26
28
  isFullPath: true,
27
29
  },
@@ -40,31 +42,53 @@ class AitezaTrigger {
40
42
  displayName: 'Response Mode',
41
43
  name: 'responseMode',
42
44
  type: 'options',
45
+ noDataExpression: true,
43
46
  options: [
47
+ {
48
+ name: 'Immediately',
49
+ value: 'onReceived',
50
+ description: 'Respond immediately with 200 OK as soon as the webhook is received',
51
+ },
44
52
  {
45
53
  name: 'When Last Node Finishes',
46
54
  value: 'lastNode',
47
55
  description: 'Respond with data from the last node in the workflow',
48
56
  },
49
57
  {
50
- name: 'Immediately',
51
- value: 'onReceived',
52
- description: 'Respond immediately with 200 OK',
58
+ name: 'Using \'Respond to Webhook\' Node',
59
+ value: 'responseNode',
60
+ description: 'Respond using the data from a downstream "Respond to Webhook" node',
53
61
  },
54
62
  ],
55
63
  default: 'lastNode',
56
64
  description: 'When and how to respond to the webhook',
57
65
  },
66
+ {
67
+ displayName: 'AITEZA Base URL',
68
+ name: 'baseUrl',
69
+ type: 'string',
70
+ default: '',
71
+ placeholder: 'https://aiteza.example.com',
72
+ description: 'Optional. If set, this URL is forwarded to downstream AITEZA nodes (as _baseUrl) so they don\'t need to repeat it. Without trailing slash.',
73
+ },
58
74
  ],
59
75
  };
60
76
  async webhook() {
61
77
  const body = this.getBodyData();
62
78
  const headers = this.getHeaderData();
79
+ const query = this.getQueryData();
63
80
  const authHeader = headers.authorization || headers.Authorization || '';
64
81
  const authToken = authHeader.replace(/^Bearer\s+/i, '');
65
- const outputData = { ...body };
82
+ const baseUrl = this.getNodeParameter('baseUrl', '').replace(/\/+$/, '');
83
+ const outputData = {
84
+ body,
85
+ headers,
86
+ query,
87
+ };
66
88
  if (authToken)
67
89
  outputData._authToken = authToken;
90
+ if (baseUrl)
91
+ outputData._baseUrl = baseUrl;
68
92
  return {
69
93
  workflowData: [this.helpers.returnJsonArray(outputData)],
70
94
  };
@@ -4,6 +4,7 @@ export interface AitezaAuthContext {
4
4
  triggerToken?: string;
5
5
  }
6
6
  export declare function getUpstreamAuthToken(ef: IExecuteFunctions): string | undefined;
7
+ export declare function getUpstreamBaseUrl(ef: IExecuteFunctions): string | undefined;
7
8
  export declare function aitezaApiRequest(this: IExecuteFunctions | ILoadOptionsFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject | IDataObject[], qs?: IDataObject, extraOpts?: Partial<IHttpRequestOptions>, authCtx?: AitezaAuthContext): Promise<any>;
8
9
  export declare function aitezaApiRequestFullResponse(this: IExecuteFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject, qs?: IDataObject, extraOpts?: Partial<IHttpRequestOptions>, authCtx?: AitezaAuthContext): Promise<{
9
10
  body: any;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getUpstreamAuthToken = getUpstreamAuthToken;
4
+ exports.getUpstreamBaseUrl = getUpstreamBaseUrl;
4
5
  exports.aitezaApiRequest = aitezaApiRequest;
5
6
  exports.aitezaApiRequestFullResponse = aitezaApiRequestFullResponse;
6
7
  exports.loadDatarooms = loadDatarooms;
@@ -28,6 +29,20 @@ function getUpstreamAuthToken(ef) {
28
29
  }
29
30
  return undefined;
30
31
  }
32
+ function getUpstreamBaseUrl(ef) {
33
+ try {
34
+ const items = ef.getInputData();
35
+ for (const item of items) {
36
+ const url = item.json?._baseUrl;
37
+ if (url)
38
+ return url.replace(/\/+$/, '');
39
+ }
40
+ }
41
+ catch {
42
+ // ignore
43
+ }
44
+ return undefined;
45
+ }
31
46
  // ---------------------------------------------------------------------------
32
47
  // Internal helpers
33
48
  // ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiteza/n8n-nodes-aiteza",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
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",
@@ -37,6 +37,9 @@
37
37
  "dist/nodes/Aiteza/AitezaTrigger.node.js"
38
38
  ]
39
39
  },
40
+ "dependencies": {
41
+ "form-data": "^4.0.0"
42
+ },
40
43
  "devDependencies": {
41
44
  "@typescript-eslint/parser": "~7.18.0",
42
45
  "eslint": "~8.57.0",