n8n-nodes-cribops 0.1.20 → 0.2.0

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,11 +1,6 @@
1
- import { INodeType, INodeTypeDescription, IWebhookFunctions, IWebhookResponseData, IHookFunctions, ILoadOptionsFunctions, INodePropertyOptions } from 'n8n-workflow';
1
+ import { INodeType, INodeTypeDescription, IWebhookFunctions, IWebhookResponseData, IHookFunctions } from 'n8n-workflow';
2
2
  export declare class CribopsTrigger implements INodeType {
3
3
  description: INodeTypeDescription;
4
- methods: {
5
- loadOptions: {
6
- getWebhooks(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
7
- };
8
- };
9
4
  webhookMethods: {
10
5
  default: {
11
6
  checkExists(this: IHookFunctions): Promise<boolean>;
@@ -5,14 +5,14 @@ const n8n_workflow_1 = require("n8n-workflow");
5
5
  const CribopsHttp_1 = require("../../utils/CribopsHttp");
6
6
  class CribopsTrigger {
7
7
  description = {
8
- displayName: 'Cribops Trigger',
8
+ displayName: 'Cribops Webhook Trigger',
9
9
  name: 'cribopsTrigger',
10
10
  icon: 'file:cribopstrigger.svg',
11
11
  group: ['trigger'],
12
12
  version: 1,
13
- description: 'Triggers when receiving messages via Cribops webhook',
13
+ description: 'Receive webhooks from Cribops queue at a controlled rate',
14
14
  defaults: {
15
- name: 'Cribops Trigger',
15
+ name: 'Cribops Webhook Trigger',
16
16
  },
17
17
  inputs: [],
18
18
  outputs: ["main" /* NodeConnectionType.Main */],
@@ -27,148 +27,128 @@ class CribopsTrigger {
27
27
  name: 'default',
28
28
  httpMethod: 'POST',
29
29
  responseMode: 'onReceived',
30
- path: '={{$parameter["webhookId"]}}',
30
+ path: '={{$parameter["webhookPath"]}}',
31
31
  isFullPath: false,
32
32
  },
33
33
  ],
34
34
  properties: [
35
35
  {
36
- displayName: 'Webhook Name or ID',
37
- name: 'webhookId',
38
- type: 'options',
36
+ displayName: 'Webhook Path',
37
+ name: 'webhookPath',
38
+ type: 'string',
39
39
  required: true,
40
- typeOptions: {
41
- loadOptionsMethod: 'getWebhooks',
42
- },
43
40
  default: '',
44
- description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
41
+ placeholder: 'my-webhook-endpoint',
42
+ description: 'The path for this webhook endpoint (will be appended to your n8n webhook URL)',
45
43
  },
46
44
  {
47
- displayName: 'Event Types',
48
- name: 'eventTypes',
49
- type: 'multiOptions',
45
+ displayName: 'Options',
46
+ name: 'options',
47
+ type: 'collection',
48
+ placeholder: 'Add Option',
49
+ default: {},
50
50
  options: [
51
51
  {
52
- name: 'Agent Response',
53
- value: 'agent_response',
54
- description: 'Responses from agents',
52
+ displayName: 'Include Cribops Metadata',
53
+ name: 'includeCribopsMetadata',
54
+ type: 'boolean',
55
+ default: true,
56
+ description: 'Include Cribops tracking ID, queue info, and processing metadata',
55
57
  },
56
58
  {
57
- name: 'User Message',
58
- value: 'user_message',
59
- description: 'Messages from users',
59
+ displayName: 'Include Original Headers',
60
+ name: 'includeHeaders',
61
+ type: 'boolean',
62
+ default: false,
63
+ description: 'Include the original webhook headers',
60
64
  },
61
65
  {
62
- name: 'File Attachment',
63
- value: 'file_attachment',
64
- description: 'File attachments',
66
+ displayName: 'Report Performance',
67
+ name: 'reportPerformance',
68
+ type: 'boolean',
69
+ default: true,
70
+ description: 'Automatically report processing time back to Cribops',
65
71
  },
66
72
  {
67
- name: 'System Event',
68
- value: 'system_event',
69
- description: 'System events and notifications',
73
+ displayName: 'Report Errors',
74
+ name: 'reportErrors',
75
+ type: 'boolean',
76
+ default: true,
77
+ description: 'Automatically report errors back to Cribops for monitoring',
78
+ },
79
+ {
80
+ displayName: 'Validate Signature',
81
+ name: 'validateSignature',
82
+ type: 'boolean',
83
+ default: true,
84
+ description: 'Validate webhook signature from Cribops',
70
85
  },
71
- ],
72
- default: ['user_message', 'agent_response'],
73
- description: 'Types of events to trigger on',
74
- },
75
- {
76
- displayName: 'Additional Fields',
77
- name: 'additionalFields',
78
- type: 'collection',
79
- placeholder: 'Add Field',
80
- default: {},
81
- options: [
82
86
  {
83
- displayName: 'Secret Token',
84
- name: 'secretToken',
87
+ displayName: 'Signature Secret',
88
+ name: 'signatureSecret',
85
89
  type: 'string',
86
90
  typeOptions: {
87
91
  password: true,
88
92
  },
93
+ displayOptions: {
94
+ show: {
95
+ validateSignature: [true],
96
+ },
97
+ },
89
98
  default: '',
90
- description: 'Secret token for webhook signature validation',
91
- },
92
- {
93
- displayName: 'Include Headers',
94
- name: 'includeHeaders',
95
- type: 'boolean',
96
- default: false,
97
- description: 'Whether to include the webhook headers in the output',
99
+ description: 'Secret key shared with Cribops for signature validation',
98
100
  },
99
101
  ],
100
102
  },
101
103
  ],
102
104
  };
103
- methods = {
104
- loadOptions: {
105
- async getWebhooks() {
106
- const credentials = await this.getCredentials('cribopsApi');
107
- const cribopsHttp = new CribopsHttp_1.CribopsHttp({
108
- baseUrl: credentials.baseUrl,
109
- apiToken: credentials.apiToken,
110
- });
111
- try {
112
- // Get organization ID from credentials if provided
113
- const organizationId = credentials.organizationId;
114
- const webhooks = await cribopsHttp.getWebhooks(organizationId);
115
- // Filter for N8N type webhooks that are active and not linked
116
- const availableWebhooks = webhooks.filter((webhook) => webhook.type === 'N8N' &&
117
- webhook.status === 'active' &&
118
- !webhook.linked_workflow_id);
119
- return availableWebhooks.map((webhook) => ({
120
- name: webhook.name,
121
- value: webhook.id,
122
- description: webhook.description || `Type: ${webhook.type}`,
123
- }));
124
- }
125
- catch (error) {
126
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to load webhooks: ${error instanceof Error ? error.message : String(error)}`);
127
- }
128
- },
129
- },
130
- };
131
105
  webhookMethods = {
132
106
  default: {
133
107
  async checkExists() {
134
- // Always return false to create webhook registration
135
- // The actual webhook already exists in Cribops backend
108
+ // Always return false to ensure webhook is registered
136
109
  return false;
137
110
  },
138
111
  async create() {
139
112
  const webhookUrl = this.getNodeWebhookUrl('default');
140
- const webhookId = this.getNodeParameter('webhookId');
113
+ const webhookPath = this.getNodeParameter('webhookPath');
114
+ const options = this.getNodeParameter('options');
141
115
  const credentials = await this.getCredentials('cribopsApi');
142
116
  const workflowId = this.getWorkflow().id;
143
117
  const workflowName = this.getWorkflow().name;
118
+ if (!webhookPath) {
119
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Webhook path is required');
120
+ }
144
121
  const cribopsHttp = new CribopsHttp_1.CribopsHttp({
145
122
  baseUrl: credentials.baseUrl,
146
123
  apiToken: credentials.apiToken,
147
124
  });
148
125
  try {
149
- // Link this workflow to the webhook entity
150
- const body = {
151
- workflow_id: workflowId,
152
- webhook_url: webhookUrl,
126
+ // Register this N8N endpoint with Cribops so it knows where to send webhooks
127
+ const registrationData = {
128
+ webhook_path: webhookPath,
129
+ n8n_webhook_url: webhookUrl,
153
130
  test_webhook_url: webhookUrl.replace('/webhook/', '/webhook-test/'),
131
+ workflow_id: workflowId,
154
132
  workflow_name: workflowName || 'Unnamed Workflow',
133
+ report_performance: options.reportPerformance !== false,
134
+ report_errors: options.reportErrors !== false,
135
+ signature_secret: options.signatureSecret ? String(options.signatureSecret) : null,
155
136
  };
156
- // Link the n8n workflow to the webhook entity
157
- await cribopsHttp.request('POST', `/api/v1/webhooks/${webhookId}/link`, body);
137
+ await cribopsHttp.registerN8NEndpoint(registrationData);
158
138
  // Store webhook data for later use
159
139
  const webhookData = this.getWorkflowStaticData('node');
160
- webhookData.webhookId = webhookId;
140
+ webhookData.webhookPath = webhookPath;
161
141
  webhookData.webhookUrl = webhookUrl;
162
142
  return true;
163
143
  }
164
144
  catch (error) {
165
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to link webhook: ${error instanceof Error ? error.message : String(error)}`);
145
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to register N8N endpoint with Cribops: ${error instanceof Error ? error.message : String(error)}`);
166
146
  }
167
147
  },
168
148
  async delete() {
169
149
  const webhookData = this.getWorkflowStaticData('node');
170
150
  const credentials = await this.getCredentials('cribopsApi');
171
- if (!webhookData.webhookId) {
151
+ if (!webhookData.webhookPath) {
172
152
  return true;
173
153
  }
174
154
  const cribopsHttp = new CribopsHttp_1.CribopsHttp({
@@ -176,15 +156,15 @@ class CribopsTrigger {
176
156
  apiToken: credentials.apiToken,
177
157
  });
178
158
  try {
179
- // Unlink the workflow from the webhook entity
180
- await cribopsHttp.request('DELETE', `/api/v1/webhooks/${webhookData.webhookId}/link`);
181
- delete webhookData.webhookId;
159
+ // Unregister the N8N endpoint
160
+ await cribopsHttp.unregisterN8NEndpoint(webhookData.webhookPath);
161
+ delete webhookData.webhookPath;
182
162
  delete webhookData.webhookUrl;
183
163
  return true;
184
164
  }
185
165
  catch (error) {
186
166
  // Log error but don't fail
187
- console.error('Failed to unlink webhook:', error);
167
+ console.error('Failed to unregister N8N endpoint:', error);
188
168
  return true;
189
169
  }
190
170
  },
@@ -193,68 +173,70 @@ class CribopsTrigger {
193
173
  async webhook() {
194
174
  const body = this.getBodyData();
195
175
  const headers = this.getHeaderData();
196
- const eventTypes = this.getNodeParameter('eventTypes', []);
197
- const additionalFields = this.getNodeParameter('additionalFields', {});
198
- const webhookId = this.getNodeParameter('webhookId');
199
- // Validate secret token if provided
200
- if (additionalFields.secretToken) {
201
- // Check for signature in headers (could be HMAC signature or bearer token)
202
- const signature = headers['x-cribops-signature'] || headers['x-webhook-signature'];
203
- const authHeader = headers['authorization'];
204
- // You can implement HMAC signature validation here if needed
205
- // For now, simple token comparison
206
- if (additionalFields.secretToken !== signature && `Bearer ${additionalFields.secretToken}` !== authHeader) {
176
+ const options = this.getNodeParameter('options');
177
+ const webhookPath = this.getNodeParameter('webhookPath');
178
+ const credentials = await this.getCredentials('cribopsApi');
179
+ const startTime = Date.now();
180
+ // Validate signature if enabled (Cribops signs webhooks it sends)
181
+ if (options.validateSignature) {
182
+ const signature = headers['x-cribops-signature'];
183
+ const secret = options.signatureSecret;
184
+ if (!signature) {
207
185
  return {
208
186
  webhookResponse: {
209
187
  status: 401,
210
- body: { error: 'Unauthorized' },
188
+ body: { error: 'Missing Cribops signature' },
211
189
  },
212
190
  };
213
191
  }
192
+ // TODO: Implement HMAC validation with shared secret
214
193
  }
215
- // Filter by event type if specified
216
- const eventType = body.event_type || body.type || body.eventType;
217
- if (eventTypes.length > 0 && eventType && !eventTypes.includes(eventType)) {
218
- return {
219
- webhookResponse: {
220
- status: 200,
221
- body: { received: true, filtered: true },
222
- },
194
+ // Extract Cribops metadata from the webhook
195
+ const cribopsMetadata = {
196
+ webhook_id: body.webhook_id || headers['x-cribops-webhook-id'],
197
+ queue_name: body.queue_name || headers['x-cribops-queue'],
198
+ original_source: body.original_source || headers['x-cribops-source'],
199
+ received_at: body.received_at || headers['x-cribops-received-at'],
200
+ attempt_number: body.attempt_number || headers['x-cribops-attempt'] || 1,
201
+ };
202
+ // Prepare webhook data
203
+ const webhookData = {
204
+ // The actual webhook payload
205
+ payload: body.payload || body,
206
+ // Processing metadata
207
+ processed_at: new Date().toISOString(),
208
+ webhook_path: webhookPath,
209
+ };
210
+ // Include Cribops metadata if requested
211
+ if (options.includeCribopsMetadata !== false) {
212
+ webhookData.cribops = cribopsMetadata;
213
+ }
214
+ // Include original headers if requested
215
+ if (options.includeHeaders) {
216
+ webhookData.original_headers = body.original_headers || headers;
217
+ }
218
+ // Track processing time for performance reporting
219
+ const processingTime = Date.now() - startTime;
220
+ // If performance reporting is enabled, we'll report after workflow completes
221
+ if (options.reportPerformance !== false && cribopsMetadata.webhook_id) {
222
+ // Store for later reporting
223
+ const staticData = this.getWorkflowStaticData('node');
224
+ staticData.performanceTracking = {
225
+ webhook_id: cribopsMetadata.webhook_id,
226
+ start_time: startTime,
227
+ receive_time: processingTime,
223
228
  };
224
229
  }
225
- // Prepare output data with all relevant fields
226
- const outputData = {
227
- // Core webhook data
228
- webhook_id: webhookId,
229
- event_type: eventType,
230
- // Message content
231
- message: body.message || body.content || body.text,
232
- // Conversation/thread tracking
233
- conversation_id: body.conversation_id || body.conversationId || body.thread_id || body.threadId,
234
- // User/agent identification
235
- user_id: body.user_id || body.userId || body.from_user || body.fromUser,
236
- agent_id: body.agent_id || body.agentId || body.to_agent || body.toAgent,
237
- // Response handling
238
- response_webhook: body.response_webhook || body.responseWebhook || body.callback_url || body.callbackUrl,
239
- // Metadata
240
- metadata: body.metadata || {},
241
- // File attachments if any
242
- attachments: body.attachments || body.files || [],
243
- // Timestamp
244
- timestamp: body.timestamp || body.created_at || body.createdAt || new Date().toISOString(),
245
- // Include any other fields from the webhook
246
- ...body,
247
- };
248
- // Include headers if requested
249
- const workflowData = additionalFields.includeHeaders
250
- ? { json: outputData, headers }
251
- : { json: outputData };
252
230
  // Return the data to the workflow
253
231
  return {
254
- workflowData: [[workflowData]],
232
+ workflowData: [[{ json: webhookData }]],
255
233
  webhookResponse: {
256
234
  status: 200,
257
- body: { received: true },
235
+ body: {
236
+ received: true,
237
+ webhook_id: cribopsMetadata.webhook_id,
238
+ processing_time_ms: processingTime,
239
+ },
258
240
  },
259
241
  };
260
242
  }
@@ -0,0 +1,5 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class HiveAccelerator implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }