n8n-nodes-cribops 0.1.16 → 0.1.18

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.
@@ -21,7 +21,7 @@ class CribopsApi {
21
21
  displayName: 'Base URL',
22
22
  name: 'baseUrl',
23
23
  type: 'string',
24
- default: 'https://api.cribops.com',
24
+ default: 'https://cribops.com',
25
25
  required: true,
26
26
  description: 'Base URL of the Cribops API',
27
27
  },
@@ -48,6 +48,12 @@ class Cribops {
48
48
  description: 'List all available agents',
49
49
  action: 'List all available agents',
50
50
  },
51
+ {
52
+ name: 'Poll Queue',
53
+ value: 'pollQueue',
54
+ description: 'Poll messages from the queue',
55
+ action: 'Poll messages from the queue',
56
+ },
51
57
  {
52
58
  name: 'Reply to Conversation',
53
59
  value: 'replyToConversation',
@@ -149,6 +155,45 @@ class Cribops {
149
155
  },
150
156
  description: 'Whether to show typing indicator (true) or stop typing (false)',
151
157
  },
158
+ {
159
+ displayName: 'Tenant ID',
160
+ name: 'tenantId',
161
+ type: 'string',
162
+ required: true,
163
+ default: '',
164
+ placeholder: 'my-tenant',
165
+ displayOptions: {
166
+ show: {
167
+ operation: ['pollQueue'],
168
+ },
169
+ },
170
+ description: 'The tenant ID for your Cribops organization',
171
+ },
172
+ {
173
+ displayName: 'Batch Size',
174
+ name: 'batchSize',
175
+ type: 'number',
176
+ default: 10,
177
+ displayOptions: {
178
+ show: {
179
+ operation: ['pollQueue'],
180
+ },
181
+ },
182
+ description: 'Number of messages to retrieve (max 100)',
183
+ },
184
+ {
185
+ displayName: 'Queue Name',
186
+ name: 'queueName',
187
+ type: 'string',
188
+ default: '',
189
+ placeholder: 'e.g., stripe_events',
190
+ displayOptions: {
191
+ show: {
192
+ operation: ['pollQueue'],
193
+ },
194
+ },
195
+ description: 'Specific queue to poll (optional). Leave empty to poll all queues.',
196
+ },
152
197
  {
153
198
  displayName: 'Message',
154
199
  name: 'message',
@@ -342,6 +387,9 @@ class Cribops {
342
387
  case 'listAgents':
343
388
  responseData = await listAgents(this, cribopsHttp, i);
344
389
  break;
390
+ case 'pollQueue':
391
+ responseData = await pollQueue(this, cribopsHttp, i);
392
+ break;
345
393
  default:
346
394
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${operation}`, { itemIndex: i });
347
395
  }
@@ -536,6 +584,46 @@ async function listAgents(executeFunctions, cribopsHttp, itemIndex) {
536
584
  const agents = await cribopsHttp.getAgents();
537
585
  return { agents, count: agents.length };
538
586
  }
587
+ async function pollQueue(executeFunctions, cribopsHttp, itemIndex) {
588
+ const tenantId = executeFunctions.getNodeParameter('tenantId', itemIndex);
589
+ const batchSize = executeFunctions.getNodeParameter('batchSize', itemIndex, 10);
590
+ const queueName = executeFunctions.getNodeParameter('queueName', itemIndex, '') || undefined;
591
+ const messages = await cribopsHttp.pollQueue(tenantId, batchSize, queueName);
592
+ // Process each message to parse the data if it's JSON
593
+ const processedMessages = messages.map(message => {
594
+ let parsedData = message.data.data;
595
+ try {
596
+ parsedData = JSON.parse(message.data.data);
597
+ }
598
+ catch (e) {
599
+ // Keep as string if not valid JSON
600
+ }
601
+ return {
602
+ id: message.id,
603
+ correlation_id: message.correlation_id,
604
+ queue_name: message.queue_name,
605
+ data: parsedData,
606
+ headers: message.data.headers,
607
+ params: message.data.params,
608
+ inserted_at: message.inserted_at,
609
+ // Extract useful fields from headers
610
+ tenant_id: message.data.headers['x-cribops-tenant-id'] || tenantId,
611
+ path: message.data.headers['x-cribops-path'],
612
+ };
613
+ });
614
+ // Auto-acknowledge messages after processing
615
+ if (messages.length > 0) {
616
+ const messageIds = messages.map(msg => msg.id);
617
+ try {
618
+ await cribopsHttp.acknowledgeMessages(tenantId, messageIds);
619
+ }
620
+ catch (ackError) {
621
+ // Log error but don't fail the operation
622
+ console.error('Failed to acknowledge messages:', ackError);
623
+ }
624
+ }
625
+ return { messages: processedMessages, count: processedMessages.length };
626
+ }
539
627
  async function sendTypingIndicator(executeFunctions, cribopsHttp, itemIndex) {
540
628
  const agentId = executeFunctions.getNodeParameter('agentId', itemIndex, '', { extractValue: true });
541
629
  const conversationId = executeFunctions.getNodeParameter('conversationId', itemIndex);
@@ -1,9 +1,9 @@
1
- import { INodeType, INodeTypeDescription, ITriggerFunctions, ITriggerResponse, IWebhookFunctions, IWebhookResponseData, IHookFunctions, ILoadOptionsFunctions, INodePropertyOptions } from 'n8n-workflow';
1
+ import { INodeType, INodeTypeDescription, IWebhookFunctions, IWebhookResponseData, IHookFunctions, ILoadOptionsFunctions, INodePropertyOptions } from 'n8n-workflow';
2
2
  export declare class CribopsTrigger implements INodeType {
3
3
  description: INodeTypeDescription;
4
4
  methods: {
5
5
  loadOptions: {
6
- getAgents(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
6
+ getWebhooks(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
7
7
  };
8
8
  };
9
9
  webhookMethods: {
@@ -13,6 +13,5 @@ export declare class CribopsTrigger implements INodeType {
13
13
  delete(this: IHookFunctions): Promise<boolean>;
14
14
  };
15
15
  };
16
- trigger(this: ITriggerFunctions): Promise<ITriggerResponse>;
17
16
  webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
18
17
  }
@@ -10,7 +10,7 @@ class CribopsTrigger {
10
10
  icon: 'file:cribopstrigger.svg',
11
11
  group: ['trigger'],
12
12
  version: 1,
13
- description: 'Triggers when receiving messages from Cribops agents',
13
+ description: 'Triggers when receiving messages via Cribops webhook',
14
14
  defaults: {
15
15
  name: 'Cribops Trigger',
16
16
  },
@@ -27,21 +27,21 @@ class CribopsTrigger {
27
27
  name: 'default',
28
28
  httpMethod: 'POST',
29
29
  responseMode: 'onReceived',
30
- // Dynamic path to trigger UUID generation
31
- path: '={{$parameter["agentId"]}}',
30
+ path: '={{$parameter["webhookId"]}}',
31
+ isFullPath: false,
32
32
  },
33
33
  ],
34
34
  properties: [
35
35
  {
36
- displayName: 'Agent Name or ID',
37
- name: 'agentId',
36
+ displayName: 'Webhook Name or ID',
37
+ name: 'webhookId',
38
38
  type: 'options',
39
39
  required: true,
40
40
  typeOptions: {
41
- loadOptionsMethod: 'getAgents',
41
+ loadOptionsMethod: 'getWebhooks',
42
42
  },
43
43
  default: '',
44
- description: 'The Cribops agent to receive messages from. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
44
+ description: 'The Cribops webhook to use. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
45
45
  },
46
46
  {
47
47
  displayName: 'Event Types',
@@ -63,8 +63,13 @@ class CribopsTrigger {
63
63
  value: 'file_attachment',
64
64
  description: 'File attachments',
65
65
  },
66
+ {
67
+ name: 'System Event',
68
+ value: 'system_event',
69
+ description: 'System events and notifications',
70
+ },
66
71
  ],
67
- default: ['user_message'],
72
+ default: ['user_message', 'agent_response'],
68
73
  description: 'Types of events to trigger on',
69
74
  },
70
75
  {
@@ -82,7 +87,14 @@ class CribopsTrigger {
82
87
  password: true,
83
88
  },
84
89
  default: '',
85
- description: 'Secret token for webhook authentication',
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',
86
98
  },
87
99
  ],
88
100
  },
@@ -90,22 +102,25 @@ class CribopsTrigger {
90
102
  };
91
103
  methods = {
92
104
  loadOptions: {
93
- async getAgents() {
105
+ async getWebhooks() {
94
106
  const credentials = await this.getCredentials('cribopsApi');
95
107
  const cribopsHttp = new CribopsHttp_1.CribopsHttp({
96
108
  baseUrl: credentials.baseUrl,
97
109
  apiToken: credentials.apiToken,
98
110
  });
99
111
  try {
100
- const agents = await cribopsHttp.getAgents();
101
- return agents.map((agent) => ({
102
- name: agent.name,
103
- value: agent.id,
104
- description: `ID: ${agent.id}`,
112
+ // Call the API to get available webhooks
113
+ // The API endpoint would be something like GET /api/v1/webhooks
114
+ const response = await cribopsHttp.request('GET', '/api/v1/webhooks');
115
+ const webhooks = response.data || [];
116
+ return webhooks.map((webhook) => ({
117
+ name: webhook.name || webhook.id,
118
+ value: webhook.id,
119
+ description: webhook.description || `Webhook ID: ${webhook.id}`,
105
120
  }));
106
121
  }
107
122
  catch (error) {
108
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to load agents: ${error instanceof Error ? error.message : String(error)}`);
123
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to load webhooks: ${error instanceof Error ? error.message : String(error)}`);
109
124
  }
110
125
  },
111
126
  },
@@ -113,26 +128,13 @@ class CribopsTrigger {
113
128
  webhookMethods = {
114
129
  default: {
115
130
  async checkExists() {
116
- const webhookUrl = this.getNodeWebhookUrl('default');
117
- const agentId = this.getNodeParameter('agentId');
118
- const credentials = await this.getCredentials('cribopsApi');
119
- const cribopsHttp = new CribopsHttp_1.CribopsHttp({
120
- baseUrl: credentials.baseUrl,
121
- apiToken: credentials.apiToken,
122
- });
123
- try {
124
- // Check if webhook exists for this agent
125
- // This would need to be implemented in your Cribops API
126
- // For now, return false to always create
127
- return false;
128
- }
129
- catch (error) {
130
- return false;
131
- }
131
+ // Always return false to create webhook registration
132
+ // The actual webhook already exists in Cribops backend
133
+ return false;
132
134
  },
133
135
  async create() {
134
136
  const webhookUrl = this.getNodeWebhookUrl('default');
135
- const agentId = this.getNodeParameter('agentId');
137
+ const webhookId = this.getNodeParameter('webhookId');
136
138
  const eventTypes = this.getNodeParameter('eventTypes', []);
137
139
  const additionalFields = this.getNodeParameter('additionalFields', {});
138
140
  const credentials = await this.getCredentials('cribopsApi');
@@ -141,30 +143,29 @@ class CribopsTrigger {
141
143
  apiToken: credentials.apiToken,
142
144
  });
143
145
  try {
144
- // Register webhook with Cribops
145
- // This is a placeholder - implement actual API call
146
+ // Register this n8n webhook URL with the Cribops webhook
146
147
  const body = {
147
- url: webhookUrl,
148
- agent_id: agentId,
148
+ webhook_id: webhookId,
149
+ target_url: webhookUrl,
149
150
  event_types: eventTypes,
150
151
  secret: additionalFields.secretToken || undefined,
151
152
  };
152
- // TODO: Make actual API call to register webhook
153
- // await cribopsHttp.registerWebhook(agentId, body);
153
+ // Register the n8n webhook URL with Cribops
154
+ await cribopsHttp.request('POST', `/api/v1/webhooks/${webhookId}/targets`, body);
154
155
  // Store webhook data for later use
155
156
  const webhookData = this.getWorkflowStaticData('node');
156
- webhookData.webhookId = `webhook_${agentId}_${Date.now()}`;
157
- webhookData.agentId = agentId;
157
+ webhookData.webhookId = webhookId;
158
+ webhookData.targetUrl = webhookUrl;
158
159
  return true;
159
160
  }
160
161
  catch (error) {
161
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to register webhook: ${error instanceof Error ? error.message : String(error)}`);
162
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to register webhook target: ${error instanceof Error ? error.message : String(error)}`);
162
163
  }
163
164
  },
164
165
  async delete() {
165
166
  const webhookData = this.getWorkflowStaticData('node');
166
167
  const credentials = await this.getCredentials('cribopsApi');
167
- if (!webhookData.webhookId) {
168
+ if (!webhookData.webhookId || !webhookData.targetUrl) {
168
169
  return true;
169
170
  }
170
171
  const cribopsHttp = new CribopsHttp_1.CribopsHttp({
@@ -172,38 +173,36 @@ class CribopsTrigger {
172
173
  apiToken: credentials.apiToken,
173
174
  });
174
175
  try {
175
- // Unregister webhook from Cribops
176
- // TODO: Make actual API call to unregister webhook
177
- // await cribopsHttp.deleteWebhook(webhookData.agentId, webhookData.webhookId);
176
+ // Unregister the n8n webhook URL from Cribops
177
+ await cribopsHttp.request('DELETE', `/api/v1/webhooks/${webhookData.webhookId}/targets`, {
178
+ target_url: webhookData.targetUrl,
179
+ });
178
180
  delete webhookData.webhookId;
179
- delete webhookData.agentId;
181
+ delete webhookData.targetUrl;
180
182
  return true;
181
183
  }
182
184
  catch (error) {
183
- return false;
185
+ // Log error but don't fail
186
+ console.error('Failed to unregister webhook target:', error);
187
+ return true;
184
188
  }
185
189
  },
186
190
  },
187
191
  };
188
- async trigger() {
189
- // Minimal implementation - webhooks are handled by webhook() method
190
- return {
191
- closeFunction: async () => { },
192
- manualTriggerFunction: async () => {
193
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'This node only works with webhooks. Please activate the workflow.');
194
- },
195
- };
196
- }
197
192
  async webhook() {
198
193
  const body = this.getBodyData();
199
194
  const headers = this.getHeaderData();
200
195
  const eventTypes = this.getNodeParameter('eventTypes', []);
201
196
  const additionalFields = this.getNodeParameter('additionalFields', {});
202
- const agentId = this.getNodeParameter('agentId');
197
+ const webhookId = this.getNodeParameter('webhookId');
203
198
  // Validate secret token if provided
204
199
  if (additionalFields.secretToken) {
205
- const receivedToken = headers['x-cribops-signature'] || headers['authorization'];
206
- if (receivedToken !== additionalFields.secretToken) {
200
+ // Check for signature in headers (could be HMAC signature or bearer token)
201
+ const signature = headers['x-cribops-signature'] || headers['x-webhook-signature'];
202
+ const authHeader = headers['authorization'];
203
+ // You can implement HMAC signature validation here if needed
204
+ // For now, simple token comparison
205
+ if (additionalFields.secretToken !== signature && `Bearer ${additionalFields.secretToken}` !== authHeader) {
207
206
  return {
208
207
  webhookResponse: {
209
208
  status: 401,
@@ -212,8 +211,9 @@ class CribopsTrigger {
212
211
  };
213
212
  }
214
213
  }
215
- // Filter by event type
216
- if (eventTypes.length > 0 && body.type && !eventTypes.includes(body.type)) {
214
+ // Filter by event type if specified
215
+ const eventType = body.event_type || body.type || body.eventType;
216
+ if (eventTypes.length > 0 && eventType && !eventTypes.includes(eventType)) {
217
217
  return {
218
218
  webhookResponse: {
219
219
  status: 200,
@@ -221,27 +221,40 @@ class CribopsTrigger {
221
221
  },
222
222
  };
223
223
  }
224
- // Enrich the output with agent_id and other useful metadata
224
+ // Prepare output data with all relevant fields
225
225
  const outputData = {
226
+ // Core webhook data
227
+ webhook_id: webhookId,
228
+ event_type: eventType,
229
+ // Message content
230
+ message: body.message || body.content || body.text,
231
+ // Conversation/thread tracking
232
+ conversation_id: body.conversation_id || body.conversationId || body.thread_id || body.threadId,
233
+ // User/agent identification
234
+ user_id: body.user_id || body.userId || body.from_user || body.fromUser,
235
+ agent_id: body.agent_id || body.agentId || body.to_agent || body.toAgent,
236
+ // Response handling
237
+ response_webhook: body.response_webhook || body.responseWebhook || body.callback_url || body.callbackUrl,
238
+ // Metadata
239
+ metadata: body.metadata || {},
240
+ // File attachments if any
241
+ attachments: body.attachments || body.files || [],
242
+ // Timestamp
243
+ timestamp: body.timestamp || body.created_at || body.createdAt || new Date().toISOString(),
244
+ // Include any other fields from the webhook
226
245
  ...body,
227
- agent_id: agentId,
228
- // Ensure conversation_id is available (handle different field names)
229
- conversation_id: body.conversation_id || body.conversationId || body.thread_id,
230
- // Ensure response_webhook is available if it exists
231
- response_webhook: body.response_webhook || body.responseWebhook || body.callback_url,
232
246
  };
233
- // Log what we're outputting for debugging
234
- console.log('CribopsTrigger output:', JSON.stringify(outputData, null, 2));
247
+ // Include headers if requested
248
+ const workflowData = additionalFields.includeHeaders
249
+ ? { json: outputData, headers }
250
+ : { json: outputData };
235
251
  // Return the data to the workflow
236
252
  return {
237
- workflowData: [
238
- [
239
- {
240
- json: outputData,
241
- headers,
242
- },
243
- ],
244
- ],
253
+ workflowData: [[workflowData]],
254
+ webhookResponse: {
255
+ status: 200,
256
+ body: { received: true },
257
+ },
245
258
  };
246
259
  }
247
260
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-cribops",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "n8n community node for Cribops AI platform integration",
5
5
  "keywords": [
6
6
  "n8n-community-node-package"
@@ -1,4 +1,4 @@
1
- import { IDataObject } from 'n8n-workflow';
1
+ import { IDataObject, IHttpRequestMethods, IHttpRequestOptions } from 'n8n-workflow';
2
2
  export interface CribopsHttpConfig {
3
3
  baseUrl: string;
4
4
  apiToken: string;
@@ -26,6 +26,17 @@ export interface CribopsWebhookMessage {
26
26
  fileName?: string;
27
27
  fileType?: string;
28
28
  }
29
+ export interface CribopsQueueMessage {
30
+ id: number;
31
+ correlation_id: string;
32
+ queue_name: string;
33
+ data: {
34
+ data: string;
35
+ params: IDataObject;
36
+ headers: IDataObject;
37
+ };
38
+ inserted_at: string;
39
+ }
29
40
  export declare class CribopsHttp {
30
41
  private config;
31
42
  constructor(config: CribopsHttpConfig);
@@ -37,4 +48,14 @@ export declare class CribopsHttp {
37
48
  validateWebhook(payload: any, signature: string): Promise<boolean>;
38
49
  testConnection(): Promise<boolean>;
39
50
  sendTypingIndicator(agentId: string, conversationId: string, typing: boolean): Promise<any>;
51
+ pollQueue(tenantId: string, limit?: number, queueName?: string): Promise<CribopsQueueMessage[]>;
52
+ acknowledgeMessages(tenantId: string, messageIds: number[]): Promise<{
53
+ status: string;
54
+ deleted_count: number;
55
+ }>;
56
+ failMessages(tenantId: string, messageIds: number[], errorMessage: string): Promise<{
57
+ status: string;
58
+ updated_count: number;
59
+ }>;
60
+ request<T = any>(method: IHttpRequestMethods, endpoint: string, data?: IDataObject, options?: Partial<IHttpRequestOptions>): Promise<T>;
40
61
  }
@@ -112,5 +112,41 @@ class CribopsHttp {
112
112
  throw new Error(`Failed to send typing indicator for agent ${agentId}: ${error}`);
113
113
  }
114
114
  }
115
+ async pollQueue(tenantId, limit = 10, queueName) {
116
+ try {
117
+ const params = { limit };
118
+ if (queueName) {
119
+ params.queue_name = queueName;
120
+ }
121
+ const url = `/api/queue/${tenantId}/poll?${new URLSearchParams(params).toString()}`;
122
+ const response = await this.makeRequest('GET', url);
123
+ return response;
124
+ }
125
+ catch (error) {
126
+ throw new Error(`Failed to poll queue for tenant ${tenantId}: ${error}`);
127
+ }
128
+ }
129
+ async acknowledgeMessages(tenantId, messageIds) {
130
+ try {
131
+ const response = await this.makeRequest('POST', `/api/queue/${tenantId}/acknowledge`, { message_ids: messageIds });
132
+ return response;
133
+ }
134
+ catch (error) {
135
+ throw new Error(`Failed to acknowledge messages for tenant ${tenantId}: ${error}`);
136
+ }
137
+ }
138
+ async failMessages(tenantId, messageIds, errorMessage) {
139
+ try {
140
+ const response = await this.makeRequest('POST', `/api/queue/${tenantId}/fail`, { message_ids: messageIds, error_message: errorMessage });
141
+ return response;
142
+ }
143
+ catch (error) {
144
+ throw new Error(`Failed to mark messages as failed for tenant ${tenantId}: ${error}`);
145
+ }
146
+ }
147
+ // Generic request method for custom API calls
148
+ async request(method, endpoint, data, options) {
149
+ return this.makeRequest(method, endpoint, data, options);
150
+ }
115
151
  }
116
152
  exports.CribopsHttp = CribopsHttp;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-cribops",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "n8n community node for Cribops AI platform integration",
5
5
  "keywords": [
6
6
  "n8n-community-node-package"