n8n-nodes-github-copilot 3.0.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.
@@ -1,61 +1,54 @@
1
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
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
3
  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/2.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}`);
4
+ const utils_1 = require("./utils");
5
+ async function processAudioFileLegacy(context, itemIndex) {
6
+ const audioSource = context.getNodeParameter('audioSource', itemIndex);
7
+ try {
8
+ const audioFile = context.getNodeParameter('audioFile', itemIndex, '');
9
+ const audioUrl = context.getNodeParameter('audioUrl', itemIndex, '');
10
+ const audioProperty = context.getNodeParameter('audioBinaryProperty', itemIndex, '');
11
+ const result = await (0, utils_1.processAudioFile)(context, itemIndex, audioSource, audioFile, audioUrl, audioProperty);
12
+ const tokenValidation = (0, utils_1.validateTokenLimit)(result.estimatedTokens, 100000);
13
+ if (!tokenValidation.valid) {
14
+ return {
15
+ dataUrl: '',
16
+ description: (0, utils_1.createAudioSummary)(result.filename, result.size)
17
+ };
18
+ }
19
+ const optimized = (0, utils_1.optimizeAudioForTokens)(result.data, 80000);
20
+ let description = `Audio file: ${result.filename} (${Math.round(result.size / 1024)}KB)`;
21
+ if (optimized.truncated) {
22
+ description += ` - Compressed from ${optimized.originalTokens} to ${optimized.finalTokens} tokens`;
23
+ }
24
+ return {
25
+ dataUrl: `data:${result.mimeType};base64,${optimized.data}`,
26
+ description
27
+ };
28
+ }
29
+ catch (error) {
30
+ return {
31
+ dataUrl: '',
32
+ description: `Error processing audio: ${error instanceof Error ? error.message : 'Unknown error'}`
33
+ };
42
34
  }
43
- return await response.json();
44
35
  }
45
- function getMimeType(filename) {
46
- const ext = filename.toLowerCase().split('.').pop();
47
- switch (ext) {
48
- case 'jpg':
49
- case 'jpeg':
50
- return 'image/jpeg';
51
- case 'png':
52
- return 'image/png';
53
- case 'webp':
54
- return 'image/webp';
55
- case 'gif':
56
- return 'image/gif';
57
- default:
58
- return 'image/jpeg';
36
+ async function processImageFileLegacy(context, itemIndex) {
37
+ const imageSource = context.getNodeParameter('imageSource', itemIndex);
38
+ try {
39
+ const imageFile = context.getNodeParameter('imageFile', itemIndex, '');
40
+ const imageUrl = context.getNodeParameter('imageUrl', itemIndex, '');
41
+ const imageProperty = context.getNodeParameter('imageBinaryProperty', itemIndex, '');
42
+ const result = await (0, utils_1.processImageFile)(context, itemIndex, imageSource, imageFile, imageUrl, imageProperty);
43
+ const tokenValidation = (0, utils_1.validateTokenLimit)(result.estimatedTokens, 50000);
44
+ if (!tokenValidation.valid) {
45
+ throw new Error(`Image too large: ${result.estimatedTokens} estimated tokens. Please use a smaller image.`);
46
+ }
47
+ const optimizedData = (0, utils_1.compressImageToTokenLimit)(result.data, 40000);
48
+ return `data:${result.mimeType};base64,${optimizedData}`;
49
+ }
50
+ catch (error) {
51
+ throw new Error(`Error processing image: ${error instanceof Error ? error.message : 'Unknown error'}`);
59
52
  }
60
53
  }
61
54
  class GitHubCopilotChatAPI {
@@ -84,24 +77,15 @@ class GitHubCopilotChatAPI {
84
77
  displayName: 'Operation',
85
78
  name: 'operation',
86
79
  type: 'options',
80
+ noDataExpression: true,
87
81
  options: [
88
82
  {
89
83
  name: 'Chat Completion',
90
- value: 'chatCompletion',
91
- description: 'Send a message and get AI response using Copilot models',
92
- },
93
- {
94
- name: 'Audio Transcription',
95
- value: 'audioTranscription',
96
- description: 'Transcribe audio file to text using Copilot',
97
- },
98
- {
99
- name: 'Image Analysis',
100
- value: 'imageAnalysis',
101
- description: 'Analyze image with AI (vision models)',
84
+ value: 'chat',
85
+ description: 'Send messages to GitHub Copilot Chat API',
102
86
  },
103
87
  ],
104
- default: 'chatCompletion',
88
+ default: 'chat',
105
89
  },
106
90
  {
107
91
  displayName: 'Model',
@@ -109,38 +93,38 @@ class GitHubCopilotChatAPI {
109
93
  type: 'options',
110
94
  options: [
111
95
  {
112
- name: 'GPT-5 (Latest)',
96
+ name: 'GPT-5',
113
97
  value: 'gpt-5',
114
- description: 'OpenAI GPT-5 - Latest and most advanced model',
98
+ description: 'OpenAI GPT-5 (Latest and most capable)',
115
99
  },
116
100
  {
117
101
  name: 'GPT-5 Mini',
118
102
  value: 'gpt-5-mini',
119
- description: 'OpenAI GPT-5 Mini - Faster and more efficient',
103
+ description: 'OpenAI GPT-5 Mini (Faster, cost-effective)',
120
104
  },
121
105
  {
122
106
  name: 'Claude Opus 4.1',
123
107
  value: 'claude-opus-4.1',
124
- description: 'Anthropic Claude Opus 4.1 - Advanced reasoning',
108
+ description: 'Anthropic Claude Opus 4.1 (Advanced reasoning)',
125
109
  },
126
110
  {
127
111
  name: 'Gemini 2.5 Pro',
128
112
  value: 'gemini-2.5-pro',
129
- description: 'Google Gemini 2.5 Pro - Multimodal capabilities',
113
+ description: 'Google Gemini 2.5 Pro (Multimodal capabilities)',
130
114
  },
131
115
  {
132
116
  name: 'Grok Code Fast 1',
133
117
  value: 'grok-code-fast-1',
134
- description: 'xAI Grok - Optimized for coding tasks',
118
+ description: 'xAI Grok Code Fast 1 (Optimized for coding)',
135
119
  },
136
120
  {
137
121
  name: 'GPT-4.1 Copilot',
138
122
  value: 'gpt-4.1-copilot',
139
- description: 'GitHub Copilot optimized GPT-4.1 model',
123
+ description: 'OpenAI GPT-4.1 specialized for coding assistance',
140
124
  },
141
125
  ],
142
- default: 'gpt-5',
143
- description: 'Choose the AI model from your Copilot subscription',
126
+ default: 'gpt-5-mini',
127
+ description: 'Select AI model to use',
144
128
  },
145
129
  {
146
130
  displayName: 'Message',
@@ -150,234 +134,302 @@ class GitHubCopilotChatAPI {
150
134
  rows: 4,
151
135
  },
152
136
  default: '',
153
- placeholder: 'Enter your message here...',
154
- description: 'The message to send to the AI',
155
- displayOptions: {
156
- show: {
157
- operation: ['chatCompletion', 'imageAnalysis'],
158
- },
159
- },
137
+ placeholder: 'What can I help you with?',
138
+ description: 'The message to send to the AI model',
160
139
  },
161
140
  {
162
- displayName: 'Audio File',
163
- name: 'audioFile',
141
+ displayName: 'System Message',
142
+ name: 'systemMessage',
164
143
  type: 'string',
165
- default: '',
166
- placeholder: 'Path to audio file or base64 data',
167
- description: 'Audio file to transcribe (supports MP3, WAV, M4A, FLAC, OGG)',
168
- displayOptions: {
169
- show: {
170
- operation: ['audioTranscription'],
171
- },
144
+ typeOptions: {
145
+ rows: 3,
172
146
  },
147
+ default: '',
148
+ placeholder: 'You are a helpful assistant...',
149
+ description: 'System message to set the behavior of the AI model',
173
150
  },
174
151
  {
175
- displayName: 'Audio Language',
176
- name: 'audioLanguage',
152
+ displayName: 'Include Image',
153
+ name: 'includeImage',
154
+ type: 'boolean',
155
+ default: false,
156
+ description: 'Whether to include an image in the message',
157
+ },
158
+ {
159
+ displayName: 'Image Source',
160
+ name: 'imageSource',
177
161
  type: 'options',
178
- options: [
179
- { name: 'Auto-detect', value: 'auto' },
180
- { name: 'Portuguese (Brazil)', value: 'pt' },
181
- { name: 'English', value: 'en' },
182
- { name: 'Spanish', value: 'es' },
183
- { name: 'French', value: 'fr' },
184
- { name: 'German', value: 'de' },
185
- { name: 'Italian', value: 'it' },
186
- { name: 'Japanese', value: 'ja' },
187
- { name: 'Chinese', value: 'zh' },
188
- ],
189
- default: 'auto',
190
- description: 'Language of the audio (helps with accuracy)',
191
162
  displayOptions: {
192
163
  show: {
193
- operation: ['audioTranscription'],
164
+ includeImage: [true],
194
165
  },
195
166
  },
167
+ options: [
168
+ {
169
+ name: 'Manual Input',
170
+ value: 'manual',
171
+ description: 'Provide image as base64 string or file path',
172
+ },
173
+ {
174
+ name: 'URL',
175
+ value: 'url',
176
+ description: 'Download image from URL',
177
+ },
178
+ {
179
+ name: 'Binary Data',
180
+ value: 'binary',
181
+ description: 'Use binary data from previous node',
182
+ },
183
+ ],
184
+ default: 'manual',
185
+ description: 'Source of the image data',
196
186
  },
197
187
  {
198
188
  displayName: 'Image File',
199
189
  name: 'imageFile',
200
190
  type: 'string',
201
- default: '',
202
- placeholder: 'Path to image file or base64 data',
203
- description: 'Image file to analyze (supports JPG, PNG, WebP, GIF)',
204
191
  displayOptions: {
205
192
  show: {
206
- operation: ['imageAnalysis'],
193
+ includeImage: [true],
194
+ imageSource: ['manual'],
207
195
  },
208
196
  },
197
+ default: '',
198
+ placeholder: 'Paste base64 string or file path',
199
+ description: 'Image as base64 string or file path',
209
200
  },
210
201
  {
211
- displayName: 'System Prompt',
212
- name: 'systemPrompt',
202
+ displayName: 'Image URL',
203
+ name: 'imageUrl',
213
204
  type: 'string',
214
- typeOptions: {
215
- rows: 2,
205
+ displayOptions: {
206
+ show: {
207
+ includeImage: [true],
208
+ imageSource: ['url'],
209
+ },
216
210
  },
217
211
  default: '',
218
- placeholder: 'You are a helpful assistant...',
219
- description: 'Optional system prompt to set AI behavior',
212
+ placeholder: 'https://example.com/image.jpg',
213
+ description: 'URL of the image to download and include',
214
+ },
215
+ {
216
+ displayName: 'Image Binary Property',
217
+ name: 'imageBinaryProperty',
218
+ type: 'string',
220
219
  displayOptions: {
221
220
  show: {
222
- operation: ['chatCompletion', 'imageAnalysis'],
221
+ includeImage: [true],
222
+ imageSource: ['binary'],
223
223
  },
224
224
  },
225
+ default: 'data',
226
+ placeholder: 'data',
227
+ description: 'Name of the binary property containing the image',
225
228
  },
226
229
  {
227
- displayName: 'Temperature',
228
- name: 'temperature',
229
- type: 'number',
230
- typeOptions: {
231
- minValue: 0,
232
- maxValue: 2,
233
- numberStepSize: 0.1,
230
+ displayName: 'Include Audio',
231
+ name: 'includeAudio',
232
+ type: 'boolean',
233
+ default: false,
234
+ description: 'Whether to include an audio file in the message',
235
+ },
236
+ {
237
+ displayName: 'Audio Source',
238
+ name: 'audioSource',
239
+ type: 'options',
240
+ displayOptions: {
241
+ show: {
242
+ includeAudio: [true],
243
+ },
234
244
  },
235
- default: 1,
236
- description: 'Controls randomness: 0 = focused, 2 = creative',
245
+ options: [
246
+ {
247
+ name: 'Manual Input',
248
+ value: 'manual',
249
+ description: 'Provide audio as base64 string or file path',
250
+ },
251
+ {
252
+ name: 'URL',
253
+ value: 'url',
254
+ description: 'Download audio from URL',
255
+ },
256
+ {
257
+ name: 'Binary Data',
258
+ value: 'binary',
259
+ description: 'Use binary data from previous node',
260
+ },
261
+ ],
262
+ default: 'manual',
263
+ description: 'Source of the audio data',
264
+ },
265
+ {
266
+ displayName: 'Audio File',
267
+ name: 'audioFile',
268
+ type: 'string',
237
269
  displayOptions: {
238
270
  show: {
239
- operation: ['chatCompletion', 'imageAnalysis'],
271
+ includeAudio: [true],
272
+ audioSource: ['manual'],
240
273
  },
241
274
  },
275
+ default: '',
276
+ placeholder: 'Paste base64 string or file path',
277
+ description: 'Audio as base64 string or file path',
242
278
  },
243
279
  {
244
- displayName: 'Max Tokens',
245
- name: 'maxTokens',
246
- type: 'number',
247
- typeOptions: {
248
- minValue: 1,
249
- maxValue: 8192,
280
+ displayName: 'Audio URL',
281
+ name: 'audioUrl',
282
+ type: 'string',
283
+ displayOptions: {
284
+ show: {
285
+ includeAudio: [true],
286
+ audioSource: ['url'],
287
+ },
250
288
  },
251
- default: 2000,
252
- description: 'Maximum tokens in response',
289
+ default: '',
290
+ placeholder: 'https://example.com/audio.mp3',
291
+ description: 'URL of the audio file to download and include',
292
+ },
293
+ {
294
+ displayName: 'Audio Binary Property',
295
+ name: 'audioBinaryProperty',
296
+ type: 'string',
253
297
  displayOptions: {
254
298
  show: {
255
- operation: ['chatCompletion', 'imageAnalysis'],
299
+ includeAudio: [true],
300
+ audioSource: ['binary'],
256
301
  },
257
302
  },
303
+ default: 'data',
304
+ placeholder: 'data',
305
+ description: 'Name of the binary property containing the audio file',
306
+ },
307
+ {
308
+ displayName: 'Advanced Options',
309
+ name: 'advancedOptions',
310
+ type: 'collection',
311
+ placeholder: 'Add Field',
312
+ default: {},
313
+ options: [
314
+ {
315
+ displayName: 'Temperature',
316
+ name: 'temperature',
317
+ type: 'number',
318
+ typeOptions: {
319
+ minValue: 0,
320
+ maxValue: 2,
321
+ numberPrecision: 2,
322
+ },
323
+ default: 1,
324
+ description: 'Controls randomness of the response. Higher values make output more random.',
325
+ },
326
+ {
327
+ displayName: 'Max Tokens',
328
+ name: 'max_tokens',
329
+ type: 'number',
330
+ typeOptions: {
331
+ minValue: 1,
332
+ maxValue: 128000,
333
+ },
334
+ default: 4096,
335
+ description: 'Maximum number of tokens to generate in the response',
336
+ },
337
+ {
338
+ displayName: 'Top P',
339
+ name: 'top_p',
340
+ type: 'number',
341
+ typeOptions: {
342
+ minValue: 0,
343
+ maxValue: 1,
344
+ numberPrecision: 2,
345
+ },
346
+ default: 1,
347
+ description: 'Alternative to temperature, controls diversity via nucleus sampling',
348
+ },
349
+ ],
258
350
  },
259
351
  ],
260
352
  };
261
353
  }
262
354
  async execute() {
355
+ var _a, _b, _c;
263
356
  const items = this.getInputData();
264
357
  const returnData = [];
265
358
  for (let i = 0; i < items.length; i++) {
266
359
  try {
267
360
  const operation = this.getNodeParameter('operation', i);
268
361
  const model = this.getNodeParameter('model', i);
269
- let result;
270
- if (operation === 'chatCompletion') {
271
- const message = this.getNodeParameter('message', i);
272
- const systemPrompt = this.getNodeParameter('systemPrompt', i);
273
- const temperature = this.getNodeParameter('temperature', i);
274
- const maxTokens = this.getNodeParameter('maxTokens', i);
362
+ if (operation === 'chat') {
363
+ const userMessage = this.getNodeParameter('message', i);
364
+ const systemMessage = this.getNodeParameter('systemMessage', i, '');
365
+ const advancedOptions = this.getNodeParameter('advancedOptions', i, {});
366
+ const includeImage = this.getNodeParameter('includeImage', i, false);
367
+ const includeAudio = this.getNodeParameter('includeAudio', i, false);
275
368
  const messages = [];
276
- if (systemPrompt) {
277
- messages.push({ role: 'system', content: systemPrompt });
278
- }
279
- messages.push({ role: 'user', content: message });
280
- const requestBody = {
281
- model: model,
282
- messages: messages,
283
- temperature: temperature,
284
- max_tokens: maxTokens,
285
- stream: false,
286
- };
287
- const response = await makeApiRequest(this, '/chat/completions', requestBody);
288
- result = {
289
- response: response.choices[0].message.content,
290
- model: model,
291
- usage: response.usage,
292
- finish_reason: response.choices[0].finish_reason,
293
- timestamp: new Date().toISOString(),
294
- };
295
- }
296
- else if (operation === 'audioTranscription') {
297
- const audioFile = this.getNodeParameter('audioFile', i);
298
- const language = this.getNodeParameter('audioLanguage', i);
299
- const transcriptionPrompt = language === 'auto'
300
- ? `Please transcribe this audio file to text. Detect the language automatically and provide the transcription.`
301
- : `Please transcribe this audio file to text. The audio is in ${language} language.`;
302
- const messages = [
303
- {
369
+ if (systemMessage) {
370
+ messages.push({
304
371
  role: 'system',
305
- content: 'You are an expert at audio transcription. When given an audio file, provide an accurate transcription of the spoken content.'
306
- },
307
- {
308
- role: 'user',
309
- content: `${transcriptionPrompt}\n\nAudio file: ${audioFile}`
310
- }
311
- ];
312
- const requestBody = {
313
- model: model,
314
- messages: messages,
315
- temperature: 0.1,
316
- max_tokens: 4000,
317
- stream: false,
318
- };
319
- const response = await makeApiRequest(this, '/chat/completions', requestBody);
320
- result = {
321
- transcription: response.choices[0].message.content,
322
- language: language,
323
- model: model,
324
- usage: response.usage,
325
- timestamp: new Date().toISOString(),
326
- };
327
- }
328
- else if (operation === 'imageAnalysis') {
329
- const message = this.getNodeParameter('message', i);
330
- const imageFile = this.getNodeParameter('imageFile', i);
331
- const systemPrompt = this.getNodeParameter('systemPrompt', i);
332
- const temperature = this.getNodeParameter('temperature', i);
333
- const maxTokens = this.getNodeParameter('maxTokens', i);
334
- let imageBase64;
335
- if (imageFile.startsWith('data:image/')) {
336
- imageBase64 = imageFile;
337
- }
338
- else if (imageFile.match(/^[A-Za-z0-9+/=]+$/)) {
339
- imageBase64 = `data:image/jpeg;base64,${imageFile}`;
372
+ content: systemMessage,
373
+ });
340
374
  }
341
- else {
342
- const fs = await Promise.resolve().then(() => __importStar(require('fs')));
343
- const imageBuffer = fs.readFileSync(imageFile);
344
- const mimeType = getMimeType(imageFile);
345
- imageBase64 = `data:${mimeType};base64,${imageBuffer.toString('base64')}`;
346
- }
347
- const messages = [];
348
- if (systemPrompt) {
349
- messages.push({ role: 'system', content: systemPrompt });
375
+ let userContent = userMessage;
376
+ if (includeImage || includeAudio) {
377
+ const contentArray = [];
378
+ if (userMessage.trim()) {
379
+ contentArray.push({
380
+ type: 'text',
381
+ text: userMessage,
382
+ });
383
+ }
384
+ if (includeImage) {
385
+ const imageDataUrl = await processImageFileLegacy(this, i);
386
+ contentArray.push({
387
+ type: 'image_url',
388
+ image_url: {
389
+ url: imageDataUrl,
390
+ },
391
+ });
392
+ }
393
+ if (includeAudio) {
394
+ const audioResult = await processAudioFileLegacy(this, i);
395
+ if (audioResult.dataUrl) {
396
+ contentArray.push({
397
+ type: 'text',
398
+ text: `[Audio included: ${audioResult.description}]`,
399
+ });
400
+ }
401
+ else {
402
+ contentArray.push({
403
+ type: 'text',
404
+ text: `[Audio processing failed: ${audioResult.description}]`,
405
+ });
406
+ }
407
+ }
408
+ userContent = contentArray;
350
409
  }
351
410
  messages.push({
352
411
  role: 'user',
353
- content: [
354
- { type: 'text', text: message },
355
- { type: 'image_url', image_url: { url: imageBase64 } }
356
- ]
412
+ content: userContent,
357
413
  });
358
414
  const requestBody = {
359
- model: model,
360
- messages: messages,
361
- temperature: temperature,
362
- max_tokens: maxTokens,
415
+ model,
416
+ messages,
363
417
  stream: false,
418
+ ...advancedOptions,
364
419
  };
365
- const response = await makeApiRequest(this, '/chat/completions', requestBody);
366
- result = {
367
- response: response.choices[0].message.content,
368
- model: model,
369
- usage: response.usage,
370
- finish_reason: response.choices[0].finish_reason,
371
- timestamp: new Date().toISOString(),
420
+ const response = await (0, utils_1.makeApiRequest)(this, '/chat/completions', requestBody);
421
+ const result = {
422
+ message: ((_b = (_a = response.choices[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content) || '',
423
+ model,
424
+ operation,
425
+ usage: response.usage || null,
426
+ finish_reason: ((_c = response.choices[0]) === null || _c === void 0 ? void 0 : _c.finish_reason) || 'unknown',
372
427
  };
428
+ returnData.push({
429
+ json: result,
430
+ pairedItem: { item: i },
431
+ });
373
432
  }
374
- else {
375
- throw new Error(`Unknown operation: ${operation}`);
376
- }
377
- returnData.push({
378
- json: result,
379
- pairedItem: { item: i },
380
- });
381
433
  }
382
434
  catch (error) {
383
435
  if (this.continueOnFail()) {