@learningsuite/n8n-nodes-learningsuite 1.2.6 → 1.3.4

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.
Files changed (28) hide show
  1. package/README.md +41 -12
  2. package/dist/nodes/LearningSuite/LearningSuite.node.d.ts +7 -1
  3. package/dist/nodes/LearningSuite/LearningSuite.node.js +2 -0
  4. package/dist/nodes/LearningSuite/LearningSuiteTrigger.node.d.ts +7 -1
  5. package/dist/nodes/LearningSuite/LearningSuiteTrigger.node.js +14 -0
  6. package/dist/nodes/LearningSuite/descriptions/ai.properties.js +86 -0
  7. package/dist/nodes/LearningSuite/descriptions/customFields.properties.js +138 -17
  8. package/dist/nodes/LearningSuite/descriptions/trigger.instant.properties.js +32 -0
  9. package/dist/nodes/LearningSuite/descriptions/webhook.properties.js +35 -0
  10. package/dist/nodes/LearningSuite/descriptions/webhook.sampleData.properties.js +26 -0
  11. package/dist/nodes/LearningSuite/execute/ai.handlers.d.ts +3 -0
  12. package/dist/nodes/LearningSuite/execute/ai.handlers.js +44 -0
  13. package/dist/nodes/LearningSuite/execute/customFields.handlers.d.ts +1 -0
  14. package/dist/nodes/LearningSuite/execute/customFields.handlers.js +273 -22
  15. package/dist/nodes/LearningSuite/execute/webhook.handlers.js +16 -0
  16. package/dist/nodes/LearningSuite/methods/loadOptions/ai.loadOptions.d.ts +3 -0
  17. package/dist/nodes/LearningSuite/methods/loadOptions/ai.loadOptions.js +11 -0
  18. package/dist/nodes/LearningSuite/methods/loadOptions/common.d.ts +1 -0
  19. package/dist/nodes/LearningSuite/methods/loadOptions/common.js +5 -0
  20. package/dist/nodes/LearningSuite/methods/loadOptions/customFields.loadOptions.d.ts +8 -4
  21. package/dist/nodes/LearningSuite/methods/loadOptions/customFields.loadOptions.js +76 -3
  22. package/dist/nodes/LearningSuite/shared/customFields.helpers.js +1 -4
  23. package/dist/nodes/LearningSuite/shared/customFields.shared.d.ts +1 -0
  24. package/dist/nodes/LearningSuite/shared/customFields.upload.d.ts +1 -1
  25. package/dist/nodes/LearningSuite/shared/customFields.upload.js +11 -10
  26. package/dist/nodes/LearningSuite/shared/request.d.ts +3 -0
  27. package/dist/nodes/LearningSuite/shared/request.js +18 -0
  28. package/package.json +1 -1
@@ -3,6 +3,32 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.webhookSampleDataProperties = void 0;
5
5
  exports.webhookSampleDataProperties = [
6
+ {
7
+ displayName: 'Agent Action Executed Options',
8
+ name: 'additionalAgentActionExecutedSample',
9
+ type: 'collection',
10
+ default: {},
11
+ placeholder: 'Add option',
12
+ displayOptions: { show: { sampleDataType: ['agent-action-executed-events'] } },
13
+ options: [
14
+ {
15
+ displayName: 'Tool Key Name or ID',
16
+ name: 'toolKey',
17
+ type: 'options',
18
+ default: '',
19
+ description: 'Optional tool key to filter agent action executions. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
20
+ typeOptions: { loadOptionsMethod: 'ai_getAgentActions' },
21
+ },
22
+ {
23
+ displayName: 'Agent Name or ID',
24
+ name: 'agentId',
25
+ type: 'options',
26
+ default: '',
27
+ description: 'Optional agent ID to filter agent action executions. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
28
+ typeOptions: { loadOptionsMethod: 'ai_getAiAgents' },
29
+ },
30
+ ],
31
+ },
6
32
  {
7
33
  displayName: 'Progress Changed Options',
8
34
  name: 'additionalProgressChangedSample',
@@ -1,4 +1,7 @@
1
1
  import type { ExecuteHandler } from '../exec.types';
2
2
  export declare const aiHandlers: {
3
+ agentChat: ExecuteHandler;
4
+ getAgentActions: ExecuteHandler;
5
+ getAiAgents: ExecuteHandler;
3
6
  ragChat: ExecuteHandler;
4
7
  };
@@ -1,7 +1,48 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.aiHandlers = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
4
5
  const shared_1 = require("../shared");
6
+ const getAgentActions = async (ctx) => {
7
+ return await shared_1.lsRequest.call(ctx, 'GET', '/agent-actions');
8
+ };
9
+ const getAiAgents = async (ctx) => {
10
+ return await shared_1.lsRequest.call(ctx, 'GET', '/ai-agents');
11
+ };
12
+ function parseJsonObjectParam(ctx, i, name) {
13
+ const raw = ctx.getNodeParameter(name, i, '');
14
+ if (raw === undefined || raw === null || raw === '')
15
+ return undefined;
16
+ if (typeof raw === 'object')
17
+ return raw;
18
+ let parsed;
19
+ try {
20
+ parsed = JSON.parse(raw);
21
+ }
22
+ catch (err) {
23
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Invalid JSON in "${name}": ${String(err)}`);
24
+ }
25
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
26
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `"${name}" must be a JSON object`);
27
+ }
28
+ return parsed;
29
+ }
30
+ const agentChat = async (ctx, i) => {
31
+ const agentId = ctx.getNodeParameter('agentId', i);
32
+ if (!agentId)
33
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'AI Agent is required');
34
+ const body = {
35
+ userId: ctx.getNodeParameter('userId', i),
36
+ chatId: ctx.getNodeParameter('chatId', i, undefined),
37
+ message: ctx.getNodeParameter('message', i, undefined),
38
+ includeChatHistory: ctx.getNodeParameter('includeChatHistory', i, false),
39
+ endChat: ctx.getNodeParameter('endChat', i, false),
40
+ metadata: parseJsonObjectParam(ctx, i, 'metadata'),
41
+ profileContext: parseJsonObjectParam(ctx, i, 'profileContext'),
42
+ };
43
+ const cleanBody = Object.fromEntries(Object.entries(body).filter(([, v]) => v !== undefined && v !== ''));
44
+ return await shared_1.lsRequest.call(ctx, 'POST', `/ai-agents/${encodeURIComponent(agentId)}/chat`, { body: cleanBody });
45
+ };
5
46
  const ragChat = async (ctx, i) => {
6
47
  const body = {
7
48
  question: ctx.getNodeParameter('question', i),
@@ -19,5 +60,8 @@ const ragChat = async (ctx, i) => {
19
60
  return await shared_1.lsRequest.call(ctx, 'POST', '/ai/rag-chat', { body: cleanBody });
20
61
  };
21
62
  exports.aiHandlers = {
63
+ agentChat,
64
+ getAgentActions,
65
+ getAiAgents,
22
66
  ragChat,
23
67
  };
@@ -9,6 +9,7 @@ export declare const customFieldsHandlers: {
9
9
  getFieldValues: ExecuteHandler;
10
10
  setFieldValue: ExecuteHandler;
11
11
  setMultipleFieldValues: ExecuteHandler;
12
+ createFileUploadTarget: ExecuteHandler;
12
13
  getProfiles: ExecuteHandler;
13
14
  getProfilesExpanded: ExecuteHandler;
14
15
  getProfileByCard: ExecuteHandler;
@@ -95,6 +95,178 @@ function normalizeCustomFieldValue(response) {
95
95
  return { value: response[0] };
96
96
  return { value: response };
97
97
  }
98
+ function normalizeValuesArray(value) {
99
+ if (value === undefined || value === null) {
100
+ return [];
101
+ }
102
+ return Array.isArray(value) ? value : [value];
103
+ }
104
+ function normalizeStoreFileValuesResponse(response) {
105
+ if (!Array.isArray(response)) {
106
+ return normalizeValuesArray(response);
107
+ }
108
+ if (response.length === 0) {
109
+ return [];
110
+ }
111
+ if (response.length === 1 && Array.isArray(response[0])) {
112
+ return response[0];
113
+ }
114
+ return response;
115
+ }
116
+ function normalizeFileValuesResponse(response) {
117
+ return normalizeStoreFileValuesResponse(response);
118
+ }
119
+ function normalizeProfileIndex(value) {
120
+ if (typeof value === 'number' && Number.isFinite(value)) {
121
+ return value;
122
+ }
123
+ if (typeof value === 'string' && value.trim() !== '') {
124
+ const parsed = Number(value);
125
+ return Number.isFinite(parsed) ? parsed : undefined;
126
+ }
127
+ return undefined;
128
+ }
129
+ function getMediaFieldLimit(type, typeDefinition) {
130
+ switch (type) {
131
+ case 'files':
132
+ return typeDefinition.maxFiles;
133
+ case 'images':
134
+ return typeDefinition.maxImages;
135
+ case 'videos':
136
+ return typeDefinition.maxVideos;
137
+ case 'audios':
138
+ return typeDefinition.maxAudios;
139
+ default:
140
+ return undefined;
141
+ }
142
+ }
143
+ function buildProfileSelector(ctx, i) {
144
+ const profileId = ctx.getNodeParameter('profileId', i, '');
145
+ const profileIndex = normalizeProfileIndex(ctx.getNodeParameter('profileIndex', i, ''));
146
+ const profileName = ctx.getNodeParameter('profileName', i, '');
147
+ const selector = {};
148
+ if (profileId) {
149
+ selector.profileId = profileId;
150
+ }
151
+ else if (profileIndex !== undefined) {
152
+ selector.profileIndex = profileIndex;
153
+ }
154
+ else if (profileName) {
155
+ selector.profileName = profileName;
156
+ }
157
+ return selector;
158
+ }
159
+ function hasProfileSelector(selector) {
160
+ return selector.profileId !== undefined || selector.profileIndex !== undefined || selector.profileName !== undefined;
161
+ }
162
+ function getEffectiveProfileSelector(fieldContext, selector) {
163
+ return fieldContext.multipleProfilesAllowed ? selector : {};
164
+ }
165
+ async function cardAllowsMultipleProfiles(ctx, cardId) {
166
+ const cards = await shared_1.lsRequest.call(ctx, 'GET', '/custom-fields/cards');
167
+ if (!Array.isArray(cards)) {
168
+ return false;
169
+ }
170
+ const card = cards.find((entry) => {
171
+ if (typeof entry !== 'object' || entry === null)
172
+ return false;
173
+ return entry.id === cardId;
174
+ });
175
+ if (!card || typeof card !== 'object') {
176
+ return false;
177
+ }
178
+ return card.multipleProfilesAllowed === true;
179
+ }
180
+ function getFileValueMode(ctx, i) {
181
+ return ctx.getNodeParameter('fileValueMode', i, 'add');
182
+ }
183
+ function countBinaryPropertyNames(binaryPropertyNames) {
184
+ return binaryPropertyNames
185
+ .split(',')
186
+ .map((name) => name.trim())
187
+ .filter((name) => name.length > 0).length;
188
+ }
189
+ function findFileFieldContext(ctx, cards, customFieldKey) {
190
+ var _a, _b, _c;
191
+ for (const card of cards) {
192
+ if (!(card === null || card === void 0 ? void 0 : card.id) || !Array.isArray(card.definitions)) {
193
+ continue;
194
+ }
195
+ const definition = card.definitions.find((def) => (def === null || def === void 0 ? void 0 : def.key) === customFieldKey);
196
+ if (!definition) {
197
+ continue;
198
+ }
199
+ const type = String((_b = (_a = definition.typeDefinition) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : '');
200
+ if (!FILE_FIELD_TYPES.has(type)) {
201
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Custom field "${customFieldKey}" is not a file, image, video, or audio field.`);
202
+ }
203
+ return {
204
+ cardId: card.id,
205
+ type,
206
+ maxItems: getMediaFieldLimit(type, (_c = definition.typeDefinition) !== null && _c !== void 0 ? _c : {}),
207
+ multipleProfilesAllowed: card.multipleProfilesAllowed === true,
208
+ };
209
+ }
210
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Unknown custom field "${customFieldKey}".`);
211
+ }
212
+ async function getPublicUrlFileFieldContext(ctx, customFieldKey) {
213
+ const cardsResponse = await shared_1.lsRequest.call(ctx, 'GET', '/custom-fields/cards/expanded');
214
+ if (!Array.isArray(cardsResponse)) {
215
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Failed to load custom field definitions.');
216
+ }
217
+ return findFileFieldContext(ctx, cardsResponse, customFieldKey);
218
+ }
219
+ async function readExistingFileValues(ctx, userId, customFieldKey, fieldContext, profileSelector, forceProfileContext = false) {
220
+ const effectiveProfileSelector = getEffectiveProfileSelector(fieldContext, profileSelector);
221
+ const useProfileContext = forceProfileContext || fieldContext.multipleProfilesAllowed || hasProfileSelector(effectiveProfileSelector);
222
+ if (useProfileContext) {
223
+ const existingProfileValues = await shared_1.lsRequest.call(ctx, 'GET', `/custom-fields/store/${userId}/profiles/by-card/${fieldContext.cardId}`, { qs: effectiveProfileSelector });
224
+ return {
225
+ existingValues: normalizeFileValuesResponse(existingProfileValues === null || existingProfileValues === void 0 ? void 0 : existingProfileValues[customFieldKey]),
226
+ useProfileContext,
227
+ };
228
+ }
229
+ const existingFieldValues = await shared_1.lsRequest.call(ctx, 'GET', `/custom-fields/store/${userId}/fields/${customFieldKey}`);
230
+ return {
231
+ existingValues: normalizeStoreFileValuesResponse(existingFieldValues),
232
+ useProfileContext,
233
+ };
234
+ }
235
+ function assertFileValueCountWithinLimit(ctx, customFieldKey, valueCount, maxItems) {
236
+ if (maxItems !== undefined && valueCount > maxItems) {
237
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Cannot set custom field "${customFieldKey}". Maximum of ${maxItems} file values would be exceeded.`);
238
+ }
239
+ }
240
+ function buildNextFileValues(ctx, customFieldKey, existingValues, uploadedValues, maxItems, mode) {
241
+ assertFileValueCountWithinLimit(ctx, customFieldKey, uploadedValues.length, maxItems);
242
+ if (mode === 'replace') {
243
+ return uploadedValues;
244
+ }
245
+ const appendedValues = [...existingValues, ...uploadedValues];
246
+ if (maxItems === undefined || appendedValues.length <= maxItems) {
247
+ return appendedValues;
248
+ }
249
+ if (mode === 'replaceIfFull') {
250
+ return uploadedValues;
251
+ }
252
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Cannot add file to custom field "${customFieldKey}". Maximum of ${maxItems} would be exceeded.`);
253
+ }
254
+ async function writeFileValues(ctx, userId, customFieldKey, fieldContext, profileSelector, useProfileContext, nextValues) {
255
+ const effectiveProfileSelector = getEffectiveProfileSelector(fieldContext, profileSelector);
256
+ if (useProfileContext) {
257
+ const updateBody = {
258
+ fieldKey: customFieldKey,
259
+ fieldValue: nextValues,
260
+ ...effectiveProfileSelector,
261
+ };
262
+ return await shared_1.lsRequest.call(ctx, 'PUT', `/custom-fields/store/${userId}/profiles/by-card/${fieldContext.cardId}`, {
263
+ body: updateBody,
264
+ });
265
+ }
266
+ return await shared_1.lsRequest.call(ctx, 'PUT', `/custom-fields/store/${userId}/fields/${customFieldKey}`, {
267
+ body: { fieldValue: nextValues },
268
+ });
269
+ }
98
270
  function readTypedFieldValue(ctx, i, fieldType) {
99
271
  switch (fieldType) {
100
272
  case 'string':
@@ -154,9 +326,9 @@ const getStore = async (ctx, i) => {
154
326
  };
155
327
  const getStoreValues = async (ctx, i) => {
156
328
  const userId = ctx.getNodeParameter('userId', i);
157
- const profileIndex = ctx.getNodeParameter('profileIndex', i, null);
329
+ const profileIndex = normalizeProfileIndex(ctx.getNodeParameter('profileIndex', i, ''));
158
330
  const qs = {};
159
- if (profileIndex !== null && Number.isFinite(profileIndex)) {
331
+ if (profileIndex !== undefined) {
160
332
  qs.profileIndex = profileIndex;
161
333
  }
162
334
  return await shared_1.lsRequest.call(ctx, 'GET', `/custom-fields/store/${userId}/values`, { qs });
@@ -172,10 +344,24 @@ const setFieldValue = async (ctx, i) => {
172
344
  const fieldKey = ctx.getNodeParameter('fieldKey', i);
173
345
  const fieldType = ctx.getNodeParameter('fieldType', i, '');
174
346
  let fieldValue;
347
+ let fileFieldContext;
348
+ let fileProfileSelector;
349
+ let useFileProfileContext = false;
175
350
  if (FILE_FIELD_TYPES.has(fieldType)) {
176
351
  const binaryPropertyNames = readTypedFieldValue(ctx, i, fieldType);
177
352
  const fileNameOverride = ctx.getNodeParameter('fieldValueFileName', i, '').trim() || undefined;
178
- fieldValue = await (0, shared_1.uploadFilesFromBinaryProperties)(ctx, i, userId, binaryPropertyNames, fieldType, fileNameOverride);
353
+ const fileValueMode = getFileValueMode(ctx, i);
354
+ fileFieldContext = await getPublicUrlFileFieldContext(ctx, fieldKey);
355
+ fileProfileSelector = buildProfileSelector(ctx, i);
356
+ const { existingValues, useProfileContext } = await readExistingFileValues(ctx, userId, fieldKey, fileFieldContext, fileProfileSelector);
357
+ useFileProfileContext = useProfileContext;
358
+ const uploadCount = countBinaryPropertyNames(binaryPropertyNames);
359
+ if (fileValueMode !== 'replaceIfFull') {
360
+ const expectedValues = fileValueMode === 'replace' ? uploadCount : existingValues.length + uploadCount;
361
+ assertFileValueCountWithinLimit(ctx, fieldKey, expectedValues, fileFieldContext.maxItems);
362
+ }
363
+ const uploadedValues = await (0, shared_1.uploadFilesFromBinaryProperties)(ctx, i, userId, fieldKey, binaryPropertyNames, fieldType, fileNameOverride);
364
+ fieldValue = buildNextFileValues(ctx, fieldKey, existingValues, uploadedValues, fileFieldContext.maxItems, fileValueMode);
179
365
  }
180
366
  else {
181
367
  fieldValue = readTypedFieldValue(ctx, i, fieldType);
@@ -184,8 +370,7 @@ const setFieldValue = async (ctx, i) => {
184
370
  throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `No value provided for custom field "${fieldKey}".`);
185
371
  }
186
372
  const profileId = ctx.getNodeParameter('profileId', i, '');
187
- const profileIndex = ctx.getNodeParameter('profileIndex', i, null);
188
- const validProfileIndex = profileIndex !== null && Number.isFinite(profileIndex) ? profileIndex : undefined;
373
+ const validProfileIndex = normalizeProfileIndex(ctx.getNodeParameter('profileIndex', i, ''));
189
374
  const body = { fieldValue };
190
375
  if (profileId) {
191
376
  body.profileId = profileId;
@@ -193,6 +378,10 @@ const setFieldValue = async (ctx, i) => {
193
378
  else if (validProfileIndex !== undefined) {
194
379
  body.profileIndex = validProfileIndex;
195
380
  }
381
+ if (fileFieldContext && fileProfileSelector) {
382
+ const response = await writeFileValues(ctx, userId, fieldKey, fileFieldContext, fileProfileSelector, useFileProfileContext, fieldValue);
383
+ return normalizeCustomFieldValue(response);
384
+ }
196
385
  const response = await shared_1.lsRequest.call(ctx, 'PUT', `/custom-fields/store/${userId}/fields/${fieldKey}`, { body });
197
386
  return normalizeCustomFieldValue(response);
198
387
  };
@@ -213,8 +402,8 @@ function buildFieldTypeMap(cards) {
213
402
  const setMultipleFieldValues = async (ctx, i) => {
214
403
  const userId = ctx.getNodeParameter('userId', i);
215
404
  const profileId = ctx.getNodeParameter('profileId', i, '');
216
- const profileIndex = ctx.getNodeParameter('profileIndex', i, null);
217
- const validProfileIndex = profileIndex !== null && Number.isFinite(profileIndex) ? profileIndex : undefined;
405
+ const validProfileIndex = normalizeProfileIndex(ctx.getNodeParameter('profileIndex', i, ''));
406
+ const fileValueMode = getFileValueMode(ctx, i);
218
407
  const mapperData = ctx.getNodeParameter('fieldValues', i);
219
408
  const fieldMappings = (mapperData === null || mapperData === void 0 ? void 0 : mapperData.value) || {};
220
409
  const cardsResponse = await shared_1.lsRequest.call(ctx, 'GET', '/custom-fields/cards/expanded');
@@ -222,6 +411,7 @@ const setMultipleFieldValues = async (ctx, i) => {
222
411
  throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Failed to load custom field definitions.');
223
412
  }
224
413
  const cards = cardsResponse;
414
+ const expandedCards = cardsResponse;
225
415
  const fieldTypeMap = buildFieldTypeMap(cards);
226
416
  const entries = Object.entries(fieldMappings).filter(([, value]) => value !== undefined);
227
417
  const payload = [];
@@ -233,7 +423,22 @@ const setMultipleFieldValues = async (ctx, i) => {
233
423
  let fieldValue;
234
424
  if (FILE_FIELD_TYPES.has(lsType)) {
235
425
  const binaryPropertyNames = typeof value === 'string' ? value : 'data';
236
- fieldValue = await (0, shared_1.uploadFilesFromBinaryProperties)(ctx, i, userId, binaryPropertyNames, lsType);
426
+ const fieldContext = findFileFieldContext(ctx, expandedCards, fieldKey);
427
+ const profileSelector = {};
428
+ if (profileId) {
429
+ profileSelector.profileId = profileId;
430
+ }
431
+ else if (validProfileIndex !== undefined) {
432
+ profileSelector.profileIndex = validProfileIndex;
433
+ }
434
+ const { existingValues } = await readExistingFileValues(ctx, userId, fieldKey, fieldContext, profileSelector);
435
+ const uploadCount = countBinaryPropertyNames(binaryPropertyNames);
436
+ if (fileValueMode !== 'replaceIfFull') {
437
+ const expectedValues = fileValueMode === 'replace' ? uploadCount : existingValues.length + uploadCount;
438
+ assertFileValueCountWithinLimit(ctx, fieldKey, expectedValues, fieldContext.maxItems);
439
+ }
440
+ const uploadedValues = await (0, shared_1.uploadFilesFromBinaryProperties)(ctx, i, userId, fieldKey, binaryPropertyNames, lsType);
441
+ fieldValue = buildNextFileValues(ctx, fieldKey, existingValues, uploadedValues, fieldContext.maxItems, fileValueMode);
237
442
  }
238
443
  else {
239
444
  fieldValue = normalizeSingleFieldValueOrFail(ctx, value, fieldKey, lsType);
@@ -254,6 +459,37 @@ const setMultipleFieldValues = async (ctx, i) => {
254
459
  body: payload,
255
460
  });
256
461
  };
462
+ const createFileUploadTarget = async (ctx, i) => {
463
+ const userId = ctx.getNodeParameter('userId', i);
464
+ const customFieldKey = (ctx.getNodeParameter('customFieldKey', i, '') || ctx.getNodeParameter('fieldKey', i, '')).trim();
465
+ const fileName = ctx.getNodeParameter('customFieldFileName', i, '').trim();
466
+ const publicDownloadUrl = ctx.getNodeParameter('publicDownloadUrl', i, '').trim();
467
+ const profileSelector = buildProfileSelector(ctx, i);
468
+ const fileValueMode = getFileValueMode(ctx, i);
469
+ if (!customFieldKey) {
470
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'No custom field key provided.');
471
+ }
472
+ if (!publicDownloadUrl) {
473
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'No public download URL provided.');
474
+ }
475
+ const fieldContext = await getPublicUrlFileFieldContext(ctx, customFieldKey);
476
+ const { existingValues, useProfileContext } = await readExistingFileValues(ctx, userId, customFieldKey, fieldContext, profileSelector);
477
+ const body = { customFieldKey };
478
+ if (fileName) {
479
+ body.fileName = fileName;
480
+ }
481
+ body.publicDownloadUrl = publicDownloadUrl;
482
+ const createFileResponse = (await shared_1.lsRequest.call(ctx, 'POST', `/custom-fields/store/${userId}/files`, {
483
+ body,
484
+ }));
485
+ const customFieldValue = createFileResponse.customFieldValue;
486
+ if (customFieldValue === undefined || customFieldValue === null) {
487
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'LearningSuite did not return a custom field value for the uploaded file.');
488
+ }
489
+ const uploadedValues = normalizeValuesArray(customFieldValue);
490
+ const nextValues = buildNextFileValues(ctx, customFieldKey, existingValues, uploadedValues, fieldContext.maxItems, fileValueMode);
491
+ return await writeFileValues(ctx, userId, customFieldKey, fieldContext, profileSelector, useProfileContext, nextValues);
492
+ };
257
493
  const getProfiles = async (ctx, i) => {
258
494
  const userId = ctx.getNodeParameter('userId', i);
259
495
  const customFieldCardId = ctx.getNodeParameter('customFieldCardId', i, undefined);
@@ -274,13 +510,13 @@ const getProfileByCard = async (ctx, i) => {
274
510
  const userId = ctx.getNodeParameter('userId', i);
275
511
  const cardId = ctx.getNodeParameter('customFieldCardId', i);
276
512
  const profileId = ctx.getNodeParameter('profileId', i, '');
277
- const profileIndex = ctx.getNodeParameter('profileIndex', i, null);
513
+ const profileIndex = normalizeProfileIndex(ctx.getNodeParameter('profileIndex', i, ''));
278
514
  const profileName = ctx.getNodeParameter('profileName', i, '');
279
515
  const qs = {};
280
516
  if (profileId) {
281
517
  qs.profileId = profileId;
282
518
  }
283
- else if (profileIndex !== null && Number.isFinite(profileIndex)) {
519
+ else if (profileIndex !== undefined) {
284
520
  qs.profileIndex = profileIndex;
285
521
  }
286
522
  else if (profileName) {
@@ -294,10 +530,22 @@ const updateProfileField = async (ctx, i) => {
294
530
  const fieldKey = ctx.getNodeParameter('fieldKey', i);
295
531
  const fieldType = ctx.getNodeParameter('fieldType', i, '');
296
532
  let fieldValue;
533
+ let fileFieldContext;
534
+ let fileProfileSelector;
297
535
  if (FILE_FIELD_TYPES.has(fieldType)) {
298
536
  const binaryPropertyNames = readTypedFieldValue(ctx, i, fieldType);
299
537
  const fileNameOverride = ctx.getNodeParameter('fieldValueFileName', i, '').trim() || undefined;
300
- fieldValue = await (0, shared_1.uploadFilesFromBinaryProperties)(ctx, i, userId, binaryPropertyNames, fieldType, fileNameOverride);
538
+ const fileValueMode = getFileValueMode(ctx, i);
539
+ fileFieldContext = await getPublicUrlFileFieldContext(ctx, fieldKey);
540
+ fileProfileSelector = getEffectiveProfileSelector(fileFieldContext, buildProfileSelector(ctx, i));
541
+ const { existingValues } = await readExistingFileValues(ctx, userId, fieldKey, fileFieldContext, fileProfileSelector, true);
542
+ const uploadCount = countBinaryPropertyNames(binaryPropertyNames);
543
+ if (fileValueMode !== 'replaceIfFull') {
544
+ const expectedValues = fileValueMode === 'replace' ? uploadCount : existingValues.length + uploadCount;
545
+ assertFileValueCountWithinLimit(ctx, fieldKey, expectedValues, fileFieldContext.maxItems);
546
+ }
547
+ const uploadedValues = await (0, shared_1.uploadFilesFromBinaryProperties)(ctx, i, userId, fieldKey, binaryPropertyNames, fieldType, fileNameOverride);
548
+ fieldValue = buildNextFileValues(ctx, fieldKey, existingValues, uploadedValues, fileFieldContext.maxItems, fileValueMode);
301
549
  }
302
550
  else {
303
551
  fieldValue = readTypedFieldValue(ctx, i, fieldType);
@@ -306,19 +554,21 @@ const updateProfileField = async (ctx, i) => {
306
554
  throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `No value provided for custom field "${fieldKey}".`);
307
555
  }
308
556
  const body = { fieldKey, fieldValue };
309
- const profileId = ctx.getNodeParameter('profileId', i, '');
310
- const profileIndex = ctx.getNodeParameter('profileIndex', i, null);
311
- if (profileId) {
312
- body.profileId = profileId;
557
+ let profileSelector = buildProfileSelector(ctx, i);
558
+ if (fileFieldContext) {
559
+ profileSelector = getEffectiveProfileSelector(fileFieldContext, profileSelector);
313
560
  }
314
- else if (profileIndex !== null && Number.isFinite(profileIndex)) {
315
- body.profileIndex = profileIndex;
561
+ else if (!(await cardAllowsMultipleProfiles(ctx, cardId))) {
562
+ profileSelector = {};
316
563
  }
317
- if (body.profileId === undefined && body.profileIndex === undefined) {
318
- const profileName = ctx.getNodeParameter('profileName', i, '');
319
- if (profileName) {
320
- body.profileName = profileName;
321
- }
564
+ if (profileSelector.profileId !== undefined) {
565
+ body.profileId = profileSelector.profileId;
566
+ }
567
+ else if (profileSelector.profileIndex !== undefined) {
568
+ body.profileIndex = profileSelector.profileIndex;
569
+ }
570
+ else if (profileSelector.profileName !== undefined) {
571
+ body.profileName = profileSelector.profileName;
322
572
  }
323
573
  const response = await shared_1.lsRequest.call(ctx, 'PUT', `/custom-fields/store/${userId}/profiles/by-card/${cardId}`, {
324
574
  body,
@@ -335,6 +585,7 @@ exports.customFieldsHandlers = {
335
585
  getFieldValues,
336
586
  setFieldValue,
337
587
  setMultipleFieldValues,
588
+ createFileUploadTarget,
338
589
  getProfiles,
339
590
  getProfilesExpanded,
340
591
  getProfileByCard,
@@ -34,6 +34,17 @@ function buildDesiredFilter(ctx, i, eventType) {
34
34
  var _a;
35
35
  const filter = {};
36
36
  switch (eventType) {
37
+ // ---------------- Agent Action
38
+ case 'agentAction.executed': {
39
+ const col = getCol(ctx, i, 'additionalAgentActionExecuted');
40
+ if (col === null || col === void 0 ? void 0 : col.toolKey) {
41
+ filter.toolKey = String(col.toolKey);
42
+ }
43
+ if (col === null || col === void 0 ? void 0 : col.agentId) {
44
+ filter.agentId = String(col.agentId);
45
+ }
46
+ break;
47
+ }
37
48
  // ---------------- Community
38
49
  case 'communityPost.commented': {
39
50
  const col = getCol(ctx, i, 'additionalCommunityPostCommented');
@@ -223,6 +234,11 @@ const getSampleData = async (ctx, i) => {
223
234
  const sampleDataType = ctx.getNodeParameter('sampleDataType', i);
224
235
  let qs = {};
225
236
  switch (sampleDataType) {
237
+ case 'agent-action-executed-events': {
238
+ const col = ctx.getNodeParameter('additionalAgentActionExecutedSample', i, {});
239
+ qs = col;
240
+ break;
241
+ }
226
242
  case 'progress-changed-events': {
227
243
  const col = ctx.getNodeParameter('additionalProgressChangedSample', i, {});
228
244
  qs = col;
@@ -0,0 +1,3 @@
1
+ import type { ILoadOptionsFunctions, INodePropertyOptions } from 'n8n-workflow';
2
+ export declare function ai_getAgentActions(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
3
+ export declare function ai_getAiAgents(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ai_getAgentActions = ai_getAgentActions;
4
+ exports.ai_getAiAgents = ai_getAiAgents;
5
+ const common_1 = require("./common");
6
+ async function ai_getAgentActions() {
7
+ return common_1.fetchOptions.call(this, '/agent-actions', undefined, ['name'], ['toolKey']);
8
+ }
9
+ async function ai_getAiAgents() {
10
+ return common_1.fetchOptions.call(this, '/ai-agents', undefined, ['name'], ['id']);
11
+ }
@@ -4,4 +4,5 @@ type LoadOptionRow = IDataObject;
4
4
  export declare function toOptions(rows: LoadOptionRow[], labelKeys?: string[], valueKeys?: string[]): INodePropertyOptions[];
5
5
  export declare function ensureArray<T>(res: T | T[]): T[];
6
6
  export declare function fetchOptions(this: ILoadOptionsFunctions, endpoint: string, qs?: IDataObject, labelKeys?: string[], valueKeys?: string[]): Promise<INodePropertyOptions[]>;
7
+ export declare function fetchOptionsAll(this: ILoadOptionsFunctions, endpoint: string, qs?: IDataObject, labelKeys?: string[], valueKeys?: string[]): Promise<INodePropertyOptions[]>;
7
8
  export {};
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toOptions = toOptions;
4
4
  exports.ensureArray = ensureArray;
5
5
  exports.fetchOptions = fetchOptions;
6
+ exports.fetchOptionsAll = fetchOptionsAll;
6
7
  const shared_1 = require("../../shared");
7
8
  function toOptions(rows, labelKeys = ['name', 'title', 'email'], valueKeys = ['id', 'sid', 'slug']) {
8
9
  return rows.map((r) => {
@@ -20,3 +21,7 @@ async function fetchOptions(endpoint, qs, labelKeys, valueKeys) {
20
21
  const rows = ensureArray(res);
21
22
  return toOptions(rows, labelKeys, valueKeys);
22
23
  }
24
+ async function fetchOptionsAll(endpoint, qs, labelKeys, valueKeys) {
25
+ const rows = await shared_1.lsRequestAll.call(this, endpoint, { qs });
26
+ return toOptions(rows, labelKeys, valueKeys);
27
+ }
@@ -1,7 +1,11 @@
1
- import type { ILoadOptionsFunctions } from 'n8n-workflow';
2
- export declare function customFields_getCards(this: ILoadOptionsFunctions): Promise<import("n8n-workflow").INodePropertyOptions[]>;
3
- export declare function customFields_getDefinitions(this: ILoadOptionsFunctions): Promise<import("n8n-workflow").INodePropertyOptions[]>;
4
- export declare function customFields_getCategories(this: ILoadOptionsFunctions): Promise<import("n8n-workflow").INodePropertyOptions[]>;
1
+ import type { ILoadOptionsFunctions, INodePropertyOptions } from 'n8n-workflow';
2
+ export declare function customFields_getCards(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
3
+ export declare function customFields_getDefinitions(this: ILoadOptionsFunctions): Promise<{
4
+ name: string;
5
+ value: string;
6
+ }[]>;
7
+ export declare function customFields_getMediaDefinitions(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
8
+ export declare function customFields_getCategories(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
5
9
  export declare function customFields_getFieldType(this: ILoadOptionsFunctions): Promise<{
6
10
  name: string;
7
11
  value: string;