n8n-nodes-bgos 1.3.1 → 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.
- package/README.md +31 -0
- package/dist/nodes/BGOSAction/BGOSAction.node.js +28 -1
- package/dist/nodes/BGOSAction/handler/eventHandler.d.ts +1 -0
- package/dist/nodes/BGOSAction/handler/eventHandler.js +10 -0
- package/dist/nodes/BGOSAction/techWebhook.d.ts +2 -0
- package/dist/nodes/BGOSAction/techWebhook.js +4 -0
- package/dist/nodes/Bgos/Bgos.node.js +1 -1
- package/dist/nodes/BgosTrigger/BgosTrigger.node.js +1 -1
- package/package.json +1 -1
- /package/dist/credentials/{bgos.svg → bgos-icon.svg} +0 -0
- /package/dist/icons/{bgos.png → bgos-icon.png} +0 -0
- /package/dist/icons/{bgos.svg → bgos-icon.svg} +0 -0
- /package/dist/nodes/BGOSAction/{bgos.png → bgos-icon.png} +0 -0
- /package/dist/nodes/BGOSAction/{bgos.svg → bgos-icon.svg} +0 -0
- /package/dist/nodes/Bgos/{bgos.png → bgos-icon.png} +0 -0
- /package/dist/nodes/Bgos/{bgos.svg → bgos-icon.svg} +0 -0
- /package/dist/nodes/BgosTrigger/{bgos.png → bgos-icon.png} +0 -0
- /package/dist/nodes/BgosTrigger/{bgos.svg → bgos-icon.svg} +0 -0
package/README.md
CHANGED
|
@@ -16,6 +16,37 @@ Perform operations on the BGOS platform:
|
|
|
16
16
|
| **User** | Get or Create, Update |
|
|
17
17
|
| **Scheduled Task** | Get, Get User Tasks, Delete |
|
|
18
18
|
|
|
19
|
+
#### Render Mode — Inline Buttons vs. Modal Pop-Under
|
|
20
|
+
|
|
21
|
+
Two ways the BGOS app renders buttons that come with a message:
|
|
22
|
+
|
|
23
|
+
| Mode | Looks Like | When It Makes Sense |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| **Inline** (default on `sendMessage`) | Telegram-style chips inside a warm off-white card below the message. Skip (top-left), "✍ Custom reply" footer link. Stays clickable forever — user can answer now, tomorrow, or never. | The message is **not** a direct reply to something the user just typed: cron, scheduler, external webhook, unprompted check-in. Single question, up to 6 labeled choices. |
|
|
26
|
+
| **Modal** (default on `askUserInput`) | Bottom sheet / pop-under that demands attention. Multi-question carousel, free-text fallback, skip-all. | Active back-and-forth conversation — user just sent a message and you need their answer to continue. Also use for multi-question flows. |
|
|
27
|
+
|
|
28
|
+
**Default to inline unless you know the user is actively in the chat.** Modals interrupt. Inline never does.
|
|
29
|
+
|
|
30
|
+
##### Decision tree for AI agents
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Is this message triggered by a user message they sent in the last ~2 minutes?
|
|
34
|
+
├─ YES → modal OK (but inline also works)
|
|
35
|
+
└─ NO → inline. Always.
|
|
36
|
+
|
|
37
|
+
Do you need multi-question carousel, free-text, OR skip-all semantics?
|
|
38
|
+
├─ YES → must use modal (askUserInput op with askRenderMode=modal)
|
|
39
|
+
└─ NO → prefer inline
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
##### Agent-facing contract
|
|
43
|
+
|
|
44
|
+
- **Inline** chips ≤ 6 (backend rejects more). Labels ≤ 24 chars render cleanly; longer wraps.
|
|
45
|
+
- When the user clicks a chip, your `BGOS Trigger` receives `button_clicked` with `callback_data` = the option's `value`.
|
|
46
|
+
- When the user taps **Skip**, `button_clicked` fires with `callback_data: "__skip__"` and `option_id: null`.
|
|
47
|
+
- When the user taps **Custom reply** and submits free text, they get **two** events: a normal user `message` AND a `button_clicked` with `callback_data: "__custom__"` + `custom_text: <what they typed>`. Correlate by `message_id`.
|
|
48
|
+
- Respond to button clicks with the **Answer a Callback** operation so the chip animation completes.
|
|
49
|
+
|
|
19
50
|
#### Ask User Input
|
|
20
51
|
|
|
21
52
|
Pops the BGOS app's polished modal/sheet to ask the user 1–4 multiple-choice questions in a single carousel. Each question accepts: option list (label + value), optional free-text fallback, optional skip. The node POSTs each question to `/api/v1/messages` with `messageType=ask_user_input`, sharing one `askId` so they group into one carousel.
|
|
@@ -7,7 +7,7 @@ class BGOSAction {
|
|
|
7
7
|
description = {
|
|
8
8
|
displayName: 'BGOS Action',
|
|
9
9
|
name: 'bgosAction',
|
|
10
|
-
icon: 'file:bgos.svg',
|
|
10
|
+
icon: 'file:bgos-icon.svg',
|
|
11
11
|
group: ['transform'],
|
|
12
12
|
version: 1,
|
|
13
13
|
subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
|
|
@@ -228,6 +228,17 @@ class BGOSAction {
|
|
|
228
228
|
placeholder: '[{"label":"Yes","value":"yes"},{"label":"No","value":"no"}]',
|
|
229
229
|
description: 'JSON array of button options to display with the message',
|
|
230
230
|
},
|
|
231
|
+
{
|
|
232
|
+
displayName: 'Render Mode',
|
|
233
|
+
name: 'renderMode',
|
|
234
|
+
type: 'options',
|
|
235
|
+
default: 'inline',
|
|
236
|
+
options: [
|
|
237
|
+
{ name: 'Inline buttons (Telegram-style, asynchronous)', value: 'inline' },
|
|
238
|
+
{ name: 'Modal pop-under (interactive, blocks UI)', value: 'modal' },
|
|
239
|
+
],
|
|
240
|
+
description: 'Rule: If the message is NOT a direct reply to something the user just typed — cron, scheduler, external webhook, unprompted check-in — use INLINE. If the user just sent a message within the last ~2 min and you want their full attention, MODAL is acceptable. When in doubt, pick INLINE — it never interrupts. Max 6 options for inline.',
|
|
241
|
+
},
|
|
231
242
|
],
|
|
232
243
|
},
|
|
233
244
|
// ── Delete a Message ──────────────────────────────────────────────────
|
|
@@ -340,6 +351,18 @@ class BGOSAction {
|
|
|
340
351
|
description: 'Whether the node should block until every question is answered (or the timeout expires) and return structured answers. When disabled, the node returns immediately after posting; subscribe to the Ask Response event in a BGOS Trigger to receive each answer.',
|
|
341
352
|
displayOptions: { show: { resource: ['message'], operation: ['askUserInput'] } },
|
|
342
353
|
},
|
|
354
|
+
{
|
|
355
|
+
displayName: 'Render Mode',
|
|
356
|
+
name: 'askRenderMode',
|
|
357
|
+
type: 'options',
|
|
358
|
+
default: 'modal',
|
|
359
|
+
options: [
|
|
360
|
+
{ name: 'Modal pop-under (interactive, blocks UI)', value: 'modal' },
|
|
361
|
+
{ name: 'Inline buttons (Telegram-style, asynchronous)', value: 'inline' },
|
|
362
|
+
],
|
|
363
|
+
description: 'MODAL is the default because askUserInput is typically used mid-conversation. Switch to INLINE if this ask is triggered by cron/schedule/external webhook, or if you do NOT want to interrupt the user. Note: only MODAL supports multi-question carousel, free-text, and skip-all. INLINE renders 1 question with up to 6 chips plus a "Custom reply" / "Skip" footer.',
|
|
364
|
+
displayOptions: { show: { resource: ['message'], operation: ['askUserInput'] } },
|
|
365
|
+
},
|
|
343
366
|
{
|
|
344
367
|
displayName: 'Additional Fields',
|
|
345
368
|
name: 'askAdditionalFields',
|
|
@@ -569,6 +592,9 @@ class BGOSAction {
|
|
|
569
592
|
nodeParams.fileId = String(additionalFields.fileId ?? '');
|
|
570
593
|
nodeParams.sender = String(additionalFields.sender ?? 'assistant');
|
|
571
594
|
nodeParams.options = String(additionalFields.options ?? '');
|
|
595
|
+
nodeParams.renderMode = additionalFields.renderMode
|
|
596
|
+
? String(additionalFields.renderMode)
|
|
597
|
+
: 'inline';
|
|
572
598
|
}
|
|
573
599
|
if (operation === 'deleteMessage') {
|
|
574
600
|
nodeParams.deleteMessageId = String(this.getNodeParameter('deleteMessageId', i, '') ?? '');
|
|
@@ -577,6 +603,7 @@ class BGOSAction {
|
|
|
577
603
|
nodeParams.chatId = String(this.getNodeParameter('chatId', i, '') ?? '');
|
|
578
604
|
nodeParams.userId = String(this.getNodeParameter('askUserId', i, '') ?? '');
|
|
579
605
|
nodeParams.waitForAnswers = Boolean(this.getNodeParameter('waitForAnswers', i, true));
|
|
606
|
+
nodeParams.renderMode = String(this.getNodeParameter('askRenderMode', i, 'modal') ?? 'modal');
|
|
580
607
|
const askQuestionsRaw = this.getNodeParameter('askQuestions', i, {});
|
|
581
608
|
const questions = (askQuestionsRaw.question ?? []).map((q) => ({
|
|
582
609
|
text: String(q.text ?? '').trim(),
|
|
@@ -37,6 +37,7 @@ type NodeParams = {
|
|
|
37
37
|
waitForAnswers?: boolean;
|
|
38
38
|
askTimeoutSeconds?: number;
|
|
39
39
|
askPollIntervalMs?: number;
|
|
40
|
+
renderMode?: string;
|
|
40
41
|
[key: string]: unknown;
|
|
41
42
|
};
|
|
42
43
|
export declare function handleEventByType(this: IExecuteFunctions, eventType: string, eventData: Record<string, unknown>, nodeParams: NodeParams): Promise<unknown>;
|
|
@@ -50,6 +50,10 @@ async function handleEventByType(eventType, eventData, nodeParams) {
|
|
|
50
50
|
else if (Array.isArray(eventData.options)) {
|
|
51
51
|
buttonOptions = eventData.options;
|
|
52
52
|
}
|
|
53
|
+
const renderMode = (nodeParams.renderMode
|
|
54
|
+
?? eventData.renderMode
|
|
55
|
+
?? eventData.render_mode
|
|
56
|
+
?? 'inline');
|
|
53
57
|
return await techWebhook_1.sendMessageToBackend.call(this, apiOptions, {
|
|
54
58
|
assistantId: assistantId,
|
|
55
59
|
chatId: chatId,
|
|
@@ -57,6 +61,7 @@ async function handleEventByType(eventType, eventData, nodeParams) {
|
|
|
57
61
|
text,
|
|
58
62
|
options: buttonOptions,
|
|
59
63
|
files,
|
|
64
|
+
renderMode,
|
|
60
65
|
isCode: (eventData.isCode ?? message?.isCode ?? null),
|
|
61
66
|
artifactCode: (eventData.artifactCode ?? message?.artifactCode ?? null),
|
|
62
67
|
isArticle: (eventData.isArticle ?? message?.isArticle ?? null),
|
|
@@ -108,6 +113,10 @@ async function handleEventByType(eventType, eventData, nodeParams) {
|
|
|
108
113
|
if (!q.options.length)
|
|
109
114
|
throw new Error(`Question ${idx + 1}: at least one option is required.`);
|
|
110
115
|
}
|
|
116
|
+
const renderMode = (nodeParams.renderMode
|
|
117
|
+
?? eventData.renderMode
|
|
118
|
+
?? eventData.render_mode
|
|
119
|
+
?? 'modal');
|
|
111
120
|
// Post each question. The first call's response carries the
|
|
112
121
|
// backend-generated askId; reuse it for the rest so they group into
|
|
113
122
|
// one carousel/sheet on the frontend.
|
|
@@ -124,6 +133,7 @@ async function handleEventByType(eventType, eventData, nodeParams) {
|
|
|
124
133
|
allowFreeText: q.allowFreeText,
|
|
125
134
|
allowSkip: q.allowSkip,
|
|
126
135
|
options: q.options,
|
|
136
|
+
renderMode,
|
|
127
137
|
});
|
|
128
138
|
postedIds.push(posted.id);
|
|
129
139
|
if (!askId && posted.askId)
|
|
@@ -25,6 +25,7 @@ export declare function sendMessageToBackend(this: IExecuteFunctions, options: B
|
|
|
25
25
|
text?: string | null;
|
|
26
26
|
options?: unknown[];
|
|
27
27
|
files?: unknown[];
|
|
28
|
+
renderMode?: string | null;
|
|
28
29
|
isCode?: boolean | null;
|
|
29
30
|
artifactCode?: string | null;
|
|
30
31
|
isArticle?: boolean | null;
|
|
@@ -67,6 +68,7 @@ export declare function postAskQuestion(this: IExecuteFunctions, options: BgosAp
|
|
|
67
68
|
label: string;
|
|
68
69
|
value: string;
|
|
69
70
|
}>;
|
|
71
|
+
renderMode?: string | null;
|
|
70
72
|
}): Promise<AskQuestionPosted>;
|
|
71
73
|
export interface ChatMessageEntry {
|
|
72
74
|
message: {
|
|
@@ -117,6 +117,8 @@ async function sendMessageToBackend(options, payload) {
|
|
|
117
117
|
options: payload.options ?? [],
|
|
118
118
|
files: payload.files ?? [],
|
|
119
119
|
};
|
|
120
|
+
if (payload.renderMode)
|
|
121
|
+
body.renderMode = payload.renderMode;
|
|
120
122
|
return this.helpers.httpRequest({
|
|
121
123
|
method: 'POST',
|
|
122
124
|
url,
|
|
@@ -148,6 +150,8 @@ async function postAskQuestion(options, payload) {
|
|
|
148
150
|
};
|
|
149
151
|
if (payload.askId)
|
|
150
152
|
body.askId = payload.askId;
|
|
153
|
+
if (payload.renderMode)
|
|
154
|
+
body.renderMode = payload.renderMode;
|
|
151
155
|
const result = (await this.helpers.httpRequest({
|
|
152
156
|
method: 'POST',
|
|
153
157
|
url,
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|