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 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: `http://localhost:8080`)
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: 'http://localhost:8080',
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
- description: 'API key for authentication (optional, for future use)',
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
- Authorization: '={{"Bearer " + $credentials.apiKey}}',
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.Authorization = `Bearer ${apiKey}`;
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: String(payload.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 staticData = this.getWorkflowStaticData('node');
57
- const now = Date.now();
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",
3
+ "version": "1.0.4",
4
4
  "description": "n8n community nodes for BGOS (Brand Growth OS) - AI assistant chat platform",
5
- "keywords": ["n8n-community-node-package", "n8n", "bgos", "chat", "assistant", "ai"],
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
+ }