n8n-nodes-github-copilot 3.2.3 → 3.2.5

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.
@@ -4,55 +4,7 @@ exports.GitHubCopilotChatAPI = void 0;
4
4
  const utils_1 = require("./utils");
5
5
  const nodeProperties_1 = require("./nodeProperties");
6
6
  const modelCapabilities_1 = require("./utils/modelCapabilities");
7
- async function processAudioFileLegacy(context, itemIndex) {
8
- const audioSource = context.getNodeParameter('audioSource', itemIndex);
9
- try {
10
- const audioFile = context.getNodeParameter('audioFile', itemIndex, '');
11
- const audioUrl = context.getNodeParameter('audioUrl', itemIndex, '');
12
- const audioProperty = context.getNodeParameter('audioBinaryProperty', itemIndex, '');
13
- const result = await (0, utils_1.processAudioFile)(context, itemIndex, audioSource, audioFile, audioUrl, audioProperty);
14
- const tokenValidation = (0, utils_1.validateTokenLimit)(result.estimatedTokens, 100000);
15
- if (!tokenValidation.valid) {
16
- return {
17
- dataUrl: '',
18
- description: (0, utils_1.createAudioSummary)(result.filename, result.size)
19
- };
20
- }
21
- const optimized = (0, utils_1.optimizeAudioForTokens)(result.data, 80000);
22
- let description = `Audio file: ${result.filename} (${Math.round(result.size / 1024)}KB)`;
23
- if (optimized.truncated) {
24
- description += ` - Compressed from ${optimized.originalTokens} to ${optimized.finalTokens} tokens`;
25
- }
26
- return {
27
- dataUrl: `data:${result.mimeType};base64,${optimized.data}`,
28
- description
29
- };
30
- }
31
- catch (error) {
32
- return {
33
- dataUrl: '',
34
- description: `Error processing audio: ${error instanceof Error ? error.message : 'Unknown error'}`
35
- };
36
- }
37
- }
38
- async function processImageFileLegacy(context, itemIndex) {
39
- const imageSource = context.getNodeParameter('imageSource', itemIndex);
40
- try {
41
- const imageFile = context.getNodeParameter('imageFile', itemIndex, '');
42
- const imageUrl = context.getNodeParameter('imageUrl', itemIndex, '');
43
- const imageProperty = context.getNodeParameter('imageBinaryProperty', itemIndex, '');
44
- const result = await (0, utils_1.processImageFile)(context, itemIndex, imageSource, imageFile, imageUrl, imageProperty);
45
- const tokenValidation = (0, utils_1.validateTokenLimit)(result.estimatedTokens, 50000);
46
- if (!tokenValidation.valid) {
47
- throw new Error(`Image too large: ${result.estimatedTokens} estimated tokens. Please use a smaller image.`);
48
- }
49
- const optimizedData = (0, utils_1.compressImageToTokenLimit)(result.data, 40000);
50
- return `data:${result.mimeType};base64,${optimizedData}`;
51
- }
52
- catch (error) {
53
- throw new Error(`Error processing image: ${error instanceof Error ? error.message : 'Unknown error'}`);
54
- }
55
- }
7
+ const mediaDetection_1 = require("./utils/mediaDetection");
56
8
  class GitHubCopilotChatAPI {
57
9
  constructor() {
58
10
  this.description = {
@@ -89,15 +41,16 @@ class GitHubCopilotChatAPI {
89
41
  const userMessage = this.getNodeParameter('message', i);
90
42
  const systemMessage = this.getNodeParameter('systemMessage', i, '');
91
43
  const advancedOptions = this.getNodeParameter('advancedOptions', i, {});
92
- const includeImage = this.getNodeParameter('includeImage', i, false);
93
- const includeAudio = this.getNodeParameter('includeAudio', i, false);
94
- const validation = (0, modelCapabilities_1.validateModelCapabilities)(model, includeImage, includeAudio);
95
- if (!validation.isValid) {
96
- throw new Error(validation.errorMessage || 'Model validation failed');
97
- }
98
- if (validation.warnings) {
99
- for (const warning of validation.warnings) {
100
- console.warn(`GitHub Copilot API Warning: ${warning}`);
44
+ const includeMedia = this.getNodeParameter('includeMedia', i, false);
45
+ if (includeMedia) {
46
+ const validation = (0, modelCapabilities_1.validateModelCapabilities)(model, true, true);
47
+ if (!validation.isValid) {
48
+ throw new Error(validation.errorMessage || 'Model validation failed');
49
+ }
50
+ if (validation.warnings) {
51
+ for (const warning of validation.warnings) {
52
+ console.warn(`GitHub Copilot API Warning: ${warning}`);
53
+ }
101
54
  }
102
55
  }
103
56
  const messages = [];
@@ -108,7 +61,11 @@ class GitHubCopilotChatAPI {
108
61
  });
109
62
  }
110
63
  let userContent = userMessage;
111
- if (includeImage || includeAudio) {
64
+ if (includeMedia) {
65
+ const mediaSource = this.getNodeParameter('mediaSource', i);
66
+ const mediaFile = this.getNodeParameter('mediaFile', i, '');
67
+ const mediaUrl = this.getNodeParameter('mediaUrl', i, '');
68
+ const mediaBinaryProperty = this.getNodeParameter('mediaBinaryProperty', i, '');
112
69
  const contentArray = [];
113
70
  if (userMessage.trim()) {
114
71
  contentArray.push({
@@ -116,30 +73,35 @@ class GitHubCopilotChatAPI {
116
73
  text: userMessage,
117
74
  });
118
75
  }
119
- if (includeImage) {
120
- const imageDataUrl = await processImageFileLegacy(this, i);
121
- contentArray.push({
122
- type: 'image_url',
123
- image_url: {
124
- url: imageDataUrl,
125
- },
126
- });
127
- }
128
- if (includeAudio) {
129
- const audioResult = await processAudioFileLegacy(this, i);
130
- if (audioResult.dataUrl) {
76
+ try {
77
+ const mediaResult = await (0, mediaDetection_1.processMediaFile)(this, i, mediaSource, mediaFile, mediaUrl, mediaBinaryProperty);
78
+ if (mediaResult.type === 'image' && mediaResult.dataUrl) {
79
+ contentArray.push({
80
+ type: 'image_url',
81
+ image_url: {
82
+ url: mediaResult.dataUrl,
83
+ },
84
+ });
85
+ }
86
+ else if (mediaResult.type === 'audio' && mediaResult.dataUrl) {
131
87
  contentArray.push({
132
88
  type: 'text',
133
- text: `Please analyze this audio file: ${audioResult.description}\n\nAudio data (base64): ${audioResult.dataUrl}`,
89
+ text: `Please analyze this audio file: ${mediaResult.description}\n\nAudio data (base64): ${mediaResult.dataUrl}`,
134
90
  });
135
91
  }
136
92
  else {
137
93
  contentArray.push({
138
94
  type: 'text',
139
- text: `[Audio processing failed: ${audioResult.description}]`,
95
+ text: `[Media processing failed: ${mediaResult.description}]`,
140
96
  });
141
97
  }
142
98
  }
99
+ catch (error) {
100
+ contentArray.push({
101
+ type: 'text',
102
+ text: `[Media processing error: ${error instanceof Error ? error.message : 'Unknown error'}]`,
103
+ });
104
+ }
143
105
  userContent = contentArray;
144
106
  }
145
107
  messages.push({
@@ -152,7 +114,7 @@ class GitHubCopilotChatAPI {
152
114
  stream: false,
153
115
  ...advancedOptions,
154
116
  };
155
- const hasMedia = includeImage || includeAudio;
117
+ const hasMedia = includeMedia;
156
118
  const response = await (0, utils_1.makeApiRequest)(this, '/chat/completions', requestBody, hasMedia);
157
119
  const result = {
158
120
  message: ((_b = (_a = response.choices[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content) || '',
@@ -24,12 +24,12 @@ exports.nodeProperties = [
24
24
  {
25
25
  name: 'GPT-5',
26
26
  value: 'gpt-5',
27
- description: 'OpenAI GPT-5 (Latest and most capable) ✓ Images',
27
+ description: 'OpenAI GPT-5 (Latest and most capable) ✓ Images ✓ Audio',
28
28
  },
29
29
  {
30
30
  name: 'GPT-5 Mini',
31
31
  value: 'gpt-5-mini',
32
- description: 'OpenAI GPT-5 Mini (Faster, cost-effective) ✓ Images',
32
+ description: 'OpenAI GPT-5 Mini (Faster, cost-effective) ✓ Images ✓ Audio',
33
33
  },
34
34
  {
35
35
  name: 'Claude Opus 4.1',
@@ -39,7 +39,7 @@ exports.nodeProperties = [
39
39
  {
40
40
  name: 'Gemini 2.5 Pro',
41
41
  value: 'gemini-2.5-pro',
42
- description: 'Google Gemini 2.5 Pro (Multimodal capabilities) ✓ Images',
42
+ description: 'Google Gemini 2.5 Pro (Multimodal capabilities) ✓ Images ✓ Audio',
43
43
  },
44
44
  {
45
45
  name: 'Grok Code Fast 1',
@@ -49,7 +49,7 @@ exports.nodeProperties = [
49
49
  {
50
50
  name: 'GPT-4.1 Copilot',
51
51
  value: 'gpt-4.1-copilot',
52
- description: 'OpenAI GPT-4.1 specialized for coding assistance ✓ Images',
52
+ description: 'OpenAI GPT-4.1 specialized for coding assistance ✓ Images ✓ Audio',
53
53
  },
54
54
  ],
55
55
  default: 'gpt-5-mini',
@@ -78,31 +78,31 @@ exports.nodeProperties = [
78
78
  description: 'System message to set the behavior of the AI model',
79
79
  },
80
80
  {
81
- displayName: 'Include Image',
82
- name: 'includeImage',
81
+ displayName: 'Include Media',
82
+ name: 'includeMedia',
83
83
  type: 'boolean',
84
84
  default: false,
85
- description: 'Whether to include an image in the message. Supported by GPT and Gemini models via GitHub Copilot API.',
85
+ description: 'Whether to include a media file in the message. Supported: Images (PNG, JPEG, GIF, WebP) and Audio files. Type is auto-detected.',
86
86
  },
87
87
  {
88
- displayName: 'Image Source',
89
- name: 'imageSource',
88
+ displayName: 'Media Source',
89
+ name: 'mediaSource',
90
90
  type: 'options',
91
91
  displayOptions: {
92
92
  show: {
93
- includeImage: [true],
93
+ includeMedia: [true],
94
94
  },
95
95
  },
96
96
  options: [
97
97
  {
98
98
  name: 'Manual Input',
99
99
  value: 'manual',
100
- description: 'Provide image as base64 string or file path',
100
+ description: 'Provide media as base64 string or file path',
101
101
  },
102
102
  {
103
103
  name: 'URL',
104
104
  value: 'url',
105
- description: 'Download image from URL',
105
+ description: 'Download media from URL',
106
106
  },
107
107
  {
108
108
  name: 'Binary Data',
@@ -111,127 +111,49 @@ exports.nodeProperties = [
111
111
  },
112
112
  ],
113
113
  default: 'manual',
114
- description: 'Source of the image data',
114
+ description: 'Source of the media data',
115
115
  },
116
116
  {
117
- displayName: 'Image File',
118
- name: 'imageFile',
117
+ displayName: 'Media File',
118
+ name: 'mediaFile',
119
119
  type: 'string',
120
120
  displayOptions: {
121
121
  show: {
122
- includeImage: [true],
123
- imageSource: ['manual'],
122
+ includeMedia: [true],
123
+ mediaSource: ['manual'],
124
124
  },
125
125
  },
126
126
  default: '',
127
127
  placeholder: 'Paste base64 string or file path',
128
- description: 'Image as base64 string or file path',
128
+ description: 'Media file as base64 string or file path (Images: PNG/JPEG/GIF/WebP, Audio: MP3/WAV/M4A)',
129
129
  },
130
130
  {
131
- displayName: 'Image URL',
132
- name: 'imageUrl',
131
+ displayName: 'Media URL',
132
+ name: 'mediaUrl',
133
133
  type: 'string',
134
134
  displayOptions: {
135
135
  show: {
136
- includeImage: [true],
137
- imageSource: ['url'],
136
+ includeMedia: [true],
137
+ mediaSource: ['url'],
138
138
  },
139
139
  },
140
140
  default: '',
141
- placeholder: 'https://example.com/image.jpg',
142
- description: 'URL of the image to download and include',
141
+ placeholder: 'https://example.com/file.jpg',
142
+ description: 'URL of the media file to download and include (Images: PNG/JPEG/GIF/WebP, Audio: MP3/WAV/M4A)',
143
143
  },
144
144
  {
145
- displayName: 'Image Binary Property',
146
- name: 'imageBinaryProperty',
145
+ displayName: 'Media Binary Property',
146
+ name: 'mediaBinaryProperty',
147
147
  type: 'string',
148
148
  displayOptions: {
149
149
  show: {
150
- includeImage: [true],
151
- imageSource: ['binary'],
150
+ includeMedia: [true],
151
+ mediaSource: ['binary'],
152
152
  },
153
153
  },
154
154
  default: 'data',
155
155
  placeholder: 'data',
156
- description: 'Name of the binary property containing the image',
157
- },
158
- {
159
- displayName: 'Include Audio',
160
- name: 'includeAudio',
161
- type: 'boolean',
162
- default: false,
163
- description: 'Whether to include an audio file in the message. ⚠️ Audio is sent as base64 text to models for analysis (GitHub Copilot API limitation).',
164
- },
165
- {
166
- displayName: 'Audio Source',
167
- name: 'audioSource',
168
- type: 'options',
169
- displayOptions: {
170
- show: {
171
- includeAudio: [true],
172
- },
173
- },
174
- options: [
175
- {
176
- name: 'Manual Input',
177
- value: 'manual',
178
- description: 'Provide audio as base64 string or file path',
179
- },
180
- {
181
- name: 'URL',
182
- value: 'url',
183
- description: 'Download audio from URL',
184
- },
185
- {
186
- name: 'Binary Data',
187
- value: 'binary',
188
- description: 'Use binary data from previous node',
189
- },
190
- ],
191
- default: 'manual',
192
- description: 'Source of the audio data',
193
- },
194
- {
195
- displayName: 'Audio File',
196
- name: 'audioFile',
197
- type: 'string',
198
- displayOptions: {
199
- show: {
200
- includeAudio: [true],
201
- audioSource: ['manual'],
202
- },
203
- },
204
- default: '',
205
- placeholder: 'Paste base64 string or file path',
206
- description: 'Audio as base64 string or file path',
207
- },
208
- {
209
- displayName: 'Audio URL',
210
- name: 'audioUrl',
211
- type: 'string',
212
- displayOptions: {
213
- show: {
214
- includeAudio: [true],
215
- audioSource: ['url'],
216
- },
217
- },
218
- default: '',
219
- placeholder: 'https://example.com/audio.mp3',
220
- description: 'URL of the audio file to download and include',
221
- },
222
- {
223
- displayName: 'Audio Binary Property',
224
- name: 'audioBinaryProperty',
225
- type: 'string',
226
- displayOptions: {
227
- show: {
228
- includeAudio: [true],
229
- audioSource: ['binary'],
230
- },
231
- },
232
- default: 'data',
233
- placeholder: 'data',
234
- description: 'Name of the binary property containing the audio file',
156
+ description: 'Name of the binary property containing the media file',
235
157
  },
236
158
  {
237
159
  displayName: 'Advanced Options',
@@ -7,3 +7,9 @@ export declare function processMediaFile(context: IExecuteFunctions, itemIndex:
7
7
  }>;
8
8
  export declare function isImageMimeType(mimeType: string): boolean;
9
9
  export declare function isAudioMimeType(mimeType: string): boolean;
10
+ export declare function validateImageFormat(mimeType: string): {
11
+ isValid: boolean;
12
+ error?: string;
13
+ };
14
+ export declare function getFileExtensionFromMimeType(mimeType: string): string;
15
+ export declare function suggestImageConversion(mimeType: string): string;
@@ -1,15 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isAudioMimeType = exports.isImageMimeType = exports.processMediaFile = void 0;
3
+ exports.suggestImageConversion = exports.getFileExtensionFromMimeType = exports.validateImageFormat = exports.isAudioMimeType = exports.isImageMimeType = exports.processMediaFile = void 0;
4
4
  const index_1 = require("./index");
5
5
  async function processMediaFile(context, itemIndex, source, mediaFile, mediaUrl, binaryProperty) {
6
6
  try {
7
7
  try {
8
8
  const imageResult = await (0, index_1.processImageFile)(context, itemIndex, source, mediaFile, mediaUrl, binaryProperty);
9
+ const formatValidation = validateImageFormat(imageResult.mimeType);
10
+ if (!formatValidation.isValid) {
11
+ throw new Error(suggestImageConversion(imageResult.mimeType));
12
+ }
9
13
  return {
10
14
  type: 'image',
11
15
  dataUrl: `data:${imageResult.mimeType};base64,${imageResult.data}`,
12
- description: `Image file: ${imageResult.filename} (${Math.round(imageResult.size / 1024)}KB)`,
16
+ description: `Image file: ${imageResult.filename} (${Math.round(imageResult.size / 1024)}KB, ${imageResult.mimeType})`,
13
17
  mimeType: imageResult.mimeType,
14
18
  };
15
19
  }
@@ -24,7 +28,13 @@ async function processMediaFile(context, itemIndex, source, mediaFile, mediaUrl,
24
28
  };
25
29
  }
26
30
  catch (audioError) {
27
- throw new Error(`File is neither a valid image nor audio file. Image error: ${imageError instanceof Error ? imageError.message : 'Unknown'}. Audio error: ${audioError instanceof Error ? audioError.message : 'Unknown'}`);
31
+ const supportedImageFormats = ['PNG', 'JPEG', 'GIF', 'WebP'];
32
+ const supportedAudioFormats = ['MP3', 'WAV', 'M4A', 'OGG'];
33
+ throw new Error(`File is neither a valid image nor audio file.\n` +
34
+ `Supported image formats: ${supportedImageFormats.join(', ')}\n` +
35
+ `Supported audio formats: ${supportedAudioFormats.join(', ')}\n` +
36
+ `Image error: ${imageError instanceof Error ? imageError.message : 'Unknown'}\n` +
37
+ `Audio error: ${audioError instanceof Error ? audioError.message : 'Unknown'}`);
28
38
  }
29
39
  }
30
40
  }
@@ -38,7 +48,14 @@ async function processMediaFile(context, itemIndex, source, mediaFile, mediaUrl,
38
48
  }
39
49
  exports.processMediaFile = processMediaFile;
40
50
  function isImageMimeType(mimeType) {
41
- return mimeType.startsWith('image/') && !mimeType.includes('svg');
51
+ const supportedFormats = [
52
+ 'image/png',
53
+ 'image/jpeg',
54
+ 'image/jpg',
55
+ 'image/gif',
56
+ 'image/webp'
57
+ ];
58
+ return supportedFormats.includes(mimeType.toLowerCase());
42
59
  }
43
60
  exports.isImageMimeType = isImageMimeType;
44
61
  function isAudioMimeType(mimeType) {
@@ -47,3 +64,36 @@ function isAudioMimeType(mimeType) {
47
64
  mimeType === 'video/mp4';
48
65
  }
49
66
  exports.isAudioMimeType = isAudioMimeType;
67
+ function validateImageFormat(mimeType) {
68
+ if (!isImageMimeType(mimeType)) {
69
+ const supportedFormats = ['PNG', 'JPEG', 'GIF', 'WebP'];
70
+ return {
71
+ isValid: false,
72
+ error: `Unsupported image format: ${mimeType}. GitHub Copilot API only supports: ${supportedFormats.join(', ')}`
73
+ };
74
+ }
75
+ return { isValid: true };
76
+ }
77
+ exports.validateImageFormat = validateImageFormat;
78
+ function getFileExtensionFromMimeType(mimeType) {
79
+ const mimeToExt = {
80
+ 'image/png': 'png',
81
+ 'image/jpeg': 'jpg',
82
+ 'image/jpg': 'jpg',
83
+ 'image/gif': 'gif',
84
+ 'image/webp': 'webp',
85
+ 'image/bmp': 'bmp',
86
+ 'image/tiff': 'tiff',
87
+ 'image/svg+xml': 'svg',
88
+ };
89
+ return mimeToExt[mimeType.toLowerCase()] || 'unknown';
90
+ }
91
+ exports.getFileExtensionFromMimeType = getFileExtensionFromMimeType;
92
+ function suggestImageConversion(mimeType) {
93
+ const ext = getFileExtensionFromMimeType(mimeType);
94
+ const supportedFormats = ['PNG', 'JPEG', 'GIF', 'WebP'];
95
+ return `Image format ${ext.toUpperCase()} is not supported by GitHub Copilot API. ` +
96
+ `Please convert your image to one of these formats: ${supportedFormats.join(', ')}. ` +
97
+ `Recommended: Convert to PNG or WebP for best compatibility.`;
98
+ }
99
+ exports.suggestImageConversion = suggestImageConversion;
@@ -4,21 +4,21 @@ exports.getModelInfo = exports.getSupportedModels = exports.validateModelCapabil
4
4
  exports.MODEL_CAPABILITIES = {
5
5
  'gpt-5': {
6
6
  supportsImages: true,
7
- supportsAudio: false,
7
+ supportsAudio: true,
8
8
  maxContextTokens: 200000,
9
- description: 'OpenAI GPT-5 with image support via GitHub Copilot API',
9
+ description: 'OpenAI GPT-5 with image support and audio analysis via GitHub Copilot API',
10
10
  },
11
11
  'gpt-5-mini': {
12
12
  supportsImages: true,
13
- supportsAudio: false,
13
+ supportsAudio: true,
14
14
  maxContextTokens: 128000,
15
- description: 'OpenAI GPT-5 Mini with image support via GitHub Copilot API',
15
+ description: 'OpenAI GPT-5 Mini with image support and audio analysis via GitHub Copilot API',
16
16
  },
17
17
  'gpt-4.1-copilot': {
18
18
  supportsImages: true,
19
- supportsAudio: false,
19
+ supportsAudio: true,
20
20
  maxContextTokens: 128000,
21
- description: 'OpenAI GPT-4.1 with image support via GitHub Copilot API',
21
+ description: 'OpenAI GPT-4.1 with image support and audio analysis via GitHub Copilot API',
22
22
  },
23
23
  'claude-opus-4.1': {
24
24
  supportsImages: false,
@@ -34,15 +34,15 @@ exports.MODEL_CAPABILITIES = {
34
34
  },
35
35
  'gemini-2.5-pro': {
36
36
  supportsImages: true,
37
- supportsAudio: false,
37
+ supportsAudio: true,
38
38
  maxContextTokens: 1000000,
39
- description: 'Google Gemini 2.5 Pro via GitHub Copilot API',
39
+ description: 'Google Gemini 2.5 Pro with multimodal support via GitHub Copilot API',
40
40
  },
41
41
  'gemini-2.0-flash': {
42
42
  supportsImages: true,
43
- supportsAudio: false,
43
+ supportsAudio: true,
44
44
  maxContextTokens: 1000000,
45
- description: 'Google Gemini 2.0 Flash via GitHub Copilot API',
45
+ description: 'Google Gemini 2.0 Flash with multimodal support via GitHub Copilot API',
46
46
  },
47
47
  'grok-code-fast-1': {
48
48
  supportsImages: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-github-copilot",
3
- "version": "3.2.3",
3
+ "version": "3.2.5",
4
4
  "description": "n8n community node for GitHub Copilot with CLI integration and official Chat API access to GPT-5, Claude, Gemini and more using your existing Copilot credits",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/sufficit/n8n-nodes-github-copilot",