n8n-nodes-cribops 0.1.24 → 0.2.1

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,167 +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
- // Organization is automatically determined from API key
113
- const webhooks = await cribopsHttp.getWebhooks();
114
- // Filter for N8N type webhooks that are active and not linked
115
- const availableWebhooks = webhooks.filter((webhook) => webhook.type === 'N8N' &&
116
- webhook.status === 'active' &&
117
- !webhook.linked_workflow_id);
118
- return availableWebhooks.map((webhook) => ({
119
- name: webhook.name,
120
- value: webhook.id,
121
- description: webhook.description || `Type: ${webhook.type}`,
122
- }));
123
- }
124
- catch (error) {
125
- let errorMessage = 'Failed to load webhooks';
126
- if (error instanceof Error) {
127
- errorMessage += `: ${error.message}`;
128
- // Check for common issues
129
- if (error.message.includes('HTTP 401') || error.message.includes('Unauthorized')) {
130
- errorMessage = 'Authentication failed. Please check your API token in the credentials.';
131
- }
132
- else if (error.message.includes('HTTP 404')) {
133
- errorMessage = 'Webhook endpoint not found. Please check the API base URL in credentials.';
134
- }
135
- else if (error.message.includes('HTTP 403')) {
136
- errorMessage = 'Access forbidden. Your API token may not have permission to access webhooks.';
137
- }
138
- else if (error.message.includes('Failed to fetch') || error.message.includes('network')) {
139
- errorMessage = 'Network error. Please check the API base URL and ensure the Cribops API is accessible.';
140
- }
141
- else if (error.message.includes('HTTP 400')) {
142
- errorMessage = 'Bad request. Please check that the API endpoint is correct and your API token has the necessary permissions.';
143
- }
144
- }
145
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), errorMessage);
146
- }
147
- },
148
- },
149
- };
150
105
  webhookMethods = {
151
106
  default: {
152
107
  async checkExists() {
153
- // Always return false to create webhook registration
154
- // The actual webhook already exists in Cribops backend
108
+ // Always return false to ensure webhook is registered
155
109
  return false;
156
110
  },
157
111
  async create() {
158
112
  const webhookUrl = this.getNodeWebhookUrl('default');
159
- const webhookId = this.getNodeParameter('webhookId');
113
+ const webhookPath = this.getNodeParameter('webhookPath');
114
+ const options = this.getNodeParameter('options');
160
115
  const credentials = await this.getCredentials('cribopsApi');
161
116
  const workflowId = this.getWorkflow().id;
162
117
  const workflowName = this.getWorkflow().name;
118
+ if (!webhookPath) {
119
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Webhook path is required');
120
+ }
163
121
  const cribopsHttp = new CribopsHttp_1.CribopsHttp({
164
122
  baseUrl: credentials.baseUrl,
165
123
  apiToken: credentials.apiToken,
166
124
  });
167
125
  try {
168
- // Link this workflow to the webhook entity
169
- const body = {
170
- workflow_id: workflowId,
171
- 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,
172
130
  test_webhook_url: webhookUrl.replace('/webhook/', '/webhook-test/'),
131
+ workflow_id: workflowId,
173
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,
174
136
  };
175
- // Link the n8n workflow to the webhook entity
176
- await cribopsHttp.request('POST', `/api/v1/webhooks/${webhookId}/link`, body);
137
+ await cribopsHttp.registerN8NEndpoint(registrationData);
177
138
  // Store webhook data for later use
178
139
  const webhookData = this.getWorkflowStaticData('node');
179
- webhookData.webhookId = webhookId;
140
+ webhookData.webhookPath = webhookPath;
180
141
  webhookData.webhookUrl = webhookUrl;
181
142
  return true;
182
143
  }
183
144
  catch (error) {
184
- 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)}`);
185
146
  }
186
147
  },
187
148
  async delete() {
188
149
  const webhookData = this.getWorkflowStaticData('node');
189
150
  const credentials = await this.getCredentials('cribopsApi');
190
- if (!webhookData.webhookId) {
151
+ if (!webhookData.webhookPath) {
191
152
  return true;
192
153
  }
193
154
  const cribopsHttp = new CribopsHttp_1.CribopsHttp({
@@ -195,15 +156,15 @@ class CribopsTrigger {
195
156
  apiToken: credentials.apiToken,
196
157
  });
197
158
  try {
198
- // Unlink the workflow from the webhook entity
199
- await cribopsHttp.request('DELETE', `/api/v1/webhooks/${webhookData.webhookId}/link`);
200
- delete webhookData.webhookId;
159
+ // Unregister the N8N endpoint
160
+ await cribopsHttp.unregisterN8NEndpoint(webhookData.webhookPath);
161
+ delete webhookData.webhookPath;
201
162
  delete webhookData.webhookUrl;
202
163
  return true;
203
164
  }
204
165
  catch (error) {
205
166
  // Log error but don't fail
206
- console.error('Failed to unlink webhook:', error);
167
+ console.error('Failed to unregister N8N endpoint:', error);
207
168
  return true;
208
169
  }
209
170
  },
@@ -212,68 +173,70 @@ class CribopsTrigger {
212
173
  async webhook() {
213
174
  const body = this.getBodyData();
214
175
  const headers = this.getHeaderData();
215
- const eventTypes = this.getNodeParameter('eventTypes', []);
216
- const additionalFields = this.getNodeParameter('additionalFields', {});
217
- const webhookId = this.getNodeParameter('webhookId');
218
- // Validate secret token if provided
219
- if (additionalFields.secretToken) {
220
- // Check for signature in headers (could be HMAC signature or bearer token)
221
- const signature = headers['x-cribops-signature'] || headers['x-webhook-signature'];
222
- const authHeader = headers['authorization'];
223
- // You can implement HMAC signature validation here if needed
224
- // For now, simple token comparison
225
- 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) {
226
185
  return {
227
186
  webhookResponse: {
228
187
  status: 401,
229
- body: { error: 'Unauthorized' },
188
+ body: { error: 'Missing Cribops signature' },
230
189
  },
231
190
  };
232
191
  }
192
+ // TODO: Implement HMAC validation with shared secret
233
193
  }
234
- // Filter by event type if specified
235
- const eventType = body.event_type || body.type || body.eventType;
236
- if (eventTypes.length > 0 && eventType && !eventTypes.includes(eventType)) {
237
- return {
238
- webhookResponse: {
239
- status: 200,
240
- body: { received: true, filtered: true },
241
- },
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,
242
228
  };
243
229
  }
244
- // Prepare output data with all relevant fields
245
- const outputData = {
246
- // Core webhook data
247
- webhook_id: webhookId,
248
- event_type: eventType,
249
- // Message content
250
- message: body.message || body.content || body.text,
251
- // Conversation/thread tracking
252
- conversation_id: body.conversation_id || body.conversationId || body.thread_id || body.threadId,
253
- // User/agent identification
254
- user_id: body.user_id || body.userId || body.from_user || body.fromUser,
255
- agent_id: body.agent_id || body.agentId || body.to_agent || body.toAgent,
256
- // Response handling
257
- response_webhook: body.response_webhook || body.responseWebhook || body.callback_url || body.callbackUrl,
258
- // Metadata
259
- metadata: body.metadata || {},
260
- // File attachments if any
261
- attachments: body.attachments || body.files || [],
262
- // Timestamp
263
- timestamp: body.timestamp || body.created_at || body.createdAt || new Date().toISOString(),
264
- // Include any other fields from the webhook
265
- ...body,
266
- };
267
- // Include headers if requested
268
- const workflowData = additionalFields.includeHeaders
269
- ? { json: outputData, headers }
270
- : { json: outputData };
271
230
  // Return the data to the workflow
272
231
  return {
273
- workflowData: [[workflowData]],
232
+ workflowData: [[{ json: webhookData }]],
274
233
  webhookResponse: {
275
234
  status: 200,
276
- body: { received: true },
235
+ body: {
236
+ received: true,
237
+ webhook_id: cribopsMetadata.webhook_id,
238
+ processing_time_ms: processingTime,
239
+ },
277
240
  },
278
241
  };
279
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
+ }