n8n-nodes-github-copilot 4.2.1 → 4.4.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.
Files changed (37) hide show
  1. package/dist/nodes/GitHubCopilotChatAPI/GitHubCopilotChatAPI.node.d.ts +1 -0
  2. package/dist/nodes/GitHubCopilotChatAPI/GitHubCopilotChatAPI.node.js +47 -15
  3. package/dist/nodes/GitHubCopilotChatAPI/nodeProperties.js +37 -0
  4. package/dist/nodes/GitHubCopilotChatModel/GitHubCopilotChatModel.node.d.ts +1 -0
  5. package/dist/nodes/GitHubCopilotChatModel/GitHubCopilotChatModel.node.js +174 -10
  6. package/dist/nodes/GitHubCopilotOpenAI/GitHubCopilotOpenAI.node.d.ts +1 -0
  7. package/dist/nodes/GitHubCopilotOpenAI/GitHubCopilotOpenAI.node.js +65 -4
  8. package/dist/nodes/GitHubCopilotOpenAI/nodeProperties.js +64 -27
  9. package/dist/nodes/GitHubCopilotPGVector/GitHubCopilotPGVector.node.d.ts +10 -0
  10. package/dist/nodes/GitHubCopilotPGVector/GitHubCopilotPGVector.node.js +421 -0
  11. package/dist/package.json +3 -4
  12. package/dist/shared/models/DynamicModelLoader.d.ts +1 -0
  13. package/dist/shared/models/DynamicModelLoader.js +12 -0
  14. package/dist/shared/models/GitHubCopilotModels.d.ts +14 -8
  15. package/dist/shared/models/GitHubCopilotModels.js +255 -74
  16. package/dist/shared/utils/DynamicModelsManager.d.ts +11 -0
  17. package/dist/shared/utils/DynamicModelsManager.js +50 -0
  18. package/dist/shared/utils/GitHubCopilotApiUtils.d.ts +1 -0
  19. package/dist/shared/utils/GitHubCopilotApiUtils.js +85 -6
  20. package/package.json +3 -4
  21. package/shared/icons/copilot.svg +0 -34
  22. package/shared/index.ts +0 -27
  23. package/shared/models/DynamicModelLoader.ts +0 -124
  24. package/shared/models/GitHubCopilotModels.ts +0 -420
  25. package/shared/models/ModelVersionRequirements.ts +0 -165
  26. package/shared/properties/ModelProperties.ts +0 -52
  27. package/shared/properties/ModelSelectionProperty.ts +0 -68
  28. package/shared/utils/DynamicModelsManager.ts +0 -355
  29. package/shared/utils/EmbeddingsApiUtils.ts +0 -135
  30. package/shared/utils/FileChunkingApiUtils.ts +0 -176
  31. package/shared/utils/FileOptimizationUtils.ts +0 -210
  32. package/shared/utils/GitHubCopilotApiUtils.ts +0 -407
  33. package/shared/utils/GitHubCopilotEndpoints.ts +0 -212
  34. package/shared/utils/GitHubDeviceFlowHandler.ts +0 -276
  35. package/shared/utils/OAuthTokenManager.ts +0 -196
  36. package/shared/utils/provider-injection.ts +0 -277
  37. package/shared/utils/version-detection.ts +0 -145
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.makeGitHubCopilotRequest = makeGitHubCopilotRequest;
4
+ exports.uploadFileToCopilot = uploadFileToCopilot;
4
5
  exports.downloadFileFromUrl = downloadFileFromUrl;
5
6
  exports.getFileFromBinary = getFileFromBinary;
6
7
  exports.getImageMimeType = getImageMimeType;
@@ -87,6 +88,41 @@ async function makeGitHubCopilotRequest(context, endpoint, body, hasMedia = fals
87
88
  headers,
88
89
  body: JSON.stringify(body),
89
90
  };
91
+ async function uploadFile(buffer, filename, mimeType = 'application/octet-stream') {
92
+ const url = `${GitHubCopilotEndpoints_1.GITHUB_COPILOT_API.BASE_URL}/copilot/chat/attachments/files`;
93
+ let form;
94
+ try {
95
+ form = new FormData();
96
+ const blob = new Blob([buffer], { type: mimeType });
97
+ form.append('file', blob, filename);
98
+ }
99
+ catch (err) {
100
+ const FormData = require('form-data');
101
+ form = new FormData();
102
+ form.append('file', buffer, { filename, contentType: mimeType });
103
+ }
104
+ const uploadHeaders = {
105
+ ...GitHubCopilotEndpoints_1.GITHUB_COPILOT_API.HEADERS.WITH_AUTH(token),
106
+ 'X-GitHub-Api-Version': '2025-05-01',
107
+ 'X-Interaction-Type': 'copilot-chat',
108
+ 'OpenAI-Intent': 'conversation-panel',
109
+ 'Copilot-Integration-Id': 'vscode-chat',
110
+ };
111
+ if (typeof form.getHeaders === 'function') {
112
+ Object.assign(uploadHeaders, form.getHeaders());
113
+ }
114
+ const res = await fetch(url, {
115
+ method: 'POST',
116
+ headers: uploadHeaders,
117
+ body: form,
118
+ });
119
+ if (!res.ok) {
120
+ const text = await res.text();
121
+ throw new Error(`File upload failed: ${res.status} ${res.statusText} - ${text}`);
122
+ }
123
+ const json = await res.json();
124
+ return json;
125
+ }
90
126
  const fullUrl = `${GitHubCopilotEndpoints_1.GITHUB_COPILOT_API.BASE_URL}${endpoint}`;
91
127
  let lastError = null;
92
128
  for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
@@ -104,19 +140,15 @@ async function makeGitHubCopilotRequest(context, endpoint, body, hasMedia = fals
104
140
  const errorText = await response.text();
105
141
  if (response.status === 400) {
106
142
  console.log(`🚫 400 Bad Request detected - not retrying`);
107
- const enhancedError = `GitHub Copilot API error: ${response.status} ${response.statusText}. ${errorText}`;
108
- throw new Error(enhancedError);
143
+ throw new Error(`GitHub Copilot API error: ${response.status} ${response.statusText}. ${errorText}`);
109
144
  }
110
145
  const tokenPrefix = token.substring(0, 4);
111
146
  const tokenSuffix = token.substring(token.length - 5);
112
147
  const tokenInfo = `${tokenPrefix}...${tokenSuffix}`;
113
148
  console.error(`❌ GitHub Copilot API Error: ${response.status} ${response.statusText}`);
114
149
  console.error(`❌ Error details: ${errorText}`);
115
- console.error(`❌ Used credential type: ${credentialType}`);
116
- console.error(`❌ Token format used: ${tokenInfo}`);
117
150
  console.error(`❌ Attempt: ${attempt}/${MAX_RETRIES}`);
118
- const enhancedError = `GitHub Copilot API error: ${response.status} ${response.statusText}. ${errorText} [Token used: ${tokenInfo}] [Attempt: ${attempt}/${MAX_RETRIES}]`;
119
- throw new Error(enhancedError);
151
+ throw new Error(`GitHub Copilot API error: ${response.status} ${response.statusText}. ${errorText} [Token: ${tokenInfo}] [Attempt: ${attempt}/${MAX_RETRIES}]`);
120
152
  }
121
153
  if (attempt > 1) {
122
154
  console.log(`✅ GitHub Copilot API succeeded on attempt ${attempt}/${MAX_RETRIES}`);
@@ -144,6 +176,53 @@ async function makeGitHubCopilotRequest(context, endpoint, body, hasMedia = fals
144
176
  }
145
177
  throw lastError || new Error("GitHub Copilot API request failed after all retries");
146
178
  }
179
+ async function uploadFileToCopilot(context, buffer, filename, mimeType = 'application/octet-stream') {
180
+ let credentialType = 'githubCopilotApi';
181
+ try {
182
+ credentialType = context.getNodeParameter('credentialType', 0, 'githubCopilotApi');
183
+ }
184
+ catch { }
185
+ const credentials = await context.getCredentials(credentialType);
186
+ if (!credentials || !credentials.token) {
187
+ throw new Error('GitHub Copilot: No token found in credentials for file upload');
188
+ }
189
+ const githubToken = credentials.token;
190
+ const token = await OAuthTokenManager_1.OAuthTokenManager.getValidOAuthToken(githubToken);
191
+ const url = `${GitHubCopilotEndpoints_1.GITHUB_COPILOT_API.BASE_URL}/copilot/chat/attachments/files`;
192
+ let form;
193
+ try {
194
+ form = new FormData();
195
+ const blob = new Blob([buffer], { type: mimeType });
196
+ form.append('file', blob, filename);
197
+ }
198
+ catch (err) {
199
+ const FormData = require('form-data');
200
+ form = new FormData();
201
+ form.append('file', buffer, { filename, contentType: mimeType });
202
+ }
203
+ const headers = {
204
+ ...GitHubCopilotEndpoints_1.GITHUB_COPILOT_API.HEADERS.WITH_AUTH(token),
205
+ 'X-GitHub-Api-Version': '2025-05-01',
206
+ 'X-Interaction-Type': 'copilot-chat',
207
+ 'OpenAI-Intent': 'conversation-panel',
208
+ 'Copilot-Integration-Id': 'vscode-chat',
209
+ 'Copilot-Vision-Request': 'true',
210
+ 'Copilot-Media-Request': 'true',
211
+ };
212
+ if (typeof form.getHeaders === 'function') {
213
+ Object.assign(headers, form.getHeaders());
214
+ }
215
+ const res = await fetch(url, {
216
+ method: 'POST',
217
+ headers: headers,
218
+ body: form,
219
+ });
220
+ if (!res.ok) {
221
+ const text = await res.text();
222
+ throw new Error(`File upload failed: ${res.status} ${res.statusText} - ${text}`);
223
+ }
224
+ return await res.json();
225
+ }
147
226
  async function downloadFileFromUrl(url) {
148
227
  const response = await fetch(url);
149
228
  if (!response.ok) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "n8n-nodes-github-copilot",
3
- "version": "4.2.1",
4
- "description": "n8n community node for GitHub Copilot with CLI, Chat API, AI Chat Model, and n8n v2 Chat Hub integration - access GPT-5, Claude Sonnet 4.5, Gemini and more using your Copilot subscription",
3
+ "version": "4.4.0",
4
+ "description": "n8n community node for GitHub Copilot with CLI integration, Chat API access, and AI Chat Model for workflows with full tools and function calling support - access GPT-5, Claude, Gemini and more using your Copilot subscription",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/sufficit/n8n-nodes-github-copilot",
7
7
  "author": {
@@ -21,8 +21,7 @@
21
21
  "prepublishOnly": "npm run build"
22
22
  },
23
23
  "files": [
24
- "dist",
25
- "shared"
24
+ "dist"
26
25
  ],
27
26
  "n8n": {
28
27
  "n8nNodesApiVersion": 1,
@@ -1,34 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
2
- <defs>
3
- <linearGradient id="copilotGradient" x1="0%" y1="0%" x2="100%" y2="100%">
4
- <stop offset="0%" style="stop-color:#1f6feb;stop-opacity:1" />
5
- <stop offset="100%" style="stop-color:#0969da;stop-opacity:1" />
6
- </linearGradient>
7
- </defs>
8
-
9
- <!-- GitHub Copilot inspired icon -->
10
- <circle cx="12" cy="12" r="11" fill="url(#copilotGradient)" stroke="#ffffff" stroke-width="1"/>
11
-
12
- <!-- Copilot face -->
13
- <ellipse cx="12" cy="10" rx="8" ry="6" fill="#ffffff" opacity="0.9"/>
14
-
15
- <!-- Eyes -->
16
- <circle cx="9" cy="9" r="1.5" fill="#1f6feb"/>
17
- <circle cx="15" cy="9" r="1.5" fill="#1f6feb"/>
18
-
19
- <!-- Light reflection in eyes -->
20
- <circle cx="9.5" cy="8.5" r="0.5" fill="#ffffff"/>
21
- <circle cx="15.5" cy="8.5" r="0.5" fill="#ffffff"/>
22
-
23
- <!-- Mouth/Interface line -->
24
- <path d="M8 12 L16 12" stroke="#1f6feb" stroke-width="1.5" stroke-linecap="round"/>
25
-
26
- <!-- Code brackets -->
27
- <path d="M6 15 L8 17 L6 19" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
28
- <path d="M18 15 L16 17 L18 19" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
29
-
30
- <!-- AI indicator dots -->
31
- <circle cx="10" cy="17" r="0.5" fill="#ffffff" opacity="0.8"/>
32
- <circle cx="12" cy="17" r="0.5" fill="#ffffff" opacity="0.6"/>
33
- <circle cx="14" cy="17" r="0.5" fill="#ffffff" opacity="0.4"/>
34
- </svg>
package/shared/index.ts DELETED
@@ -1,27 +0,0 @@
1
- /**
2
- * Shared utilities and models for n8n-nodes-copilot
3
- *
4
- * @module shared
5
- */
6
-
7
- // Version detection utilities
8
- export {
9
- detectN8nVersion,
10
- isN8nV2OrHigher,
11
- isChatHubAvailable,
12
- getN8nVersionString,
13
- type N8nVersionInfo,
14
- } from './utils/version-detection';
15
-
16
- // Provider injection utilities
17
- export {
18
- injectGitHubCopilotProvider,
19
- getInjectionStatus,
20
- isProviderInjected,
21
- autoInject,
22
- type ProviderInjectionStatus,
23
- } from './utils/provider-injection';
24
-
25
- // Auto-inject on module load if enabled
26
- import { autoInject } from './utils/provider-injection';
27
- autoInject();
@@ -1,124 +0,0 @@
1
- /**
2
- * Dynamic Model Loader for n8n Nodes
3
- *
4
- * Provides dynamic model loading functionality for n8n nodes.
5
- * Models are loaded based on the authenticated user's available models.
6
- */
7
-
8
- import { ILoadOptionsFunctions, INodePropertyOptions } from "n8n-workflow";
9
- import { DynamicModelsManager } from "../utils/DynamicModelsManager";
10
- import { OAuthTokenManager } from "../utils/OAuthTokenManager";
11
-
12
- /**
13
- * Load available models dynamically based on user authentication
14
- * Use this in node properties with `loadOptions` method
15
- *
16
- * @param forceRefresh - If true, bypasses cache and fetches fresh data from API
17
- */
18
- export async function loadAvailableModels(
19
- this: ILoadOptionsFunctions,
20
- forceRefresh = false
21
- ): Promise<INodePropertyOptions[]> {
22
- return loadModelsWithFilter.call(this, "chat", forceRefresh);
23
- }
24
-
25
- /**
26
- * Load available embedding models dynamically
27
- * Use this in embedding nodes with `loadOptions` method
28
- *
29
- * @param forceRefresh - If true, bypasses cache and fetches fresh data from API
30
- */
31
- export async function loadAvailableEmbeddingModels(
32
- this: ILoadOptionsFunctions,
33
- forceRefresh = false
34
- ): Promise<INodePropertyOptions[]> {
35
- return loadModelsWithFilter.call(this, "embeddings", forceRefresh);
36
- }
37
-
38
- /**
39
- * Internal function to load models with type filter
40
- */
41
- async function loadModelsWithFilter(
42
- this: ILoadOptionsFunctions,
43
- modelType: "chat" | "embeddings",
44
- forceRefresh = false
45
- ): Promise<INodePropertyOptions[]> {
46
- try {
47
- // Get credentials
48
- const credentials = await this.getCredentials("githubCopilotApi");
49
-
50
- if (!credentials || !credentials.token) {
51
- console.warn("⚠️ No credentials found for dynamic model loading");
52
- // Return only manual input option (no fallback)
53
- return [
54
- {
55
- name: "✏️ Enter Custom Model Name",
56
- value: "__manual__",
57
- description: "Type your own model name (no credentials found)",
58
- },
59
- ];
60
- }
61
-
62
- // Get GitHub token
63
- const githubToken = credentials.token as string;
64
-
65
- // Generate OAuth token
66
- let oauthToken: string;
67
- try {
68
- oauthToken = await OAuthTokenManager.getValidOAuthToken(githubToken);
69
- } catch (error) {
70
- console.error("❌ Failed to generate OAuth token for model loading:", error);
71
- // Return only manual input option (no fallback)
72
- return [
73
- {
74
- name: "✏️ Enter Custom Model Name",
75
- value: "__manual__",
76
- description: "Type your own model name (OAuth generation failed)",
77
- },
78
- ];
79
- }
80
-
81
- // Clear cache if force refresh requested
82
- if (forceRefresh) {
83
- DynamicModelsManager.clearCache(oauthToken);
84
- console.log("🔄 Force refreshing models list...");
85
- }
86
-
87
- // Fetch available models
88
- const allModels = await DynamicModelsManager.getAvailableModels(oauthToken);
89
-
90
- // Filter by type
91
- const models = DynamicModelsManager.filterModelsByType(allModels, modelType);
92
-
93
- console.log(`🔍 Filtered ${models.length} ${modelType} models from ${allModels.length} total models`);
94
-
95
- // Convert to n8n options format
96
- const options = DynamicModelsManager.modelsToN8nOptions(models);
97
-
98
- // Add manual input option at the top (for both chat and embeddings)
99
- const optionsWithManualInput: INodePropertyOptions[] = [
100
- {
101
- name: "✏️ Enter Custom Model Name",
102
- value: "__manual__",
103
- description: "Type your own model name (for new/beta models)",
104
- },
105
- ...options,
106
- ];
107
-
108
- console.log(`✅ Loaded ${options.length} ${modelType} models dynamically (+ manual input option)`);
109
- return optionsWithManualInput;
110
- } catch (error) {
111
- console.error("❌ Error loading dynamic models:", error);
112
- // Return empty list with manual input option (no fallback)
113
- // User must enter model name manually or wait for next successful discovery
114
- return [
115
- {
116
- name: "✏️ Enter Custom Model Name",
117
- value: "__manual__",
118
- description: "Type your own model name (discovery failed, using previous cache if available)",
119
- },
120
- ];
121
- }
122
- }
123
-
124
-