n8n-nodes-github-copilot 3.32.2 → 3.33.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.
- package/dist/nodes/GitHubCopilotChatAPI/GitHubCopilotChatAPI.node.d.ts +6 -1
- package/dist/nodes/GitHubCopilotChatAPI/GitHubCopilotChatAPI.node.js +13 -7
- package/dist/nodes/GitHubCopilotChatAPI/nodeProperties.js +4 -2
- package/dist/nodes/GitHubCopilotOpenAI/GitHubCopilotOpenAI.node.js +2 -4
- package/dist/package.json +1 -1
- package/dist/shared/models/DynamicModelLoader.d.ts +2 -0
- package/dist/shared/models/DynamicModelLoader.js +60 -0
- package/dist/shared/utils/DynamicModelsManager.d.ts +29 -0
- package/dist/shared/utils/DynamicModelsManager.js +107 -0
- package/dist/shared/utils/GitHubCopilotApiUtils.js +8 -0
- package/package.json +1 -1
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from "n8n-workflow";
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription, ILoadOptionsFunctions, INodePropertyOptions } from "n8n-workflow";
|
|
2
2
|
export declare class GitHubCopilotChatAPI implements INodeType {
|
|
3
3
|
description: INodeTypeDescription;
|
|
4
|
+
methods: {
|
|
5
|
+
loadOptions: {
|
|
6
|
+
getAvailableModels(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
4
9
|
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
10
|
}
|
|
@@ -6,6 +6,7 @@ const nodeProperties_1 = require("./nodeProperties");
|
|
|
6
6
|
const mediaDetection_1 = require("./utils/mediaDetection");
|
|
7
7
|
const GitHubCopilotModels_1 = require("../../shared/models/GitHubCopilotModels");
|
|
8
8
|
const GitHubCopilotEndpoints_1 = require("../../shared/utils/GitHubCopilotEndpoints");
|
|
9
|
+
const DynamicModelLoader_1 = require("../../shared/models/DynamicModelLoader");
|
|
9
10
|
class GitHubCopilotChatAPI {
|
|
10
11
|
constructor() {
|
|
11
12
|
this.description = {
|
|
@@ -29,9 +30,16 @@ class GitHubCopilotChatAPI {
|
|
|
29
30
|
],
|
|
30
31
|
properties: nodeProperties_1.nodeProperties,
|
|
31
32
|
};
|
|
33
|
+
this.methods = {
|
|
34
|
+
loadOptions: {
|
|
35
|
+
async getAvailableModels() {
|
|
36
|
+
return await DynamicModelLoader_1.loadAvailableModels.call(this);
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
32
40
|
}
|
|
33
41
|
async execute() {
|
|
34
|
-
var _a, _b, _c, _d
|
|
42
|
+
var _a, _b, _c, _d;
|
|
35
43
|
const items = this.getInputData();
|
|
36
44
|
const returnData = [];
|
|
37
45
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -114,14 +122,13 @@ class GitHubCopilotChatAPI {
|
|
|
114
122
|
while (attempt <= totalAttempts) {
|
|
115
123
|
try {
|
|
116
124
|
response = await (0, utils_1.makeApiRequest)(this, GitHubCopilotEndpoints_1.GITHUB_COPILOT_API.ENDPOINTS.CHAT_COMPLETIONS, requestBody, hasMedia);
|
|
117
|
-
retriesUsed =
|
|
118
|
-
attempt = ((_b = response._retryMetadata) === null || _b === void 0 ? void 0 : _b.attempts) || 1;
|
|
125
|
+
retriesUsed = attempt - 1;
|
|
119
126
|
break;
|
|
120
127
|
}
|
|
121
128
|
catch (error) {
|
|
122
129
|
const isLastAttempt = attempt >= totalAttempts;
|
|
123
130
|
const errorObj = error;
|
|
124
|
-
const is403Error = errorObj.status === 403 || ((
|
|
131
|
+
const is403Error = errorObj.status === 403 || ((_a = errorObj.message) === null || _a === void 0 ? void 0 : _a.includes("403"));
|
|
125
132
|
if (is403Error && enableRetry && !isLastAttempt) {
|
|
126
133
|
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 30000);
|
|
127
134
|
console.log(`GitHub Copilot API attempt ${attempt}/${totalAttempts} failed with 403, retrying in ${delay}ms...`);
|
|
@@ -137,13 +144,12 @@ class GitHubCopilotChatAPI {
|
|
|
137
144
|
throw new Error(`Failed to get response from GitHub Copilot API after ${totalAttempts} attempts (${retriesUsed} retries)`);
|
|
138
145
|
}
|
|
139
146
|
const result = {
|
|
140
|
-
message: ((
|
|
147
|
+
message: ((_c = (_b = response.choices[0]) === null || _b === void 0 ? void 0 : _b.message) === null || _c === void 0 ? void 0 : _c.content) || "",
|
|
141
148
|
model,
|
|
142
149
|
operation,
|
|
143
150
|
usage: response.usage || null,
|
|
144
|
-
finish_reason: ((
|
|
151
|
+
finish_reason: ((_d = response.choices[0]) === null || _d === void 0 ? void 0 : _d.finish_reason) || "unknown",
|
|
145
152
|
retries: retriesUsed,
|
|
146
|
-
attempts: attempt,
|
|
147
153
|
};
|
|
148
154
|
returnData.push({
|
|
149
155
|
json: result,
|
|
@@ -21,9 +21,11 @@ exports.nodeProperties = [
|
|
|
21
21
|
displayName: "Model",
|
|
22
22
|
name: "model",
|
|
23
23
|
type: "options",
|
|
24
|
-
|
|
24
|
+
typeOptions: {
|
|
25
|
+
loadOptionsMethod: "getAvailableModels",
|
|
26
|
+
},
|
|
25
27
|
default: GitHubCopilotModels_1.DEFAULT_MODELS.GENERAL,
|
|
26
|
-
description: "Select the GitHub Copilot model to use",
|
|
28
|
+
description: "Select the GitHub Copilot model to use (loaded dynamically based on your subscription)",
|
|
27
29
|
},
|
|
28
30
|
{
|
|
29
31
|
displayName: "Message",
|
|
@@ -30,7 +30,7 @@ class GitHubCopilotOpenAI {
|
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
async execute() {
|
|
33
|
-
var _a
|
|
33
|
+
var _a;
|
|
34
34
|
const items = this.getInputData();
|
|
35
35
|
const returnData = [];
|
|
36
36
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -165,9 +165,8 @@ class GitHubCopilotOpenAI {
|
|
|
165
165
|
}
|
|
166
166
|
const response = await (0, utils_1.makeApiRequest)(this, GitHubCopilotEndpoints_1.GITHUB_COPILOT_API.ENDPOINTS.CHAT_COMPLETIONS, requestBody, false);
|
|
167
167
|
const retriesUsed = ((_a = response._retryMetadata) === null || _a === void 0 ? void 0 : _a.retries) || 0;
|
|
168
|
-
const attemptsUsed = ((_b = response._retryMetadata) === null || _b === void 0 ? void 0 : _b.attempts) || 1;
|
|
169
168
|
if (retriesUsed > 0) {
|
|
170
|
-
console.log(`ℹ️ Request completed
|
|
169
|
+
console.log(`ℹ️ Request completed with ${retriesUsed} retry(ies)`);
|
|
171
170
|
}
|
|
172
171
|
const cleanJsonFromMarkdown = (content) => {
|
|
173
172
|
if (!content || typeof content !== 'string') {
|
|
@@ -238,7 +237,6 @@ class GitHubCopilotOpenAI {
|
|
|
238
237
|
},
|
|
239
238
|
...(retriesUsed > 0 && {
|
|
240
239
|
_retry_info: {
|
|
241
|
-
attempts: attemptsUsed,
|
|
242
240
|
retries: retriesUsed,
|
|
243
241
|
}
|
|
244
242
|
}),
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-github-copilot",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.33.0",
|
|
4
4
|
"description": "n8n community node for GitHub Copilot with CLI integration, Chat API access, and AI Chat Model for workflows - 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",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadAvailableModels = loadAvailableModels;
|
|
4
|
+
const DynamicModelsManager_1 = require("../utils/DynamicModelsManager");
|
|
5
|
+
const OAuthTokenManager_1 = require("../utils/OAuthTokenManager");
|
|
6
|
+
async function loadAvailableModels() {
|
|
7
|
+
try {
|
|
8
|
+
const credentials = await this.getCredentials("githubCopilotApi");
|
|
9
|
+
if (!credentials || !credentials.token) {
|
|
10
|
+
console.warn("⚠️ No credentials found for dynamic model loading");
|
|
11
|
+
return getDefaultModels();
|
|
12
|
+
}
|
|
13
|
+
const githubToken = credentials.token;
|
|
14
|
+
let oauthToken;
|
|
15
|
+
try {
|
|
16
|
+
oauthToken = await OAuthTokenManager_1.OAuthTokenManager.getValidOAuthToken(githubToken);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
console.error("❌ Failed to generate OAuth token for model loading:", error);
|
|
20
|
+
return getDefaultModels();
|
|
21
|
+
}
|
|
22
|
+
const models = await DynamicModelsManager_1.DynamicModelsManager.getAvailableModels(oauthToken);
|
|
23
|
+
const options = DynamicModelsManager_1.DynamicModelsManager.modelsToN8nOptions(models);
|
|
24
|
+
console.log(`✅ Loaded ${options.length} models dynamically`);
|
|
25
|
+
return options;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error("❌ Error loading dynamic models:", error);
|
|
29
|
+
return getDefaultModels();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function getDefaultModels() {
|
|
33
|
+
return [
|
|
34
|
+
{
|
|
35
|
+
name: "GPT-4o (Latest)",
|
|
36
|
+
value: "gpt-4o",
|
|
37
|
+
description: "Most capable GPT-4o model - Best for complex tasks",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "GPT-4o Mini",
|
|
41
|
+
value: "gpt-4o-mini",
|
|
42
|
+
description: "Faster and cheaper GPT-4o variant - Good for simple tasks",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "Claude 3.5 Sonnet",
|
|
46
|
+
value: "claude-3.5-sonnet",
|
|
47
|
+
description: "Anthropic's most capable model - Excellent reasoning",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "o1-preview",
|
|
51
|
+
value: "o1-preview",
|
|
52
|
+
description: "OpenAI's reasoning model (preview)",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "o1-mini",
|
|
56
|
+
value: "o1-mini",
|
|
57
|
+
description: "Faster reasoning model variant",
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
interface CopilotModel {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
display_name?: string;
|
|
5
|
+
model_picker_enabled?: boolean;
|
|
6
|
+
capabilities?: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare class DynamicModelsManager {
|
|
9
|
+
private static cache;
|
|
10
|
+
private static readonly CACHE_DURATION_MS;
|
|
11
|
+
private static readonly MIN_REFRESH_INTERVAL_MS;
|
|
12
|
+
private static hashToken;
|
|
13
|
+
private static fetchModelsFromAPI;
|
|
14
|
+
static getAvailableModels(oauthToken: string): Promise<CopilotModel[]>;
|
|
15
|
+
static modelsToN8nOptions(models: CopilotModel[]): Array<{
|
|
16
|
+
name: string;
|
|
17
|
+
value: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
}>;
|
|
20
|
+
static clearCache(oauthToken: string): void;
|
|
21
|
+
static clearAllCache(): void;
|
|
22
|
+
static getCacheInfo(oauthToken: string): {
|
|
23
|
+
cached: boolean;
|
|
24
|
+
modelsCount: number;
|
|
25
|
+
expiresIn: number;
|
|
26
|
+
fetchedAt: string;
|
|
27
|
+
} | null;
|
|
28
|
+
}
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DynamicModelsManager = void 0;
|
|
4
|
+
const GitHubCopilotEndpoints_1 = require("./GitHubCopilotEndpoints");
|
|
5
|
+
class DynamicModelsManager {
|
|
6
|
+
static hashToken(token) {
|
|
7
|
+
let hash = 0;
|
|
8
|
+
for (let i = 0; i < token.length; i++) {
|
|
9
|
+
const char = token.charCodeAt(i);
|
|
10
|
+
hash = (hash << 5) - hash + char;
|
|
11
|
+
hash = hash & hash;
|
|
12
|
+
}
|
|
13
|
+
return `models_${Math.abs(hash).toString(36)}`;
|
|
14
|
+
}
|
|
15
|
+
static async fetchModelsFromAPI(oauthToken) {
|
|
16
|
+
const url = `${GitHubCopilotEndpoints_1.GITHUB_COPILOT_API.BASE_URL}${GitHubCopilotEndpoints_1.GITHUB_COPILOT_API.ENDPOINTS.MODELS}`;
|
|
17
|
+
console.log("🔄 Fetching available models from GitHub Copilot API...");
|
|
18
|
+
const response = await fetch(url, {
|
|
19
|
+
method: "GET",
|
|
20
|
+
headers: {
|
|
21
|
+
Authorization: `Bearer ${oauthToken}`,
|
|
22
|
+
Accept: "application/json",
|
|
23
|
+
"Content-Type": "application/json",
|
|
24
|
+
"User-Agent": "GitHub-Copilot/1.0 (n8n-node)",
|
|
25
|
+
"Editor-Version": "vscode/1.95.0",
|
|
26
|
+
"Editor-Plugin-Version": "copilot/1.0.0",
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
const errorText = await response.text();
|
|
31
|
+
console.error(`❌ Failed to fetch models: ${response.status} ${response.statusText}`);
|
|
32
|
+
console.error(`❌ Error details: ${errorText}`);
|
|
33
|
+
throw new Error(`Failed to fetch models: ${response.status} ${response.statusText}`);
|
|
34
|
+
}
|
|
35
|
+
const data = (await response.json());
|
|
36
|
+
const enabledModels = data.data.filter((model) => model.model_picker_enabled !== false);
|
|
37
|
+
console.log(`✅ Fetched ${enabledModels.length} enabled models (${data.data.length} total)`);
|
|
38
|
+
return enabledModels;
|
|
39
|
+
}
|
|
40
|
+
static async getAvailableModels(oauthToken) {
|
|
41
|
+
const tokenHash = this.hashToken(oauthToken);
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
const cached = this.cache.get(tokenHash);
|
|
44
|
+
if (cached && cached.expiresAt > now) {
|
|
45
|
+
const remainingMinutes = Math.round((cached.expiresAt - now) / 60000);
|
|
46
|
+
console.log(`✅ Using cached models (expires in ${remainingMinutes} minutes)`);
|
|
47
|
+
return cached.models;
|
|
48
|
+
}
|
|
49
|
+
if (cached && now - cached.fetchedAt < this.MIN_REFRESH_INTERVAL_MS) {
|
|
50
|
+
const waitSeconds = Math.round((this.MIN_REFRESH_INTERVAL_MS - (now - cached.fetchedAt)) / 1000);
|
|
51
|
+
console.log(`⏰ Models fetched recently, using cache (min refresh interval: ${waitSeconds}s)`);
|
|
52
|
+
return cached.models;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const models = await this.fetchModelsFromAPI(oauthToken);
|
|
56
|
+
this.cache.set(tokenHash, {
|
|
57
|
+
models,
|
|
58
|
+
fetchedAt: now,
|
|
59
|
+
expiresAt: now + this.CACHE_DURATION_MS,
|
|
60
|
+
tokenHash,
|
|
61
|
+
});
|
|
62
|
+
return models;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
console.error("❌ Failed to fetch models from API:", error);
|
|
66
|
+
if (cached) {
|
|
67
|
+
console.log("⚠️ Using expired cache as fallback");
|
|
68
|
+
return cached.models;
|
|
69
|
+
}
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
static modelsToN8nOptions(models) {
|
|
74
|
+
return models.map((model) => ({
|
|
75
|
+
name: model.display_name || model.name || model.id,
|
|
76
|
+
value: model.id,
|
|
77
|
+
description: model.capabilities ? `Capabilities: ${model.capabilities.join(", ")}` : undefined,
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
static clearCache(oauthToken) {
|
|
81
|
+
const tokenHash = this.hashToken(oauthToken);
|
|
82
|
+
this.cache.delete(tokenHash);
|
|
83
|
+
console.log("🗑️ Cleared models cache");
|
|
84
|
+
}
|
|
85
|
+
static clearAllCache() {
|
|
86
|
+
this.cache.clear();
|
|
87
|
+
console.log("🗑️ Cleared all models cache");
|
|
88
|
+
}
|
|
89
|
+
static getCacheInfo(oauthToken) {
|
|
90
|
+
const tokenHash = this.hashToken(oauthToken);
|
|
91
|
+
const cached = this.cache.get(tokenHash);
|
|
92
|
+
if (!cached) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const now = Date.now();
|
|
96
|
+
return {
|
|
97
|
+
cached: true,
|
|
98
|
+
modelsCount: cached.models.length,
|
|
99
|
+
expiresIn: Math.max(0, cached.expiresAt - now),
|
|
100
|
+
fetchedAt: new Date(cached.fetchedAt).toISOString(),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.DynamicModelsManager = DynamicModelsManager;
|
|
105
|
+
DynamicModelsManager.cache = new Map();
|
|
106
|
+
DynamicModelsManager.CACHE_DURATION_MS = 60 * 60 * 1000;
|
|
107
|
+
DynamicModelsManager.MIN_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
|
@@ -11,6 +11,7 @@ exports.validateTokenLimit = validateTokenLimit;
|
|
|
11
11
|
exports.truncateToTokenLimit = truncateToTokenLimit;
|
|
12
12
|
const GitHubCopilotEndpoints_1 = require("./GitHubCopilotEndpoints");
|
|
13
13
|
const OAuthTokenManager_1 = require("./OAuthTokenManager");
|
|
14
|
+
const DynamicModelsManager_1 = require("./DynamicModelsManager");
|
|
14
15
|
async function makeGitHubCopilotRequest(context, endpoint, body, hasMedia = false, retryConfig) {
|
|
15
16
|
var _a, _b, _c;
|
|
16
17
|
const MAX_RETRIES = (_a = retryConfig === null || retryConfig === void 0 ? void 0 : retryConfig.maxRetries) !== null && _a !== void 0 ? _a : 3;
|
|
@@ -37,6 +38,13 @@ async function makeGitHubCopilotRequest(context, endpoint, body, hasMedia = fals
|
|
|
37
38
|
try {
|
|
38
39
|
token = await OAuthTokenManager_1.OAuthTokenManager.getValidOAuthToken(githubToken);
|
|
39
40
|
console.log(`✅ OAuth token ready (auto-generated from GitHub token)`);
|
|
41
|
+
DynamicModelsManager_1.DynamicModelsManager.getAvailableModels(token)
|
|
42
|
+
.then((models) => {
|
|
43
|
+
console.log(`✅ Models list updated: ${models.length} models available`);
|
|
44
|
+
})
|
|
45
|
+
.catch((error) => {
|
|
46
|
+
console.warn(`⚠️ Failed to update models list: ${error instanceof Error ? error.message : String(error)}`);
|
|
47
|
+
});
|
|
40
48
|
}
|
|
41
49
|
catch (error) {
|
|
42
50
|
throw new Error(`Failed to generate OAuth token: ${error instanceof Error ? error.message : String(error)}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-github-copilot",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.33.0",
|
|
4
4
|
"description": "n8n community node for GitHub Copilot with CLI integration, Chat API access, and AI Chat Model for workflows - 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",
|