n8n-nodes-github-copilot 3.1.0 → 3.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.
@@ -0,0 +1,5 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class GitHubCopilotChatAPI implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,651 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.GitHubCopilotChatAPI = void 0;
27
+ async function makeApiRequest(context, endpoint, body) {
28
+ const credentials = await context.getCredentials('githubApi');
29
+ const options = {
30
+ method: 'POST',
31
+ headers: {
32
+ 'Authorization': `Bearer ${credentials.accessToken}`,
33
+ 'Content-Type': 'application/json',
34
+ 'User-Agent': 'n8n-github-copilot-chat/3.0.0',
35
+ },
36
+ body: JSON.stringify(body),
37
+ };
38
+ const response = await fetch(`https://api.githubcopilot.com${endpoint}`, options);
39
+ if (!response.ok) {
40
+ const errorText = await response.text();
41
+ throw new Error(`GitHub Copilot API error: ${response.status} ${response.statusText}. ${errorText}`);
42
+ }
43
+ return await response.json();
44
+ }
45
+ async function downloadFileFromUrl(url) {
46
+ const response = await fetch(url);
47
+ if (!response.ok) {
48
+ throw new Error(`Failed to download file from URL: ${response.status} ${response.statusText}`);
49
+ }
50
+ return Buffer.from(await response.arrayBuffer());
51
+ }
52
+ async function getFileFromBinary(context, itemIndex, propertyName) {
53
+ const items = context.getInputData();
54
+ const item = items[itemIndex];
55
+ if (!item.binary || !item.binary[propertyName]) {
56
+ throw new Error(`No binary data found in property "${propertyName}"`);
57
+ }
58
+ const binaryData = item.binary[propertyName];
59
+ if (binaryData.data) {
60
+ return Buffer.from(binaryData.data, 'base64');
61
+ }
62
+ else if (binaryData.id) {
63
+ return await context.helpers.getBinaryDataBuffer(itemIndex, propertyName);
64
+ }
65
+ else {
66
+ throw new Error(`Invalid binary data format in property "${propertyName}"`);
67
+ }
68
+ }
69
+ async function processAudioFile(context, itemIndex) {
70
+ const audioSource = context.getNodeParameter('audioSource', itemIndex);
71
+ let audioBuffer;
72
+ let filename = 'audio.mp3';
73
+ switch (audioSource) {
74
+ case 'manual':
75
+ const audioFile = context.getNodeParameter('audioFile', itemIndex);
76
+ if (audioFile.startsWith('data:audio/')) {
77
+ const base64Data = audioFile.split(',')[1];
78
+ audioBuffer = Buffer.from(base64Data, 'base64');
79
+ }
80
+ else if (audioFile.match(/^[A-Za-z0-9+/=]+$/)) {
81
+ audioBuffer = Buffer.from(audioFile, 'base64');
82
+ }
83
+ else {
84
+ const fs = await Promise.resolve().then(() => __importStar(require('fs')));
85
+ audioBuffer = fs.readFileSync(audioFile);
86
+ filename = audioFile.split(/[\\\/]/).pop() || 'audio.mp3';
87
+ }
88
+ break;
89
+ case 'url':
90
+ const audioUrl = context.getNodeParameter('audioUrl', itemIndex);
91
+ audioBuffer = await downloadFileFromUrl(audioUrl);
92
+ filename = audioUrl.split('/').pop() || 'audio.mp3';
93
+ break;
94
+ case 'binary':
95
+ const audioBinaryProperty = context.getNodeParameter('audioBinaryProperty', itemIndex);
96
+ audioBuffer = await getFileFromBinary(context, itemIndex, audioBinaryProperty);
97
+ const items = context.getInputData();
98
+ const item = items[itemIndex];
99
+ if (item.binary && item.binary[audioBinaryProperty] && item.binary[audioBinaryProperty].fileName) {
100
+ filename = item.binary[audioBinaryProperty].fileName || 'audio.mp3';
101
+ }
102
+ break;
103
+ default:
104
+ throw new Error(`Unknown audio source: ${audioSource}`);
105
+ }
106
+ const optimized = await optimizeAudioForAPI(audioBuffer, filename);
107
+ if (optimized.buffer.length === 0) {
108
+ return {
109
+ dataUrl: '',
110
+ description: optimized.description
111
+ };
112
+ }
113
+ const mimeType = getAudioMimeType(filename);
114
+ const base64String = optimized.buffer.toString('base64');
115
+ const estimatedTokens = estimateTokenCount(base64String);
116
+ if (estimatedTokens > 100000) {
117
+ return {
118
+ dataUrl: '',
119
+ description: `Audio file too large for processing (estimated ${estimatedTokens} tokens). ${optimized.description}`
120
+ };
121
+ }
122
+ return {
123
+ dataUrl: `data:${mimeType};base64,${base64String}`,
124
+ description: optimized.description
125
+ };
126
+ }
127
+ async function processImageFile(context, itemIndex) {
128
+ const imageSource = context.getNodeParameter('imageSource', itemIndex);
129
+ let imageBuffer;
130
+ let filename = 'image.jpg';
131
+ switch (imageSource) {
132
+ case 'manual':
133
+ const imageFile = context.getNodeParameter('imageFile', itemIndex);
134
+ if (imageFile.startsWith('data:image/')) {
135
+ return imageFile;
136
+ }
137
+ else if (imageFile.match(/^[A-Za-z0-9+/=]+$/)) {
138
+ imageBuffer = Buffer.from(imageFile, 'base64');
139
+ }
140
+ else {
141
+ const fs = await Promise.resolve().then(() => __importStar(require('fs')));
142
+ imageBuffer = fs.readFileSync(imageFile);
143
+ filename = imageFile.split(/[\\\/]/).pop() || 'image.jpg';
144
+ }
145
+ break;
146
+ case 'url':
147
+ const imageUrl = context.getNodeParameter('imageUrl', itemIndex);
148
+ imageBuffer = await downloadFileFromUrl(imageUrl);
149
+ filename = imageUrl.split('/').pop() || 'image.jpg';
150
+ break;
151
+ case 'binary':
152
+ const imageBinaryProperty = context.getNodeParameter('imageBinaryProperty', itemIndex);
153
+ imageBuffer = await getFileFromBinary(context, itemIndex, imageBinaryProperty);
154
+ const items = context.getInputData();
155
+ const item = items[itemIndex];
156
+ if (item.binary && item.binary[imageBinaryProperty] && item.binary[imageBinaryProperty].fileName) {
157
+ filename = item.binary[imageBinaryProperty].fileName || 'image.jpg';
158
+ }
159
+ break;
160
+ default:
161
+ throw new Error(`Unknown image source: ${imageSource}`);
162
+ }
163
+ const mimeType = getMimeType(filename);
164
+ return `data:${mimeType};base64,${imageBuffer.toString('base64')}`;
165
+ }
166
+ function getMimeType(filename) {
167
+ const ext = filename.toLowerCase().split('.').pop();
168
+ switch (ext) {
169
+ case 'jpg':
170
+ case 'jpeg':
171
+ return 'image/jpeg';
172
+ case 'png':
173
+ return 'image/png';
174
+ case 'webp':
175
+ return 'image/webp';
176
+ case 'gif':
177
+ return 'image/gif';
178
+ default:
179
+ return 'image/jpeg';
180
+ }
181
+ }
182
+ function getAudioMimeType(filename) {
183
+ const ext = filename.toLowerCase().split('.').pop();
184
+ switch (ext) {
185
+ case 'mp3':
186
+ return 'audio/mpeg';
187
+ case 'wav':
188
+ return 'audio/wav';
189
+ case 'm4a':
190
+ return 'audio/mp4';
191
+ case 'flac':
192
+ return 'audio/flac';
193
+ case 'ogg':
194
+ return 'audio/ogg';
195
+ case 'aac':
196
+ return 'audio/aac';
197
+ default:
198
+ return 'audio/mpeg';
199
+ }
200
+ }
201
+ function estimateTokenCount(base64String) {
202
+ return Math.ceil((base64String.length * 0.75) + 100);
203
+ }
204
+ async function optimizeImageForAPI(imageBuffer, filename) {
205
+ const maxSizeBytes = 1024 * 1024;
206
+ if (imageBuffer.length <= maxSizeBytes) {
207
+ return imageBuffer;
208
+ }
209
+ try {
210
+ return imageBuffer;
211
+ }
212
+ catch (error) {
213
+ throw new Error(`Image too large (${Math.round(imageBuffer.length / 1024)}KB). Please use an image smaller than 1MB.`);
214
+ }
215
+ }
216
+ async function optimizeAudioForAPI(audioBuffer, filename) {
217
+ const maxSizeBytes = 2 * 1024 * 1024;
218
+ if (audioBuffer.length <= maxSizeBytes) {
219
+ return {
220
+ buffer: audioBuffer,
221
+ description: `Audio file: ${filename} (${Math.round(audioBuffer.length / 1024)}KB)`
222
+ };
223
+ }
224
+ const sizeInMB = Math.round(audioBuffer.length / (1024 * 1024) * 100) / 100;
225
+ const description = `Large audio file: ${filename} (${sizeInMB}MB). This file is too large to process directly. Please use a smaller audio file (under 2MB) or split it into smaller segments.`;
226
+ return {
227
+ buffer: Buffer.alloc(0),
228
+ description
229
+ };
230
+ }
231
+ class GitHubCopilotChatAPI {
232
+ constructor() {
233
+ this.description = {
234
+ displayName: 'GitHub Copilot Chat API',
235
+ name: 'gitHubCopilotChatAPI',
236
+ icon: 'file:copilot.svg',
237
+ group: ['AI'],
238
+ version: 1,
239
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["model"]}}',
240
+ description: 'Use official GitHub Copilot Chat API with your subscription - access GPT-5, Claude, Gemini and more',
241
+ defaults: {
242
+ name: 'GitHub Copilot Chat API',
243
+ },
244
+ inputs: ["main"],
245
+ outputs: ["main"],
246
+ credentials: [
247
+ {
248
+ name: 'githubApi',
249
+ required: true,
250
+ },
251
+ ],
252
+ properties: [
253
+ {
254
+ displayName: 'Operation',
255
+ name: 'operation',
256
+ type: 'options',
257
+ options: [
258
+ {
259
+ name: 'Chat Completion',
260
+ value: 'chatCompletion',
261
+ description: 'Send a message and get AI response using Copilot models',
262
+ },
263
+ {
264
+ name: 'Audio Transcription',
265
+ value: 'audioTranscription',
266
+ description: 'Transcribe audio file to text using Copilot',
267
+ },
268
+ {
269
+ name: 'Image Analysis',
270
+ value: 'imageAnalysis',
271
+ description: 'Analyze image with AI (vision models)',
272
+ },
273
+ ],
274
+ default: 'chatCompletion',
275
+ },
276
+ {
277
+ displayName: 'Model',
278
+ name: 'model',
279
+ type: 'options',
280
+ options: [
281
+ {
282
+ name: 'GPT-5 (Latest)',
283
+ value: 'gpt-5',
284
+ description: 'OpenAI GPT-5 - Latest and most advanced model',
285
+ },
286
+ {
287
+ name: 'GPT-5 Mini',
288
+ value: 'gpt-5-mini',
289
+ description: 'OpenAI GPT-5 Mini - Faster and more efficient',
290
+ },
291
+ {
292
+ name: 'Claude Opus 4.1',
293
+ value: 'claude-opus-4.1',
294
+ description: 'Anthropic Claude Opus 4.1 - Advanced reasoning',
295
+ },
296
+ {
297
+ name: 'Gemini 2.5 Pro',
298
+ value: 'gemini-2.5-pro',
299
+ description: 'Google Gemini 2.5 Pro - Multimodal capabilities',
300
+ },
301
+ {
302
+ name: 'Grok Code Fast 1',
303
+ value: 'grok-code-fast-1',
304
+ description: 'xAI Grok - Optimized for coding tasks',
305
+ },
306
+ {
307
+ name: 'GPT-4.1 Copilot',
308
+ value: 'gpt-4.1-copilot',
309
+ description: 'GitHub Copilot optimized GPT-4.1 model',
310
+ },
311
+ ],
312
+ default: 'gpt-5',
313
+ description: 'Choose the AI model from your Copilot subscription',
314
+ },
315
+ {
316
+ displayName: 'Message',
317
+ name: 'message',
318
+ type: 'string',
319
+ typeOptions: {
320
+ rows: 4,
321
+ },
322
+ default: '',
323
+ placeholder: 'Enter your message here...',
324
+ description: 'The message to send to the AI',
325
+ displayOptions: {
326
+ show: {
327
+ operation: ['chatCompletion', 'imageAnalysis'],
328
+ },
329
+ },
330
+ },
331
+ {
332
+ displayName: 'Audio Source',
333
+ name: 'audioSource',
334
+ type: 'options',
335
+ options: [
336
+ { name: 'File Path or Base64', value: 'manual' },
337
+ { name: 'Download from URL', value: 'url' },
338
+ { name: 'From Previous Node Binary', value: 'binary' },
339
+ ],
340
+ default: 'manual',
341
+ description: 'Choose how to provide the audio file',
342
+ displayOptions: {
343
+ show: {
344
+ operation: ['audioTranscription'],
345
+ },
346
+ },
347
+ },
348
+ {
349
+ displayName: 'Audio File',
350
+ name: 'audioFile',
351
+ type: 'string',
352
+ default: '',
353
+ placeholder: 'Path to audio file or base64 data',
354
+ description: 'Audio file to transcribe (supports MP3, WAV, M4A, FLAC, OGG)',
355
+ displayOptions: {
356
+ show: {
357
+ operation: ['audioTranscription'],
358
+ audioSource: ['manual'],
359
+ },
360
+ },
361
+ },
362
+ {
363
+ displayName: 'Audio URL',
364
+ name: 'audioUrl',
365
+ type: 'string',
366
+ default: '',
367
+ placeholder: 'https://example.com/audio.mp3',
368
+ description: 'URL to download audio file from',
369
+ displayOptions: {
370
+ show: {
371
+ operation: ['audioTranscription'],
372
+ audioSource: ['url'],
373
+ },
374
+ },
375
+ },
376
+ {
377
+ displayName: 'Binary Property Name',
378
+ name: 'audioBinaryProperty',
379
+ type: 'string',
380
+ default: 'data',
381
+ placeholder: 'data',
382
+ description: 'Name of the binary property containing the audio file',
383
+ displayOptions: {
384
+ show: {
385
+ operation: ['audioTranscription'],
386
+ audioSource: ['binary'],
387
+ },
388
+ },
389
+ },
390
+ {
391
+ displayName: 'Audio Language',
392
+ name: 'audioLanguage',
393
+ type: 'options',
394
+ options: [
395
+ { name: 'Auto-detect', value: 'auto' },
396
+ { name: 'Portuguese (Brazil)', value: 'pt' },
397
+ { name: 'English', value: 'en' },
398
+ { name: 'Spanish', value: 'es' },
399
+ { name: 'French', value: 'fr' },
400
+ { name: 'German', value: 'de' },
401
+ { name: 'Italian', value: 'it' },
402
+ { name: 'Japanese', value: 'ja' },
403
+ { name: 'Chinese', value: 'zh' },
404
+ ],
405
+ default: 'auto',
406
+ description: 'Language of the audio (helps with accuracy)',
407
+ displayOptions: {
408
+ show: {
409
+ operation: ['audioTranscription'],
410
+ },
411
+ },
412
+ },
413
+ {
414
+ displayName: 'Image Source',
415
+ name: 'imageSource',
416
+ type: 'options',
417
+ options: [
418
+ { name: 'File Path or Base64', value: 'manual' },
419
+ { name: 'Download from URL', value: 'url' },
420
+ { name: 'From Previous Node Binary', value: 'binary' },
421
+ ],
422
+ default: 'manual',
423
+ description: 'Choose how to provide the image file',
424
+ displayOptions: {
425
+ show: {
426
+ operation: ['imageAnalysis'],
427
+ },
428
+ },
429
+ },
430
+ {
431
+ displayName: 'Image File',
432
+ name: 'imageFile',
433
+ type: 'string',
434
+ default: '',
435
+ placeholder: 'Path to image file or base64 data',
436
+ description: 'Image file to analyze (supports JPG, PNG, WebP, GIF)',
437
+ displayOptions: {
438
+ show: {
439
+ operation: ['imageAnalysis'],
440
+ imageSource: ['manual'],
441
+ },
442
+ },
443
+ },
444
+ {
445
+ displayName: 'Image URL',
446
+ name: 'imageUrl',
447
+ type: 'string',
448
+ default: '',
449
+ placeholder: 'https://example.com/image.jpg',
450
+ description: 'URL to download image file from',
451
+ displayOptions: {
452
+ show: {
453
+ operation: ['imageAnalysis'],
454
+ imageSource: ['url'],
455
+ },
456
+ },
457
+ },
458
+ {
459
+ displayName: 'Binary Property Name',
460
+ name: 'imageBinaryProperty',
461
+ type: 'string',
462
+ default: 'data',
463
+ placeholder: 'data',
464
+ description: 'Name of the binary property containing the image file',
465
+ displayOptions: {
466
+ show: {
467
+ operation: ['imageAnalysis'],
468
+ imageSource: ['binary'],
469
+ },
470
+ },
471
+ },
472
+ {
473
+ displayName: 'System Prompt',
474
+ name: 'systemPrompt',
475
+ type: 'string',
476
+ typeOptions: {
477
+ rows: 2,
478
+ },
479
+ default: '',
480
+ placeholder: 'You are a helpful assistant...',
481
+ description: 'Optional system prompt to set AI behavior',
482
+ displayOptions: {
483
+ show: {
484
+ operation: ['chatCompletion', 'imageAnalysis'],
485
+ },
486
+ },
487
+ },
488
+ {
489
+ displayName: 'Temperature',
490
+ name: 'temperature',
491
+ type: 'number',
492
+ typeOptions: {
493
+ minValue: 0,
494
+ maxValue: 2,
495
+ numberStepSize: 0.1,
496
+ },
497
+ default: 1,
498
+ description: 'Controls randomness: 0 = focused, 2 = creative',
499
+ displayOptions: {
500
+ show: {
501
+ operation: ['chatCompletion', 'imageAnalysis'],
502
+ },
503
+ },
504
+ },
505
+ {
506
+ displayName: 'Max Tokens',
507
+ name: 'maxTokens',
508
+ type: 'number',
509
+ typeOptions: {
510
+ minValue: 1,
511
+ maxValue: 8192,
512
+ },
513
+ default: 2000,
514
+ description: 'Maximum tokens in response',
515
+ displayOptions: {
516
+ show: {
517
+ operation: ['chatCompletion', 'imageAnalysis'],
518
+ },
519
+ },
520
+ },
521
+ ],
522
+ };
523
+ }
524
+ async execute() {
525
+ const items = this.getInputData();
526
+ const returnData = [];
527
+ for (let i = 0; i < items.length; i++) {
528
+ try {
529
+ const operation = this.getNodeParameter('operation', i);
530
+ const model = this.getNodeParameter('model', i);
531
+ let result;
532
+ if (operation === 'chatCompletion') {
533
+ const message = this.getNodeParameter('message', i);
534
+ const systemPrompt = this.getNodeParameter('systemPrompt', i);
535
+ const temperature = this.getNodeParameter('temperature', i);
536
+ const maxTokens = this.getNodeParameter('maxTokens', i);
537
+ const messages = [];
538
+ if (systemPrompt) {
539
+ messages.push({ role: 'system', content: systemPrompt });
540
+ }
541
+ messages.push({ role: 'user', content: message });
542
+ const requestBody = {
543
+ model: model,
544
+ messages: messages,
545
+ temperature: temperature,
546
+ max_tokens: maxTokens,
547
+ stream: false,
548
+ };
549
+ const response = await makeApiRequest(this, '/chat/completions', requestBody);
550
+ result = {
551
+ response: response.choices[0].message.content,
552
+ model: model,
553
+ usage: response.usage,
554
+ finish_reason: response.choices[0].finish_reason,
555
+ timestamp: new Date().toISOString(),
556
+ };
557
+ }
558
+ else if (operation === 'audioTranscription') {
559
+ const language = this.getNodeParameter('audioLanguage', i);
560
+ const audioDataUrl = await processAudioFile(this, i);
561
+ const transcriptionPrompt = language === 'auto'
562
+ ? `Please transcribe this audio file to text. Detect the language automatically and provide the transcription.`
563
+ : `Please transcribe this audio file to text. The audio is in ${language} language.`;
564
+ const messages = [
565
+ {
566
+ role: 'system',
567
+ content: 'You are an expert at audio transcription. When given an audio file, provide an accurate transcription of the spoken content.'
568
+ },
569
+ {
570
+ role: 'user',
571
+ content: `${transcriptionPrompt}\n\nAudio file: ${audioDataUrl}`
572
+ }
573
+ ];
574
+ const requestBody = {
575
+ model: model,
576
+ messages: messages,
577
+ temperature: 0.1,
578
+ max_tokens: 4000,
579
+ stream: false,
580
+ };
581
+ const response = await makeApiRequest(this, '/chat/completions', requestBody);
582
+ result = {
583
+ transcription: response.choices[0].message.content,
584
+ language: language,
585
+ model: model,
586
+ usage: response.usage,
587
+ timestamp: new Date().toISOString(),
588
+ };
589
+ }
590
+ else if (operation === 'imageAnalysis') {
591
+ const message = this.getNodeParameter('message', i);
592
+ const systemPrompt = this.getNodeParameter('systemPrompt', i);
593
+ const temperature = this.getNodeParameter('temperature', i);
594
+ const maxTokens = this.getNodeParameter('maxTokens', i);
595
+ const imageBase64 = await processImageFile(this, i);
596
+ const messages = [];
597
+ if (systemPrompt) {
598
+ messages.push({ role: 'system', content: systemPrompt });
599
+ }
600
+ messages.push({
601
+ role: 'user',
602
+ content: [
603
+ { type: 'text', text: message },
604
+ { type: 'image_url', image_url: { url: imageBase64 } }
605
+ ]
606
+ });
607
+ const requestBody = {
608
+ model: model,
609
+ messages: messages,
610
+ temperature: temperature,
611
+ max_tokens: maxTokens,
612
+ stream: false,
613
+ };
614
+ const response = await makeApiRequest(this, '/chat/completions', requestBody);
615
+ result = {
616
+ response: response.choices[0].message.content,
617
+ model: model,
618
+ usage: response.usage,
619
+ finish_reason: response.choices[0].finish_reason,
620
+ timestamp: new Date().toISOString(),
621
+ };
622
+ }
623
+ else {
624
+ throw new Error(`Unknown operation: ${operation}`);
625
+ }
626
+ returnData.push({
627
+ json: result,
628
+ pairedItem: { item: i },
629
+ });
630
+ }
631
+ catch (error) {
632
+ if (this.continueOnFail()) {
633
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
634
+ returnData.push({
635
+ json: {
636
+ error: errorMessage,
637
+ operation: this.getNodeParameter('operation', i),
638
+ model: this.getNodeParameter('model', i),
639
+ },
640
+ pairedItem: { item: i },
641
+ });
642
+ }
643
+ else {
644
+ throw error;
645
+ }
646
+ }
647
+ }
648
+ return [returnData];
649
+ }
650
+ }
651
+ exports.GitHubCopilotChatAPI = GitHubCopilotChatAPI;