n8n-nodes-bgos 1.0.3 → 1.0.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 +1 -1
- package/dist/credentials/BgosApi.credentials.js +4 -3
- package/dist/nodes/BGOSAction/BGOSAction.node.js +28 -0
- package/dist/nodes/BGOSAction/handler/eventHandler.d.ts +1 -0
- package/dist/nodes/BGOSAction/handler/eventHandler.js +11 -0
- package/dist/nodes/BGOSAction/techWebhook.d.ts +6 -0
- package/dist/nodes/BGOSAction/techWebhook.js +18 -2
- package/dist/nodes/BgosTrigger/BgosTrigger.node.js +22 -9
- package/package.json +18 -14
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ Starts workflows when BGOS events occur. Supports 16 event types:
|
|
|
31
31
|
|
|
32
32
|
Configure with your BGOS backend URL and optional API key:
|
|
33
33
|
|
|
34
|
-
- **Base URL**: Your BGOS backend URL (default: `
|
|
34
|
+
- **Base URL**: Your BGOS backend URL (default: `https://api.brandgrowthos.ai`)
|
|
35
35
|
- **API Key**: Optional, for future authentication support
|
|
36
36
|
|
|
37
37
|
## Setup
|
|
@@ -11,7 +11,7 @@ class BgosApi {
|
|
|
11
11
|
displayName: 'Base URL',
|
|
12
12
|
name: 'baseUrl',
|
|
13
13
|
type: 'string',
|
|
14
|
-
default: '
|
|
14
|
+
default: 'https://api.brandgrowthos.ai',
|
|
15
15
|
required: true,
|
|
16
16
|
description: 'The base URL of your BGOS backend',
|
|
17
17
|
},
|
|
@@ -21,14 +21,15 @@ class BgosApi {
|
|
|
21
21
|
type: 'string',
|
|
22
22
|
typeOptions: { password: true },
|
|
23
23
|
default: '',
|
|
24
|
-
|
|
24
|
+
required: true,
|
|
25
|
+
description: 'Your BGOS API key. Find it by calling POST /api/v1/users — the apiKey field in the response.',
|
|
25
26
|
},
|
|
26
27
|
];
|
|
27
28
|
authenticate = {
|
|
28
29
|
type: 'generic',
|
|
29
30
|
properties: {
|
|
30
31
|
headers: {
|
|
31
|
-
|
|
32
|
+
'X-API-Key': '={{$credentials.apiKey}}',
|
|
32
33
|
},
|
|
33
34
|
},
|
|
34
35
|
};
|
|
@@ -22,6 +22,7 @@ class BGOSAction {
|
|
|
22
22
|
displayOptions: {
|
|
23
23
|
show: {
|
|
24
24
|
operation: [
|
|
25
|
+
'pushEventToFrontend',
|
|
25
26
|
'sendMessageBackend',
|
|
26
27
|
'reportCallbackResult',
|
|
27
28
|
'createAssistant',
|
|
@@ -42,6 +43,7 @@ class BGOSAction {
|
|
|
42
43
|
type: 'options',
|
|
43
44
|
noDataExpression: true,
|
|
44
45
|
options: [
|
|
46
|
+
{ name: 'Push Event to Frontend', value: 'pushEventToFrontend', description: 'Push a real-time event to the BGOS frontend via WebSocket (POST /webhook/{event_type}) — no auth required' },
|
|
45
47
|
{ name: 'Send Message to Agent', value: 'sendMessage', description: 'Send message event to Agent webhook (Ava)' },
|
|
46
48
|
{ name: 'Send Message to BGOS', value: 'sendMessageBackend', description: 'Send message to BGOS backend API (POST /api/v1/send-message)' },
|
|
47
49
|
{ name: 'Send Voice Message to BGOS', value: 'sendVoiceMessageBackend', description: 'Send voice message to BGOS backend API' },
|
|
@@ -60,6 +62,29 @@ class BGOSAction {
|
|
|
60
62
|
required: true,
|
|
61
63
|
description: 'Select operation to perform',
|
|
62
64
|
},
|
|
65
|
+
{
|
|
66
|
+
displayName: 'Event Type',
|
|
67
|
+
name: 'pushEventType',
|
|
68
|
+
type: 'options',
|
|
69
|
+
options: [
|
|
70
|
+
{ name: 'message', value: 'message' },
|
|
71
|
+
{ name: 'edited_message', value: 'edited_message' },
|
|
72
|
+
{ name: 'voice_message', value: 'voice_message' },
|
|
73
|
+
{ name: 'new_chat', value: 'new_chat' },
|
|
74
|
+
{ name: 'chat_renamed', value: 'chat_renamed' },
|
|
75
|
+
{ name: 'new_assistant', value: 'new_assistant' },
|
|
76
|
+
{ name: 'voice_started', value: 'voice_started' },
|
|
77
|
+
{ name: 'voice_ended', value: 'voice_ended' },
|
|
78
|
+
{ name: 'reply_message', value: 'reply_message' },
|
|
79
|
+
{ name: 'button_clicked', value: 'button_clicked' },
|
|
80
|
+
{ name: 'message_deleted', value: 'message_deleted' },
|
|
81
|
+
{ name: 'chat_deleted', value: 'chat_deleted' },
|
|
82
|
+
],
|
|
83
|
+
default: 'message',
|
|
84
|
+
required: true,
|
|
85
|
+
description: 'Which /webhook/{type} endpoint to call. Overrides event_type from input if set.',
|
|
86
|
+
displayOptions: { show: { operation: ['pushEventToFrontend'] } },
|
|
87
|
+
},
|
|
63
88
|
{
|
|
64
89
|
displayName: 'Assistant Webhook URL',
|
|
65
90
|
name: 'webhookUrl',
|
|
@@ -116,6 +141,9 @@ class BGOSAction {
|
|
|
116
141
|
apiKey: creds?.apiKey ?? '',
|
|
117
142
|
callbackSuccess: Boolean(this.getNodeParameter('callbackSuccess', i, true)),
|
|
118
143
|
callbackError: String(this.getNodeParameter('callbackError', i, '') ?? ''),
|
|
144
|
+
pushEventType: operation === 'pushEventToFrontend'
|
|
145
|
+
? String(this.getNodeParameter('pushEventType', i, 'message') ?? 'message')
|
|
146
|
+
: '',
|
|
119
147
|
};
|
|
120
148
|
const result = await eventHandler_1.handleEventByType.call(this, eventType, eventData, nodeParams);
|
|
121
149
|
returnData.push({
|
|
@@ -7,6 +7,7 @@ type NodeParams = {
|
|
|
7
7
|
apiKey?: string;
|
|
8
8
|
callbackSuccess?: boolean;
|
|
9
9
|
callbackError?: string;
|
|
10
|
+
pushEventType?: string;
|
|
10
11
|
[key: string]: unknown;
|
|
11
12
|
};
|
|
12
13
|
export declare function handleEventByType(this: IExecuteFunctions, eventType: string, eventData: Record<string, unknown>, nodeParams: NodeParams): Promise<unknown>;
|
|
@@ -17,6 +17,17 @@ async function handleEventByType(eventType, eventData, nodeParams) {
|
|
|
17
17
|
const chat = eventData.chat;
|
|
18
18
|
const message = eventData.message;
|
|
19
19
|
switch (operation) {
|
|
20
|
+
case 'pushEventToFrontend': {
|
|
21
|
+
const baseUrl = nodeParams.apiBaseUrl?.trim();
|
|
22
|
+
if (!baseUrl)
|
|
23
|
+
throw new Error('BGOS API base URL is required. Add BGOS API credentials to the node.');
|
|
24
|
+
// Use explicit pushEventType param, fall back to event_type from input data
|
|
25
|
+
const resolvedEventType = nodeParams.pushEventType?.trim() || eventType;
|
|
26
|
+
if (!resolvedEventType)
|
|
27
|
+
throw new Error('Event type is required for pushEventToFrontend operation.');
|
|
28
|
+
// Forward the entire event payload to /webhook/{eventType}
|
|
29
|
+
return await techWebhook_1.pushEventToFrontend.call(this, baseUrl, resolvedEventType, eventData);
|
|
30
|
+
}
|
|
20
31
|
case 'sendMessage': {
|
|
21
32
|
const userId = (nodeParams.userId ?? eventData.user_id ?? eventData.userId);
|
|
22
33
|
if (!userId)
|
|
@@ -33,6 +33,12 @@ export declare function sendMessageToBackend(this: IExecuteFunctions, options: B
|
|
|
33
33
|
isMixedAttachments?: boolean | null;
|
|
34
34
|
[key: string]: unknown;
|
|
35
35
|
}): Promise<unknown>;
|
|
36
|
+
/**
|
|
37
|
+
* Push a real-time event to the BGOS frontend via the public /webhook/{eventType} endpoint.
|
|
38
|
+
* No API key required — these endpoints are public. The user_id in the payload
|
|
39
|
+
* determines which WebSocket client receives the event.
|
|
40
|
+
*/
|
|
41
|
+
export declare function pushEventToFrontend(this: IExecuteFunctions, baseUrl: string, eventType: string, payload: Record<string, unknown>): Promise<unknown>;
|
|
36
42
|
export declare function reportCallbackResult(this: IExecuteFunctions, options: BgosApiOptions, payload: {
|
|
37
43
|
messageId: number | string;
|
|
38
44
|
optionId: number | string;
|
|
@@ -7,11 +7,12 @@ exports.renameChat = renameChat;
|
|
|
7
7
|
exports.deleteChat = deleteChat;
|
|
8
8
|
exports.saveChatHistory = saveChatHistory;
|
|
9
9
|
exports.sendMessageToBackend = sendMessageToBackend;
|
|
10
|
+
exports.pushEventToFrontend = pushEventToFrontend;
|
|
10
11
|
exports.reportCallbackResult = reportCallbackResult;
|
|
11
12
|
function buildHeaders(apiKey) {
|
|
12
13
|
const headers = { 'Content-Type': 'application/json', Accept: 'application/json' };
|
|
13
14
|
if (apiKey)
|
|
14
|
-
headers
|
|
15
|
+
headers['X-API-Key'] = apiKey;
|
|
15
16
|
return headers;
|
|
16
17
|
}
|
|
17
18
|
async function createAssistant(options, userId, assistantData) {
|
|
@@ -84,7 +85,7 @@ async function saveChatHistory(options, _userId, messages) {
|
|
|
84
85
|
async function sendMessageToBackend(options, payload) {
|
|
85
86
|
const url = `${options.baseUrl.replace(/\/$/, '')}/api/v1/send-message`;
|
|
86
87
|
const body = {
|
|
87
|
-
assistantId:
|
|
88
|
+
assistantId: Number(payload.assistantId),
|
|
88
89
|
chatId: Number(payload.chatId),
|
|
89
90
|
sender: payload.sender ?? 'assistant',
|
|
90
91
|
text: payload.text ?? null,
|
|
@@ -109,6 +110,21 @@ async function sendMessageToBackend(options, payload) {
|
|
|
109
110
|
json: true,
|
|
110
111
|
});
|
|
111
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Push a real-time event to the BGOS frontend via the public /webhook/{eventType} endpoint.
|
|
115
|
+
* No API key required — these endpoints are public. The user_id in the payload
|
|
116
|
+
* determines which WebSocket client receives the event.
|
|
117
|
+
*/
|
|
118
|
+
async function pushEventToFrontend(baseUrl, eventType, payload) {
|
|
119
|
+
const url = `${baseUrl.replace(/\/$/, '')}/webhook/${eventType}`;
|
|
120
|
+
return this.helpers.httpRequest({
|
|
121
|
+
method: 'POST',
|
|
122
|
+
url,
|
|
123
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
124
|
+
body: payload,
|
|
125
|
+
json: true,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
112
128
|
async function reportCallbackResult(options, payload) {
|
|
113
129
|
const url = `${options.baseUrl.replace(/\/$/, '')}/api/v1/messages/callback-result`;
|
|
114
130
|
const body = {
|
|
@@ -3,6 +3,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BgosTrigger = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
5
|
const stubs_1 = require("./stubs");
|
|
6
|
+
const RATE_WINDOW_MS = 1000;
|
|
7
|
+
const RATE_MAX_EVENTS = 10;
|
|
8
|
+
const GLOBAL_KEY = '__bgosRateLimiter';
|
|
9
|
+
function getRateLimiterMap() {
|
|
10
|
+
if (!global[GLOBAL_KEY]) {
|
|
11
|
+
global[GLOBAL_KEY] = new Map();
|
|
12
|
+
}
|
|
13
|
+
return global[GLOBAL_KEY];
|
|
14
|
+
}
|
|
15
|
+
function isRateLimited(workflowId) {
|
|
16
|
+
const map = getRateLimiterMap();
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
const history = map.get(workflowId) ?? [];
|
|
19
|
+
const recent = history.filter((ts) => now - ts < RATE_WINDOW_MS);
|
|
20
|
+
if (recent.length >= RATE_MAX_EVENTS)
|
|
21
|
+
return true;
|
|
22
|
+
recent.push(now);
|
|
23
|
+
map.set(workflowId, recent);
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
6
26
|
class BgosTrigger {
|
|
7
27
|
description = {
|
|
8
28
|
displayName: 'BGOS Trigger',
|
|
@@ -53,16 +73,9 @@ class BgosTrigger {
|
|
|
53
73
|
async webhook() {
|
|
54
74
|
const rawBody = this.getBodyData();
|
|
55
75
|
const updates = this.getNodeParameter('updates', 0);
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
const windowMs = 1000;
|
|
59
|
-
const maxEvents = 10;
|
|
60
|
-
const history = Array.isArray(staticData.rateHistory) ? staticData.rateHistory : [];
|
|
61
|
-
const recent = history.filter((ts) => now - ts < windowMs);
|
|
62
|
-
if (recent.length >= maxEvents)
|
|
76
|
+
const workflowId = String(this.getWorkflow().id ?? 'default');
|
|
77
|
+
if (isRateLimited(workflowId))
|
|
63
78
|
return { workflowData: [] };
|
|
64
|
-
recent.push(now);
|
|
65
|
-
staticData.rateHistory = recent;
|
|
66
79
|
let eventData;
|
|
67
80
|
if (rawBody && rawBody.event_type) {
|
|
68
81
|
eventData = rawBody;
|
package/package.json
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-bgos",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "n8n community nodes for BGOS (Brand Growth OS) - AI assistant chat platform",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"n8n",
|
|
8
|
+
"bgos",
|
|
9
|
+
"chat",
|
|
10
|
+
"assistant",
|
|
11
|
+
"ai"
|
|
12
|
+
],
|
|
6
13
|
"license": "MIT",
|
|
7
14
|
"homepage": "https://github.com/BrandGrowthOS/bgos-n8n-nodes",
|
|
8
15
|
"author": {
|
|
@@ -18,18 +25,7 @@
|
|
|
18
25
|
"node": ">=18.10",
|
|
19
26
|
"pnpm": ">=9.1"
|
|
20
27
|
},
|
|
21
|
-
"packageManager": "pnpm@9.1.4",
|
|
22
28
|
"main": "dist/nodes/Bgos/Bgos.node.js",
|
|
23
|
-
"scripts": {
|
|
24
|
-
"preinstall": "npx only-allow pnpm",
|
|
25
|
-
"build": "tsc && gulp build:icons",
|
|
26
|
-
"dev": "tsc --watch",
|
|
27
|
-
"format": "prettier nodes credentials --write",
|
|
28
|
-
"lint": "ESLINT_USE_FLAT_CONFIG=true eslint -c eslint.config.cjs .",
|
|
29
|
-
"lintfix": "ESLINT_USE_FLAT_CONFIG=true eslint -c eslint.config.cjs . --fix",
|
|
30
|
-
"prepack": "pnpm build",
|
|
31
|
-
"prepublishOnly": "pnpm build"
|
|
32
|
-
},
|
|
33
29
|
"files": [
|
|
34
30
|
"dist",
|
|
35
31
|
"README.md",
|
|
@@ -59,5 +55,13 @@
|
|
|
59
55
|
},
|
|
60
56
|
"peerDependencies": {
|
|
61
57
|
"n8n-workflow": "*"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"preinstall": "npx only-allow pnpm",
|
|
61
|
+
"build": "tsc && gulp build:icons",
|
|
62
|
+
"dev": "tsc --watch",
|
|
63
|
+
"format": "prettier nodes credentials --write",
|
|
64
|
+
"lint": "ESLINT_USE_FLAT_CONFIG=true eslint -c eslint.config.cjs .",
|
|
65
|
+
"lintfix": "ESLINT_USE_FLAT_CONFIG=true eslint -c eslint.config.cjs . --fix"
|
|
62
66
|
}
|
|
63
|
-
}
|
|
67
|
+
}
|