n8n-nodes-cribops 0.1.17 → 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.
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { INodeType, INodeTypeDescription,
|
|
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
|
-
|
|
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
|
|
13
|
+
description: 'Triggers when receiving messages via Cribops webhook',
|
|
14
14
|
defaults: {
|
|
15
15
|
name: 'Cribops Trigger',
|
|
16
16
|
},
|
|
@@ -27,92 +27,21 @@ class CribopsTrigger {
|
|
|
27
27
|
name: 'default',
|
|
28
28
|
httpMethod: 'POST',
|
|
29
29
|
responseMode: 'onReceived',
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
isFullPath: true,
|
|
30
|
+
path: '={{$parameter["webhookId"]}}',
|
|
31
|
+
isFullPath: false,
|
|
33
32
|
},
|
|
34
33
|
],
|
|
35
|
-
polling: true,
|
|
36
34
|
properties: [
|
|
37
35
|
{
|
|
38
|
-
displayName: '
|
|
39
|
-
name: '
|
|
36
|
+
displayName: 'Webhook Name or ID',
|
|
37
|
+
name: 'webhookId',
|
|
40
38
|
type: 'options',
|
|
41
|
-
options: [
|
|
42
|
-
{
|
|
43
|
-
name: 'Polling',
|
|
44
|
-
value: 'polling',
|
|
45
|
-
description: 'Poll the queue for messages at regular intervals',
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: 'Webhook',
|
|
49
|
-
value: 'webhook',
|
|
50
|
-
description: 'Receive messages via webhook',
|
|
51
|
-
},
|
|
52
|
-
],
|
|
53
|
-
default: 'polling',
|
|
54
|
-
description: 'How to receive messages from Cribops',
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
displayName: 'Tenant ID',
|
|
58
|
-
name: 'tenantId',
|
|
59
|
-
type: 'string',
|
|
60
39
|
required: true,
|
|
61
|
-
default: '',
|
|
62
|
-
description: 'The tenant ID for your Cribops organization',
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
displayName: 'Agent Name or ID',
|
|
66
|
-
name: 'agentId',
|
|
67
|
-
type: 'options',
|
|
68
|
-
required: true,
|
|
69
|
-
displayOptions: {
|
|
70
|
-
show: {
|
|
71
|
-
triggerMode: ['webhook'],
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
40
|
typeOptions: {
|
|
75
|
-
loadOptionsMethod: '
|
|
41
|
+
loadOptionsMethod: 'getWebhooks',
|
|
76
42
|
},
|
|
77
43
|
default: '',
|
|
78
|
-
description: 'The Cribops
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
displayName: 'Queue Name',
|
|
82
|
-
name: 'queueName',
|
|
83
|
-
type: 'string',
|
|
84
|
-
displayOptions: {
|
|
85
|
-
show: {
|
|
86
|
-
triggerMode: ['polling'],
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
default: '',
|
|
90
|
-
placeholder: 'e.g., stripe_events',
|
|
91
|
-
description: 'Specific queue to poll (optional). Leave empty to poll all queues.',
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
displayName: 'Poll Interval',
|
|
95
|
-
name: 'pollInterval',
|
|
96
|
-
type: 'number',
|
|
97
|
-
displayOptions: {
|
|
98
|
-
show: {
|
|
99
|
-
triggerMode: ['polling'],
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
default: 30,
|
|
103
|
-
description: 'How often to poll for messages in seconds',
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
displayName: 'Batch Size',
|
|
107
|
-
name: 'batchSize',
|
|
108
|
-
type: 'number',
|
|
109
|
-
displayOptions: {
|
|
110
|
-
show: {
|
|
111
|
-
triggerMode: ['polling'],
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
default: 10,
|
|
115
|
-
description: 'Number of messages to retrieve per poll (max 100)',
|
|
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>.',
|
|
116
45
|
},
|
|
117
46
|
{
|
|
118
47
|
displayName: 'Event Types',
|
|
@@ -134,8 +63,13 @@ class CribopsTrigger {
|
|
|
134
63
|
value: 'file_attachment',
|
|
135
64
|
description: 'File attachments',
|
|
136
65
|
},
|
|
66
|
+
{
|
|
67
|
+
name: 'System Event',
|
|
68
|
+
value: 'system_event',
|
|
69
|
+
description: 'System events and notifications',
|
|
70
|
+
},
|
|
137
71
|
],
|
|
138
|
-
default: ['user_message'],
|
|
72
|
+
default: ['user_message', 'agent_response'],
|
|
139
73
|
description: 'Types of events to trigger on',
|
|
140
74
|
},
|
|
141
75
|
{
|
|
@@ -153,7 +87,14 @@ class CribopsTrigger {
|
|
|
153
87
|
password: true,
|
|
154
88
|
},
|
|
155
89
|
default: '',
|
|
156
|
-
description: 'Secret token for webhook
|
|
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',
|
|
157
98
|
},
|
|
158
99
|
],
|
|
159
100
|
},
|
|
@@ -161,22 +102,25 @@ class CribopsTrigger {
|
|
|
161
102
|
};
|
|
162
103
|
methods = {
|
|
163
104
|
loadOptions: {
|
|
164
|
-
async
|
|
105
|
+
async getWebhooks() {
|
|
165
106
|
const credentials = await this.getCredentials('cribopsApi');
|
|
166
107
|
const cribopsHttp = new CribopsHttp_1.CribopsHttp({
|
|
167
108
|
baseUrl: credentials.baseUrl,
|
|
168
109
|
apiToken: credentials.apiToken,
|
|
169
110
|
});
|
|
170
111
|
try {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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}`,
|
|
176
120
|
}));
|
|
177
121
|
}
|
|
178
122
|
catch (error) {
|
|
179
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to load
|
|
123
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to load webhooks: ${error instanceof Error ? error.message : String(error)}`);
|
|
180
124
|
}
|
|
181
125
|
},
|
|
182
126
|
},
|
|
@@ -184,26 +128,13 @@ class CribopsTrigger {
|
|
|
184
128
|
webhookMethods = {
|
|
185
129
|
default: {
|
|
186
130
|
async checkExists() {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const cribopsHttp = new CribopsHttp_1.CribopsHttp({
|
|
191
|
-
baseUrl: credentials.baseUrl,
|
|
192
|
-
apiToken: credentials.apiToken,
|
|
193
|
-
});
|
|
194
|
-
try {
|
|
195
|
-
// Check if webhook exists for this agent
|
|
196
|
-
// This would need to be implemented in your Cribops API
|
|
197
|
-
// For now, return false to always create
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
catch (error) {
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
131
|
+
// Always return false to create webhook registration
|
|
132
|
+
// The actual webhook already exists in Cribops backend
|
|
133
|
+
return false;
|
|
203
134
|
},
|
|
204
135
|
async create() {
|
|
205
136
|
const webhookUrl = this.getNodeWebhookUrl('default');
|
|
206
|
-
const
|
|
137
|
+
const webhookId = this.getNodeParameter('webhookId');
|
|
207
138
|
const eventTypes = this.getNodeParameter('eventTypes', []);
|
|
208
139
|
const additionalFields = this.getNodeParameter('additionalFields', {});
|
|
209
140
|
const credentials = await this.getCredentials('cribopsApi');
|
|
@@ -212,30 +143,29 @@ class CribopsTrigger {
|
|
|
212
143
|
apiToken: credentials.apiToken,
|
|
213
144
|
});
|
|
214
145
|
try {
|
|
215
|
-
// Register webhook with Cribops
|
|
216
|
-
// This is a placeholder - implement actual API call
|
|
146
|
+
// Register this n8n webhook URL with the Cribops webhook
|
|
217
147
|
const body = {
|
|
218
|
-
|
|
219
|
-
|
|
148
|
+
webhook_id: webhookId,
|
|
149
|
+
target_url: webhookUrl,
|
|
220
150
|
event_types: eventTypes,
|
|
221
151
|
secret: additionalFields.secretToken || undefined,
|
|
222
152
|
};
|
|
223
|
-
//
|
|
224
|
-
|
|
153
|
+
// Register the n8n webhook URL with Cribops
|
|
154
|
+
await cribopsHttp.request('POST', `/api/v1/webhooks/${webhookId}/targets`, body);
|
|
225
155
|
// Store webhook data for later use
|
|
226
156
|
const webhookData = this.getWorkflowStaticData('node');
|
|
227
|
-
webhookData.webhookId =
|
|
228
|
-
webhookData.
|
|
157
|
+
webhookData.webhookId = webhookId;
|
|
158
|
+
webhookData.targetUrl = webhookUrl;
|
|
229
159
|
return true;
|
|
230
160
|
}
|
|
231
161
|
catch (error) {
|
|
232
|
-
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)}`);
|
|
233
163
|
}
|
|
234
164
|
},
|
|
235
165
|
async delete() {
|
|
236
166
|
const webhookData = this.getWorkflowStaticData('node');
|
|
237
167
|
const credentials = await this.getCredentials('cribopsApi');
|
|
238
|
-
if (!webhookData.webhookId) {
|
|
168
|
+
if (!webhookData.webhookId || !webhookData.targetUrl) {
|
|
239
169
|
return true;
|
|
240
170
|
}
|
|
241
171
|
const cribopsHttp = new CribopsHttp_1.CribopsHttp({
|
|
@@ -243,118 +173,36 @@ class CribopsTrigger {
|
|
|
243
173
|
apiToken: credentials.apiToken,
|
|
244
174
|
});
|
|
245
175
|
try {
|
|
246
|
-
// Unregister webhook from Cribops
|
|
247
|
-
|
|
248
|
-
|
|
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
|
+
});
|
|
249
180
|
delete webhookData.webhookId;
|
|
250
|
-
delete webhookData.
|
|
181
|
+
delete webhookData.targetUrl;
|
|
251
182
|
return true;
|
|
252
183
|
}
|
|
253
184
|
catch (error) {
|
|
254
|
-
|
|
185
|
+
// Log error but don't fail
|
|
186
|
+
console.error('Failed to unregister webhook target:', error);
|
|
187
|
+
return true;
|
|
255
188
|
}
|
|
256
189
|
},
|
|
257
190
|
},
|
|
258
191
|
};
|
|
259
|
-
async trigger() {
|
|
260
|
-
const triggerMode = this.getNodeParameter('triggerMode');
|
|
261
|
-
if (triggerMode === 'webhook') {
|
|
262
|
-
// Webhook mode - minimal implementation as webhooks are handled by webhook() method
|
|
263
|
-
return {
|
|
264
|
-
closeFunction: async () => { },
|
|
265
|
-
manualTriggerFunction: async () => {
|
|
266
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'This node only works with webhooks in webhook mode. Please activate the workflow.');
|
|
267
|
-
},
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
// Polling mode
|
|
271
|
-
const credentials = await this.getCredentials('cribopsApi');
|
|
272
|
-
const tenantId = this.getNodeParameter('tenantId');
|
|
273
|
-
const pollInterval = this.getNodeParameter('pollInterval', 30);
|
|
274
|
-
const batchSize = this.getNodeParameter('batchSize', 10);
|
|
275
|
-
const queueName = this.getNodeParameter('queueName', '') || undefined;
|
|
276
|
-
const cribopsHttp = new CribopsHttp_1.CribopsHttp({
|
|
277
|
-
baseUrl: credentials.baseUrl,
|
|
278
|
-
apiToken: credentials.apiToken,
|
|
279
|
-
});
|
|
280
|
-
let intervalId;
|
|
281
|
-
const poll = async () => {
|
|
282
|
-
try {
|
|
283
|
-
const messages = await cribopsHttp.pollQueue(tenantId, batchSize, queueName);
|
|
284
|
-
if (messages.length > 0) {
|
|
285
|
-
const messageIds = messages.map(msg => msg.id);
|
|
286
|
-
// Process each message
|
|
287
|
-
for (const message of messages) {
|
|
288
|
-
// Parse the webhook data if it's JSON
|
|
289
|
-
let parsedData = message.data.data;
|
|
290
|
-
try {
|
|
291
|
-
parsedData = JSON.parse(message.data.data);
|
|
292
|
-
}
|
|
293
|
-
catch (e) {
|
|
294
|
-
// Keep as string if not valid JSON
|
|
295
|
-
}
|
|
296
|
-
// Emit the message
|
|
297
|
-
this.emit([
|
|
298
|
-
[
|
|
299
|
-
{
|
|
300
|
-
json: {
|
|
301
|
-
id: message.id,
|
|
302
|
-
correlation_id: message.correlation_id,
|
|
303
|
-
queue_name: message.queue_name,
|
|
304
|
-
data: parsedData,
|
|
305
|
-
headers: message.data.headers,
|
|
306
|
-
params: message.data.params,
|
|
307
|
-
inserted_at: message.inserted_at,
|
|
308
|
-
// Extract useful fields from headers
|
|
309
|
-
tenant_id: message.data.headers['x-cribops-tenant-id'] || tenantId,
|
|
310
|
-
path: message.data.headers['x-cribops-path'],
|
|
311
|
-
},
|
|
312
|
-
},
|
|
313
|
-
],
|
|
314
|
-
]);
|
|
315
|
-
}
|
|
316
|
-
// Acknowledge messages after processing
|
|
317
|
-
try {
|
|
318
|
-
await cribopsHttp.acknowledgeMessages(tenantId, messageIds);
|
|
319
|
-
}
|
|
320
|
-
catch (ackError) {
|
|
321
|
-
console.error('Failed to acknowledge messages:', ackError);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
catch (error) {
|
|
326
|
-
console.error('Polling error:', error);
|
|
327
|
-
// Don't throw - continue polling
|
|
328
|
-
}
|
|
329
|
-
};
|
|
330
|
-
// Start polling
|
|
331
|
-
poll(); // Initial poll
|
|
332
|
-
intervalId = setInterval(poll, pollInterval * 1000);
|
|
333
|
-
// Manual trigger function for testing
|
|
334
|
-
const manualTriggerFunction = async () => {
|
|
335
|
-
await poll();
|
|
336
|
-
};
|
|
337
|
-
// Cleanup function
|
|
338
|
-
const closeFunction = async () => {
|
|
339
|
-
if (intervalId) {
|
|
340
|
-
clearInterval(intervalId);
|
|
341
|
-
}
|
|
342
|
-
};
|
|
343
|
-
return {
|
|
344
|
-
closeFunction,
|
|
345
|
-
manualTriggerFunction,
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
192
|
async webhook() {
|
|
349
193
|
const body = this.getBodyData();
|
|
350
194
|
const headers = this.getHeaderData();
|
|
351
195
|
const eventTypes = this.getNodeParameter('eventTypes', []);
|
|
352
196
|
const additionalFields = this.getNodeParameter('additionalFields', {});
|
|
353
|
-
const
|
|
197
|
+
const webhookId = this.getNodeParameter('webhookId');
|
|
354
198
|
// Validate secret token if provided
|
|
355
199
|
if (additionalFields.secretToken) {
|
|
356
|
-
|
|
357
|
-
|
|
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) {
|
|
358
206
|
return {
|
|
359
207
|
webhookResponse: {
|
|
360
208
|
status: 401,
|
|
@@ -363,8 +211,9 @@ class CribopsTrigger {
|
|
|
363
211
|
};
|
|
364
212
|
}
|
|
365
213
|
}
|
|
366
|
-
// Filter by event type
|
|
367
|
-
|
|
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)) {
|
|
368
217
|
return {
|
|
369
218
|
webhookResponse: {
|
|
370
219
|
status: 200,
|
|
@@ -372,27 +221,40 @@ class CribopsTrigger {
|
|
|
372
221
|
},
|
|
373
222
|
};
|
|
374
223
|
}
|
|
375
|
-
//
|
|
224
|
+
// Prepare output data with all relevant fields
|
|
376
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
|
|
377
245
|
...body,
|
|
378
|
-
agent_id: agentId,
|
|
379
|
-
// Ensure conversation_id is available (handle different field names)
|
|
380
|
-
conversation_id: body.conversation_id || body.conversationId || body.thread_id,
|
|
381
|
-
// Ensure response_webhook is available if it exists
|
|
382
|
-
response_webhook: body.response_webhook || body.responseWebhook || body.callback_url,
|
|
383
246
|
};
|
|
384
|
-
//
|
|
385
|
-
|
|
247
|
+
// Include headers if requested
|
|
248
|
+
const workflowData = additionalFields.includeHeaders
|
|
249
|
+
? { json: outputData, headers }
|
|
250
|
+
: { json: outputData };
|
|
386
251
|
// Return the data to the workflow
|
|
387
252
|
return {
|
|
388
|
-
workflowData: [
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
},
|
|
394
|
-
],
|
|
395
|
-
],
|
|
253
|
+
workflowData: [[workflowData]],
|
|
254
|
+
webhookResponse: {
|
|
255
|
+
status: 200,
|
|
256
|
+
body: { received: true },
|
|
257
|
+
},
|
|
396
258
|
};
|
|
397
259
|
}
|
|
398
260
|
}
|
package/dist/package.json
CHANGED
|
@@ -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;
|
|
@@ -57,4 +57,5 @@ export declare class CribopsHttp {
|
|
|
57
57
|
status: string;
|
|
58
58
|
updated_count: number;
|
|
59
59
|
}>;
|
|
60
|
+
request<T = any>(method: IHttpRequestMethods, endpoint: string, data?: IDataObject, options?: Partial<IHttpRequestOptions>): Promise<T>;
|
|
60
61
|
}
|
|
@@ -144,5 +144,9 @@ class CribopsHttp {
|
|
|
144
144
|
throw new Error(`Failed to mark messages as failed for tenant ${tenantId}: ${error}`);
|
|
145
145
|
}
|
|
146
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
|
+
}
|
|
147
151
|
}
|
|
148
152
|
exports.CribopsHttp = CribopsHttp;
|