github-copilot-oauth 1.0.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/LICENSE +21 -0
- package/NOTICE +27 -0
- package/README.md +194 -0
- package/dist/chunk-CUD4IYWR.js +511 -0
- package/dist/chunk-CUD4IYWR.js.map +1 -0
- package/dist/index.d.ts +122 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/proxy.d.ts +20 -0
- package/dist/proxy.js +177 -0
- package/dist/proxy.js.map +1 -0
- package/dist/types-DsdwFDV1.d.ts +88 -0
- package/package.json +71 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { c as GitHubCopilotOAuthSettings, F as FetchLike, a as GitHubCopilotDeviceFlowOptions, G as GitHubCopilotDeviceFlow, C as CopilotInitiator, d as GitHubCopilotOAuthTokens, T as TokenStore, e as GitHubCopilotProviderSettings } from './types-DsdwFDV1.js';
|
|
2
|
+
export { b as GitHubCopilotOAuthError } from './types-DsdwFDV1.js';
|
|
3
|
+
import { ProviderV3, LanguageModelV3, EmbeddingModelV3, ImageModelV3 } from '@ai-sdk/provider';
|
|
4
|
+
|
|
5
|
+
/** Return true when a chat/completions or responses JSON body contains image input. */
|
|
6
|
+
declare function hasVisionInput(body: string): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Create a fetch implementation that authenticates OpenAI-compatible requests
|
|
9
|
+
* with GitHub Copilot OAuth credentials.
|
|
10
|
+
*/
|
|
11
|
+
declare function createGitHubCopilotOAuthFetch(settings?: GitHubCopilotOAuthSettings): FetchLike;
|
|
12
|
+
/** Create a small Copilot client around `createGitHubCopilotOAuthFetch`. */
|
|
13
|
+
declare function createGitHubCopilotClient(settings?: GitHubCopilotOAuthSettings): Promise<{
|
|
14
|
+
baseURL: string;
|
|
15
|
+
fetch: typeof globalThis.fetch;
|
|
16
|
+
request: (path: string, init?: RequestInit) => Promise<Response>;
|
|
17
|
+
}>;
|
|
18
|
+
/** Route GPT-5 non-mini models to Copilot's Responses endpoint. */
|
|
19
|
+
declare function isCopilotResponsesModel(modelId: string): boolean;
|
|
20
|
+
|
|
21
|
+
declare const DEFAULT_GITHUB_COPILOT_CLIENT_ID = "Ov23li8tweQw6odWQebz";
|
|
22
|
+
/**
|
|
23
|
+
* Start GitHub Copilot's device OAuth flow.
|
|
24
|
+
*
|
|
25
|
+
* The returned flow contains a URL and user code for authorization. Call
|
|
26
|
+
* `flow.complete()` after showing those values to poll for authorization and
|
|
27
|
+
* return the GitHub OAuth token used to mint Copilot API tokens.
|
|
28
|
+
*/
|
|
29
|
+
declare function startGitHubCopilotDeviceFlow(options?: GitHubCopilotDeviceFlowOptions): Promise<GitHubCopilotDeviceFlow>;
|
|
30
|
+
|
|
31
|
+
/** Normalize and validate a GitHub Enterprise hostname. Returns undefined for github.com. */
|
|
32
|
+
declare function normalizeEnterpriseDomain(enterpriseUrl?: string, options?: {
|
|
33
|
+
allowEnterprise?: boolean;
|
|
34
|
+
}): string | undefined;
|
|
35
|
+
/** Resolve the Copilot API base URL for github.com or a validated Enterprise hostname. */
|
|
36
|
+
declare function copilotBase(enterpriseUrl?: string, options?: {
|
|
37
|
+
allowEnterprise?: boolean;
|
|
38
|
+
}): string;
|
|
39
|
+
/** Resolve the GitHub endpoint used to exchange OAuth tokens for Copilot API tokens. */
|
|
40
|
+
declare function copilotTokenExchangeUrl(enterpriseUrl?: string, options?: {
|
|
41
|
+
allowEnterprise?: boolean;
|
|
42
|
+
}): string;
|
|
43
|
+
/** Resolve the GitHub device OAuth endpoints for github.com or Enterprise. */
|
|
44
|
+
declare function githubOAuthUrls(enterpriseUrl?: string, options?: {
|
|
45
|
+
allowEnterprise?: boolean;
|
|
46
|
+
}): {
|
|
47
|
+
domain: string;
|
|
48
|
+
code: string;
|
|
49
|
+
token: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
declare const COPILOT_HEADERS: {
|
|
53
|
+
readonly 'User-Agent': "GitHubCopilotChat/0.35.0";
|
|
54
|
+
readonly 'Editor-Version': "vscode/1.107.0";
|
|
55
|
+
readonly 'Editor-Plugin-Version': "copilot-chat/0.35.0";
|
|
56
|
+
readonly 'Copilot-Integration-Id': "vscode-chat";
|
|
57
|
+
};
|
|
58
|
+
/** Build headers for Copilot chat/completions and responses requests. */
|
|
59
|
+
declare function copilotHeaders(accessToken: string, options?: {
|
|
60
|
+
vision?: boolean;
|
|
61
|
+
initiator?: CopilotInitiator;
|
|
62
|
+
}): {
|
|
63
|
+
'Copilot-Vision-Request'?: string | undefined;
|
|
64
|
+
Authorization: string;
|
|
65
|
+
'Openai-Intent': string;
|
|
66
|
+
'X-Initiator': CopilotInitiator;
|
|
67
|
+
'User-Agent': "GitHubCopilotChat/0.35.0";
|
|
68
|
+
'Editor-Version': "vscode/1.107.0";
|
|
69
|
+
'Editor-Plugin-Version': "copilot-chat/0.35.0";
|
|
70
|
+
'Copilot-Integration-Id': "vscode-chat";
|
|
71
|
+
};
|
|
72
|
+
/** Build headers for Copilot model-list requests. */
|
|
73
|
+
declare function copilotModelHeaders(accessToken: string): {
|
|
74
|
+
Authorization: string;
|
|
75
|
+
'User-Agent': "GitHubCopilotChat/0.35.0";
|
|
76
|
+
'Editor-Version': "vscode/1.107.0";
|
|
77
|
+
'Editor-Plugin-Version': "copilot-chat/0.35.0";
|
|
78
|
+
'Copilot-Integration-Id': "vscode-chat";
|
|
79
|
+
};
|
|
80
|
+
/** Build headers for GitHub's Copilot token exchange endpoint. */
|
|
81
|
+
declare function copilotTokenExchangeHeaders(githubToken: string): {
|
|
82
|
+
'User-Agent': "GitHubCopilotChat/0.35.0";
|
|
83
|
+
'Editor-Version': "vscode/1.107.0";
|
|
84
|
+
'Editor-Plugin-Version': "copilot-chat/0.35.0";
|
|
85
|
+
'Copilot-Integration-Id': "vscode-chat";
|
|
86
|
+
Accept: string;
|
|
87
|
+
Authorization: string;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Create an in-memory token store.
|
|
92
|
+
*
|
|
93
|
+
* This is useful for tests and short-lived scripts. It does not persist tokens
|
|
94
|
+
* across process restarts.
|
|
95
|
+
*/
|
|
96
|
+
declare function createMemoryTokenStore(initial?: GitHubCopilotOAuthTokens): TokenStore;
|
|
97
|
+
|
|
98
|
+
type GitHubCopilotModelId = string;
|
|
99
|
+
/** AI SDK provider for GitHub Copilot OAuth-backed language models. */
|
|
100
|
+
interface GitHubCopilotProvider extends ProviderV3 {
|
|
101
|
+
(modelId: GitHubCopilotModelId): LanguageModelV3;
|
|
102
|
+
languageModel(modelId: GitHubCopilotModelId): LanguageModelV3;
|
|
103
|
+
chat(modelId: GitHubCopilotModelId): LanguageModelV3;
|
|
104
|
+
responses(modelId: GitHubCopilotModelId): LanguageModelV3;
|
|
105
|
+
embeddingModel(modelId: string): EmbeddingModelV3;
|
|
106
|
+
imageModel(modelId: string): ImageModelV3;
|
|
107
|
+
}
|
|
108
|
+
/** Create a Vercel AI SDK provider backed by GitHub Copilot OAuth. */
|
|
109
|
+
declare function createGitHubCopilot(settings?: GitHubCopilotProviderSettings): GitHubCopilotProvider;
|
|
110
|
+
|
|
111
|
+
/** Exchange a GitHub OAuth token for a short-lived GitHub Copilot API token. */
|
|
112
|
+
declare function exchangeGitHubCopilotToken({ fetch, githubToken, enterpriseUrl, allowEnterprise, }: {
|
|
113
|
+
fetch: FetchLike;
|
|
114
|
+
githubToken: string;
|
|
115
|
+
enterpriseUrl?: string;
|
|
116
|
+
allowEnterprise?: boolean;
|
|
117
|
+
}): Promise<{
|
|
118
|
+
token: string;
|
|
119
|
+
expiresAt: number;
|
|
120
|
+
}>;
|
|
121
|
+
|
|
122
|
+
export { COPILOT_HEADERS, CopilotInitiator, DEFAULT_GITHUB_COPILOT_CLIENT_ID, FetchLike, GitHubCopilotDeviceFlow, GitHubCopilotDeviceFlowOptions, type GitHubCopilotModelId, GitHubCopilotOAuthSettings, GitHubCopilotOAuthTokens, type GitHubCopilotProvider, GitHubCopilotProviderSettings, TokenStore, copilotBase, copilotHeaders, copilotModelHeaders, copilotTokenExchangeHeaders, copilotTokenExchangeUrl, createGitHubCopilot, createGitHubCopilotClient, createGitHubCopilotOAuthFetch, createMemoryTokenStore, exchangeGitHubCopilotToken, githubOAuthUrls, hasVisionInput, isCopilotResponsesModel, normalizeEnterpriseDomain, startGitHubCopilotDeviceFlow };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
COPILOT_HEADERS,
|
|
3
|
+
DEFAULT_GITHUB_COPILOT_CLIENT_ID,
|
|
4
|
+
GitHubCopilotOAuthError,
|
|
5
|
+
copilotBase,
|
|
6
|
+
copilotHeaders,
|
|
7
|
+
copilotModelHeaders,
|
|
8
|
+
copilotTokenExchangeHeaders,
|
|
9
|
+
copilotTokenExchangeUrl,
|
|
10
|
+
createGitHubCopilotClient,
|
|
11
|
+
createGitHubCopilotOAuthFetch,
|
|
12
|
+
createMemoryTokenStore,
|
|
13
|
+
exchangeGitHubCopilotToken,
|
|
14
|
+
githubOAuthUrls,
|
|
15
|
+
hasVisionInput,
|
|
16
|
+
isCopilotResponsesModel,
|
|
17
|
+
normalizeEnterpriseDomain,
|
|
18
|
+
startGitHubCopilotDeviceFlow
|
|
19
|
+
} from "./chunk-CUD4IYWR.js";
|
|
20
|
+
|
|
21
|
+
// src/provider.ts
|
|
22
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
23
|
+
import { NoSuchModelError } from "@ai-sdk/provider";
|
|
24
|
+
function createGitHubCopilot(settings = {}) {
|
|
25
|
+
const providerName = settings.name ?? "github-copilot";
|
|
26
|
+
const fetch = createGitHubCopilotOAuthFetch(settings);
|
|
27
|
+
const openai = createOpenAI({
|
|
28
|
+
apiKey: "oauth",
|
|
29
|
+
baseURL: settings.baseURL ?? copilotBase(settings.enterpriseUrl ?? settings.tokens?.enterpriseUrl, settings),
|
|
30
|
+
name: providerName,
|
|
31
|
+
fetch
|
|
32
|
+
});
|
|
33
|
+
const createChatModel = (modelId) => openai.chat(modelId);
|
|
34
|
+
const createResponsesModel = (modelId) => openai.responses(modelId);
|
|
35
|
+
const createLanguageModel = (modelId) => isCopilotResponsesModel(modelId) ? createResponsesModel(modelId) : createChatModel(modelId);
|
|
36
|
+
const provider = Object.assign(
|
|
37
|
+
(modelId) => createLanguageModel(modelId),
|
|
38
|
+
{
|
|
39
|
+
specificationVersion: "v3",
|
|
40
|
+
languageModel: createLanguageModel,
|
|
41
|
+
chat: createChatModel,
|
|
42
|
+
responses: createResponsesModel,
|
|
43
|
+
embeddingModel: (modelId) => {
|
|
44
|
+
throw new NoSuchModelError({ modelId, modelType: "embeddingModel" });
|
|
45
|
+
},
|
|
46
|
+
imageModel: (modelId) => {
|
|
47
|
+
throw new NoSuchModelError({ modelId, modelType: "imageModel" });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
return provider;
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
COPILOT_HEADERS,
|
|
55
|
+
DEFAULT_GITHUB_COPILOT_CLIENT_ID,
|
|
56
|
+
GitHubCopilotOAuthError,
|
|
57
|
+
copilotBase,
|
|
58
|
+
copilotHeaders,
|
|
59
|
+
copilotModelHeaders,
|
|
60
|
+
copilotTokenExchangeHeaders,
|
|
61
|
+
copilotTokenExchangeUrl,
|
|
62
|
+
createGitHubCopilot,
|
|
63
|
+
createGitHubCopilotClient,
|
|
64
|
+
createGitHubCopilotOAuthFetch,
|
|
65
|
+
createMemoryTokenStore,
|
|
66
|
+
exchangeGitHubCopilotToken,
|
|
67
|
+
githubOAuthUrls,
|
|
68
|
+
hasVisionInput,
|
|
69
|
+
isCopilotResponsesModel,
|
|
70
|
+
normalizeEnterpriseDomain,
|
|
71
|
+
startGitHubCopilotDeviceFlow
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provider.ts"],"sourcesContent":["import { createOpenAI } from '@ai-sdk/openai';\r\nimport type { EmbeddingModelV3, ImageModelV3, LanguageModelV3, ProviderV3 } from '@ai-sdk/provider';\r\nimport { NoSuchModelError } from '@ai-sdk/provider';\r\nimport { copilotBase } from './enterprise';\r\nimport { createGitHubCopilotOAuthFetch, isCopilotResponsesModel } from './copilot-fetch';\r\nimport type { GitHubCopilotProviderSettings } from './types';\r\n\r\nexport type GitHubCopilotModelId = string;\r\n\r\n/** AI SDK provider for GitHub Copilot OAuth-backed language models. */\r\nexport interface GitHubCopilotProvider extends ProviderV3 {\r\n (modelId: GitHubCopilotModelId): LanguageModelV3;\r\n languageModel(modelId: GitHubCopilotModelId): LanguageModelV3;\r\n chat(modelId: GitHubCopilotModelId): LanguageModelV3;\r\n responses(modelId: GitHubCopilotModelId): LanguageModelV3;\r\n embeddingModel(modelId: string): EmbeddingModelV3;\r\n imageModel(modelId: string): ImageModelV3;\r\n}\r\n\r\n/** Create a Vercel AI SDK provider backed by GitHub Copilot OAuth. */\r\nexport function createGitHubCopilot(settings: GitHubCopilotProviderSettings = {}): GitHubCopilotProvider {\r\n const providerName = settings.name ?? 'github-copilot';\r\n const fetch = createGitHubCopilotOAuthFetch(settings);\r\n const openai = createOpenAI({\r\n apiKey: 'oauth',\r\n baseURL: settings.baseURL ?? copilotBase(settings.enterpriseUrl ?? settings.tokens?.enterpriseUrl, settings),\r\n name: providerName,\r\n fetch,\r\n });\r\n\r\n const createChatModel = (modelId: GitHubCopilotModelId) => openai.chat(modelId as never);\r\n const createResponsesModel = (modelId: GitHubCopilotModelId) => openai.responses(modelId as never);\r\n // Route GPT-5+ non-mini models to the Responses endpoint for multimodal capability.\r\n // Fall back to Chat Completions for older models.\r\n const createLanguageModel = (modelId: GitHubCopilotModelId) =>\r\n isCopilotResponsesModel(modelId) ? createResponsesModel(modelId) : createChatModel(modelId);\r\n const provider: GitHubCopilotProvider = Object.assign(\r\n (modelId: GitHubCopilotModelId) => createLanguageModel(modelId),\r\n {\r\n specificationVersion: 'v3' as const,\r\n languageModel: createLanguageModel,\r\n chat: createChatModel,\r\n responses: createResponsesModel,\r\n embeddingModel: (modelId: string) => {\r\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' });\r\n },\r\n imageModel: (modelId: string) => {\r\n throw new NoSuchModelError({ modelId, modelType: 'imageModel' });\r\n },\r\n },\r\n );\r\n\r\n return provider;\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,oBAAoB;AAE7B,SAAS,wBAAwB;AAkB1B,SAAS,oBAAoB,WAA0C,CAAC,GAA0B;AACvG,QAAM,eAAe,SAAS,QAAQ;AACtC,QAAM,QAAQ,8BAA8B,QAAQ;AACpD,QAAM,SAAS,aAAa;AAAA,IAC1B,QAAQ;AAAA,IACR,SAAS,SAAS,WAAW,YAAY,SAAS,iBAAiB,SAAS,QAAQ,eAAe,QAAQ;AAAA,IAC3G,MAAM;AAAA,IACN;AAAA,EACF,CAAC;AAED,QAAM,kBAAkB,CAAC,YAAkC,OAAO,KAAK,OAAgB;AACvF,QAAM,uBAAuB,CAAC,YAAkC,OAAO,UAAU,OAAgB;AAGjG,QAAM,sBAAsB,CAAC,YAC3B,wBAAwB,OAAO,IAAI,qBAAqB,OAAO,IAAI,gBAAgB,OAAO;AAC5F,QAAM,WAAkC,OAAO;AAAA,IAC7C,CAAC,YAAkC,oBAAoB,OAAO;AAAA,IAC9D;AAAA,MACE,sBAAsB;AAAA,MACtB,eAAe;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,gBAAgB,CAAC,YAAoB;AACnC,cAAM,IAAI,iBAAiB,EAAE,SAAS,WAAW,iBAAiB,CAAC;AAAA,MACrE;AAAA,MACA,YAAY,CAAC,YAAoB;AAC/B,cAAM,IAAI,iBAAiB,EAAE,SAAS,WAAW,aAAa,CAAC;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
|
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { F as FetchLike } from './types-DsdwFDV1.js';
|
|
2
|
+
|
|
3
|
+
type GitHubCopilotProxyOptions = {
|
|
4
|
+
/** Custom fetch implementation. Defaults to global fetch. */
|
|
5
|
+
fetch?: FetchLike;
|
|
6
|
+
/** OAuth client id. Defaults to the GitHub Copilot Chat client id. */
|
|
7
|
+
clientId?: string;
|
|
8
|
+
/** Allow validated custom GitHub Enterprise hostnames. Defaults to false. */
|
|
9
|
+
allowEnterprise?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/** Create framework-agnostic fetch handlers for browser-safe OAuth/proxy routes. */
|
|
12
|
+
declare function createGitHubCopilotProxy(options?: GitHubCopilotProxyOptions): {
|
|
13
|
+
deviceCode(request: Request): Promise<Response>;
|
|
14
|
+
deviceToken(request: Request): Promise<Response>;
|
|
15
|
+
models(request: Request): Promise<Response>;
|
|
16
|
+
chatCompletions(request: Request): Promise<Response>;
|
|
17
|
+
responses(request: Request): Promise<Response>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { type GitHubCopilotProxyOptions, createGitHubCopilotProxy };
|
package/dist/proxy.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_GITHUB_COPILOT_CLIENT_ID,
|
|
3
|
+
GitHubCopilotOAuthError,
|
|
4
|
+
copilotBase,
|
|
5
|
+
copilotHeaders,
|
|
6
|
+
copilotModelHeaders,
|
|
7
|
+
exchangeGitHubCopilotToken,
|
|
8
|
+
githubOAuthUrls,
|
|
9
|
+
hasVisionInput,
|
|
10
|
+
normalizeEnterpriseDomain
|
|
11
|
+
} from "./chunk-CUD4IYWR.js";
|
|
12
|
+
|
|
13
|
+
// src/proxy.ts
|
|
14
|
+
function pickFetch(customFetch) {
|
|
15
|
+
if (typeof customFetch === "function") return customFetch;
|
|
16
|
+
if (typeof globalThis.fetch === "function") return globalThis.fetch.bind(globalThis);
|
|
17
|
+
throw new GitHubCopilotOAuthError("fetch_required", "A fetch implementation is required for GitHub Copilot proxy handlers.");
|
|
18
|
+
}
|
|
19
|
+
function jsonResponse(body, status) {
|
|
20
|
+
return new Response(JSON.stringify(body), {
|
|
21
|
+
status,
|
|
22
|
+
headers: {
|
|
23
|
+
"Content-Type": "application/json",
|
|
24
|
+
"Cache-Control": "no-store"
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function passthroughJson(response) {
|
|
29
|
+
return new Response(response.body, {
|
|
30
|
+
status: response.status,
|
|
31
|
+
headers: {
|
|
32
|
+
"Content-Type": "application/json",
|
|
33
|
+
"Cache-Control": "no-store"
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function parseBearer(value) {
|
|
38
|
+
const trimmed = value.trim();
|
|
39
|
+
if (!trimmed) return "";
|
|
40
|
+
const match = /^Bearer\s+(.+)$/i.exec(trimmed);
|
|
41
|
+
return match ? match[1].trim() : "";
|
|
42
|
+
}
|
|
43
|
+
function enterpriseFromRequest(request, options) {
|
|
44
|
+
const fromHeader = request.headers.get("x-copilot-enterprise-url") ?? "";
|
|
45
|
+
if (fromHeader.trim()) {
|
|
46
|
+
return normalizeEnterpriseDomain(fromHeader, options);
|
|
47
|
+
}
|
|
48
|
+
const fromQuery = new URL(request.url).searchParams.get("enterpriseUrl") ?? "";
|
|
49
|
+
if (fromQuery.trim()) {
|
|
50
|
+
return normalizeEnterpriseDomain(fromQuery, options);
|
|
51
|
+
}
|
|
52
|
+
return void 0;
|
|
53
|
+
}
|
|
54
|
+
function errorResponse(error) {
|
|
55
|
+
const status = error instanceof GitHubCopilotOAuthError ? error.code === "unsupported" ? 400 : error.code === "auth_failed" ? 401 : 502 : 502;
|
|
56
|
+
const message = error instanceof Error ? error.message : "GitHub Copilot request failed.";
|
|
57
|
+
return jsonResponse({ message }, status);
|
|
58
|
+
}
|
|
59
|
+
async function withCopilotToken(request, options, run) {
|
|
60
|
+
const fetch = pickFetch(options.fetch);
|
|
61
|
+
const refreshToken = parseBearer(request.headers.get("authorization") ?? "");
|
|
62
|
+
if (!refreshToken) {
|
|
63
|
+
return jsonResponse({ message: "Missing GitHub Copilot OAuth token." }, 401);
|
|
64
|
+
}
|
|
65
|
+
const enterpriseUrl = enterpriseFromRequest(request, options);
|
|
66
|
+
let token = refreshToken;
|
|
67
|
+
try {
|
|
68
|
+
const exchanged = await exchangeGitHubCopilotToken({
|
|
69
|
+
fetch,
|
|
70
|
+
githubToken: refreshToken,
|
|
71
|
+
enterpriseUrl,
|
|
72
|
+
allowEnterprise: options.allowEnterprise
|
|
73
|
+
});
|
|
74
|
+
token = exchanged.token;
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
const primary = await run({ token, enterpriseUrl });
|
|
78
|
+
if (token !== refreshToken && (primary.status === 401 || primary.status === 403)) {
|
|
79
|
+
return run({ token: refreshToken, enterpriseUrl });
|
|
80
|
+
}
|
|
81
|
+
return primary;
|
|
82
|
+
}
|
|
83
|
+
function createGitHubCopilotProxy(options = {}) {
|
|
84
|
+
const fetch = pickFetch(options.fetch);
|
|
85
|
+
const clientId = options.clientId ?? DEFAULT_GITHUB_COPILOT_CLIENT_ID;
|
|
86
|
+
return {
|
|
87
|
+
async deviceCode(request) {
|
|
88
|
+
try {
|
|
89
|
+
const enterpriseUrl = enterpriseFromRequest(request, options);
|
|
90
|
+
const urls = githubOAuthUrls(enterpriseUrl, options);
|
|
91
|
+
const response = await fetch(urls.code, {
|
|
92
|
+
method: "POST",
|
|
93
|
+
headers: {
|
|
94
|
+
Accept: "application/json",
|
|
95
|
+
"Content-Type": "application/json"
|
|
96
|
+
},
|
|
97
|
+
body: JSON.stringify({
|
|
98
|
+
client_id: clientId,
|
|
99
|
+
scope: "read:user"
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
return passthroughJson(response);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
return errorResponse(error);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
async deviceToken(request) {
|
|
108
|
+
try {
|
|
109
|
+
const payload = await request.json().catch(() => ({}));
|
|
110
|
+
const enterpriseUrl = enterpriseFromRequest(request, options);
|
|
111
|
+
const urls = githubOAuthUrls(enterpriseUrl, options);
|
|
112
|
+
const response = await fetch(urls.token, {
|
|
113
|
+
method: "POST",
|
|
114
|
+
headers: {
|
|
115
|
+
Accept: "application/json",
|
|
116
|
+
"Content-Type": "application/json"
|
|
117
|
+
},
|
|
118
|
+
body: JSON.stringify({
|
|
119
|
+
client_id: clientId,
|
|
120
|
+
device_code: payload.device_code ?? "",
|
|
121
|
+
grant_type: payload.grant_type ?? "urn:ietf:params:oauth:grant-type:device_code"
|
|
122
|
+
})
|
|
123
|
+
});
|
|
124
|
+
return passthroughJson(response);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
return errorResponse(error);
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
async models(request) {
|
|
130
|
+
try {
|
|
131
|
+
const upstream = await withCopilotToken(
|
|
132
|
+
request,
|
|
133
|
+
options,
|
|
134
|
+
({ token, enterpriseUrl }) => fetch(`${copilotBase(enterpriseUrl, options)}/models`, {
|
|
135
|
+
headers: copilotModelHeaders(token)
|
|
136
|
+
})
|
|
137
|
+
);
|
|
138
|
+
return passthroughJson(upstream);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
return errorResponse(error);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
async chatCompletions(request) {
|
|
144
|
+
return proxyPost(request, "chat/completions");
|
|
145
|
+
},
|
|
146
|
+
async responses(request) {
|
|
147
|
+
return proxyPost(request, "responses");
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
async function proxyPost(request, path) {
|
|
151
|
+
const body = await request.text();
|
|
152
|
+
try {
|
|
153
|
+
const upstream = await withCopilotToken(
|
|
154
|
+
request,
|
|
155
|
+
options,
|
|
156
|
+
({ token, enterpriseUrl }) => fetch(`${copilotBase(enterpriseUrl, options)}/${path}`, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: {
|
|
159
|
+
"Content-Type": "application/json",
|
|
160
|
+
...copilotHeaders(token, {
|
|
161
|
+
vision: hasVisionInput(body),
|
|
162
|
+
initiator: "user"
|
|
163
|
+
})
|
|
164
|
+
},
|
|
165
|
+
body
|
|
166
|
+
})
|
|
167
|
+
);
|
|
168
|
+
return passthroughJson(upstream);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
return errorResponse(error);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
export {
|
|
175
|
+
createGitHubCopilotProxy
|
|
176
|
+
};
|
|
177
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/proxy.ts"],"sourcesContent":["import { githubOAuthUrls, normalizeEnterpriseDomain, copilotBase } from './enterprise';\r\nimport { copilotHeaders, copilotModelHeaders } from './headers';\r\nimport { exchangeGitHubCopilotToken } from './token-exchange';\r\nimport { DEFAULT_GITHUB_COPILOT_CLIENT_ID } from './device-flow';\r\nimport { GitHubCopilotOAuthError, type FetchLike } from './types';\r\nimport { hasVisionInput } from './copilot-fetch';\r\n\r\nexport type GitHubCopilotProxyOptions = {\r\n /** Custom fetch implementation. Defaults to global fetch. */\r\n fetch?: FetchLike;\r\n /** OAuth client id. Defaults to the GitHub Copilot Chat client id. */\r\n clientId?: string;\r\n /** Allow validated custom GitHub Enterprise hostnames. Defaults to false. */\r\n allowEnterprise?: boolean;\r\n};\r\n\r\nfunction pickFetch(customFetch?: FetchLike): FetchLike {\r\n if (typeof customFetch === 'function') return customFetch;\r\n if (typeof globalThis.fetch === 'function') return globalThis.fetch.bind(globalThis);\r\n throw new GitHubCopilotOAuthError('fetch_required', 'A fetch implementation is required for GitHub Copilot proxy handlers.');\r\n}\r\n\r\nfunction jsonResponse(body: unknown, status: number): Response {\r\n return new Response(JSON.stringify(body), {\r\n status,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Cache-Control': 'no-store',\r\n },\r\n });\r\n}\r\n\r\nfunction passthroughJson(response: Response): Response {\r\n return new Response(response.body, {\r\n status: response.status,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Cache-Control': 'no-store',\r\n },\r\n });\r\n}\r\n\r\n// Extracts token from Authorization header. Rejects non-Bearer schemes.\r\nfunction parseBearer(value: string): string {\r\n const trimmed = value.trim();\r\n if (!trimmed) return '';\r\n const match = /^Bearer\\s+(.+)$/i.exec(trimmed);\r\n return match ? match[1].trim() : '';\r\n}\r\n\r\n// Extracts enterprise URL from request: header > query param > undefined.\r\nfunction enterpriseFromRequest(request: Request, options: GitHubCopilotProxyOptions): string | undefined {\r\n const fromHeader = request.headers.get('x-copilot-enterprise-url') ?? '';\r\n if (fromHeader.trim()) {\r\n return normalizeEnterpriseDomain(fromHeader, options);\r\n }\r\n\r\n const fromQuery = new URL(request.url).searchParams.get('enterpriseUrl') ?? '';\r\n if (fromQuery.trim()) {\r\n return normalizeEnterpriseDomain(fromQuery, options);\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\nfunction errorResponse(error: unknown): Response {\r\n const status = error instanceof GitHubCopilotOAuthError\r\n ? error.code === 'unsupported'\r\n ? 400\r\n : error.code === 'auth_failed'\r\n ? 401\r\n : 502\r\n : 502;\r\n const message = error instanceof Error ? error.message : 'GitHub Copilot request failed.';\r\n return jsonResponse({ message }, status);\r\n}\r\n\r\n// Exchanges GitHub token for Copilot token, then calls `run`. Falls back to the\r\n// original GitHub token on 401/403 if exchange succeeded (allows Copilot API retry).\r\nasync function withCopilotToken(\r\n request: Request,\r\n options: GitHubCopilotProxyOptions,\r\n run: (input: { token: string; enterpriseUrl?: string }) => Promise<Response>,\r\n): Promise<Response> {\r\n const fetch = pickFetch(options.fetch);\r\n const refreshToken = parseBearer(request.headers.get('authorization') ?? '');\r\n if (!refreshToken) {\r\n return jsonResponse({ message: 'Missing GitHub Copilot OAuth token.' }, 401);\r\n }\r\n\r\n const enterpriseUrl = enterpriseFromRequest(request, options);\r\n let token = refreshToken;\r\n\r\n try {\r\n const exchanged = await exchangeGitHubCopilotToken({\r\n fetch,\r\n githubToken: refreshToken,\r\n enterpriseUrl,\r\n allowEnterprise: options.allowEnterprise,\r\n });\r\n token = exchanged.token;\r\n } catch {\r\n }\r\n\r\n const primary = await run({ token, enterpriseUrl });\r\n if (token !== refreshToken && (primary.status === 401 || primary.status === 403)) {\r\n return run({ token: refreshToken, enterpriseUrl });\r\n }\r\n\r\n return primary;\r\n}\r\n\r\n/** Create framework-agnostic fetch handlers for browser-safe OAuth/proxy routes. */\r\nexport function createGitHubCopilotProxy(options: GitHubCopilotProxyOptions = {}) {\r\n const fetch = pickFetch(options.fetch);\r\n const clientId = options.clientId ?? DEFAULT_GITHUB_COPILOT_CLIENT_ID;\r\n\r\n return {\r\n async deviceCode(request: Request): Promise<Response> {\r\n try {\r\n const enterpriseUrl = enterpriseFromRequest(request, options);\r\n const urls = githubOAuthUrls(enterpriseUrl, options);\r\n const response = await fetch(urls.code, {\r\n method: 'POST',\r\n headers: {\r\n Accept: 'application/json',\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify({\r\n client_id: clientId,\r\n scope: 'read:user',\r\n }),\r\n });\r\n return passthroughJson(response);\r\n } catch (error) {\r\n return errorResponse(error);\r\n }\r\n },\r\n\r\n async deviceToken(request: Request): Promise<Response> {\r\n try {\r\n const payload = (await request.json().catch(() => ({}))) as { device_code?: string; grant_type?: string };\r\n const enterpriseUrl = enterpriseFromRequest(request, options);\r\n const urls = githubOAuthUrls(enterpriseUrl, options);\r\n const response = await fetch(urls.token, {\r\n method: 'POST',\r\n headers: {\r\n Accept: 'application/json',\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify({\r\n client_id: clientId,\r\n device_code: payload.device_code ?? '',\r\n grant_type: payload.grant_type ?? 'urn:ietf:params:oauth:grant-type:device_code',\r\n }),\r\n });\r\n return passthroughJson(response);\r\n } catch (error) {\r\n return errorResponse(error);\r\n }\r\n },\r\n\r\n async models(request: Request): Promise<Response> {\r\n try {\r\n const upstream = await withCopilotToken(request, options, ({ token, enterpriseUrl }) =>\r\n fetch(`${copilotBase(enterpriseUrl, options)}/models`, {\r\n headers: copilotModelHeaders(token),\r\n }),\r\n );\r\n return passthroughJson(upstream);\r\n } catch (error) {\r\n return errorResponse(error);\r\n }\r\n },\r\n\r\n async chatCompletions(request: Request): Promise<Response> {\r\n return proxyPost(request, 'chat/completions');\r\n },\r\n\r\n async responses(request: Request): Promise<Response> {\r\n return proxyPost(request, 'responses');\r\n },\r\n };\r\n\r\n async function proxyPost(request: Request, path: 'chat/completions' | 'responses'): Promise<Response> {\r\n const body = await request.text();\r\n try {\r\n const upstream = await withCopilotToken(request, options, ({ token, enterpriseUrl }) =>\r\n fetch(`${copilotBase(enterpriseUrl, options)}/${path}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...copilotHeaders(token, {\r\n vision: hasVisionInput(body),\r\n initiator: 'user',\r\n }),\r\n },\r\n body,\r\n }),\r\n );\r\n return passthroughJson(upstream);\r\n } catch (error) {\r\n return errorResponse(error);\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
type FetchLike = typeof globalThis.fetch;
|
|
2
|
+
/** GitHub OAuth credentials plus optional short-lived Copilot API token. Treat these as account credentials. */
|
|
3
|
+
type GitHubCopilotOAuthTokens = {
|
|
4
|
+
/** GitHub OAuth access token returned by the device flow. Used to mint Copilot API tokens. */
|
|
5
|
+
githubToken: string;
|
|
6
|
+
/** Short-lived token returned by GitHub's Copilot token exchange endpoint. */
|
|
7
|
+
copilotToken?: string;
|
|
8
|
+
/** Copilot API token expiry as epoch milliseconds. */
|
|
9
|
+
copilotTokenExpiresAt?: number;
|
|
10
|
+
/** Optional validated GitHub Enterprise hostname. Omitted for github.com. */
|
|
11
|
+
enterpriseUrl?: string;
|
|
12
|
+
};
|
|
13
|
+
/** Async storage interface for loading and persisting GitHub Copilot OAuth credentials. */
|
|
14
|
+
type TokenStore = {
|
|
15
|
+
/** Load the latest credentials. Return `undefined` when the user is not signed in. */
|
|
16
|
+
load(): Promise<GitHubCopilotOAuthTokens | undefined>;
|
|
17
|
+
/** Persist credentials after sign-in or Copilot token exchange. */
|
|
18
|
+
save(tokens: GitHubCopilotOAuthTokens): Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
/** In-progress GitHub Copilot device authorization flow. */
|
|
21
|
+
type GitHubCopilotDeviceFlow = {
|
|
22
|
+
providerId: 'github-copilot';
|
|
23
|
+
/** URL the user should open to authorize the device flow. */
|
|
24
|
+
url: string;
|
|
25
|
+
/** User code to enter on the authorization page. */
|
|
26
|
+
code: string;
|
|
27
|
+
/** Human-readable instruction string for command-line or app UI. */
|
|
28
|
+
instructions: string;
|
|
29
|
+
/** Poll until authorization completes and return GitHub OAuth credentials. */
|
|
30
|
+
complete(): Promise<GitHubCopilotOAuthTokens>;
|
|
31
|
+
};
|
|
32
|
+
/** Options for starting GitHub Copilot's device OAuth flow. */
|
|
33
|
+
type GitHubCopilotDeviceFlowOptions = {
|
|
34
|
+
/** Custom fetch implementation, useful for tests and non-standard runtimes. */
|
|
35
|
+
fetch?: FetchLike;
|
|
36
|
+
/** Sleep override used between polling attempts. */
|
|
37
|
+
sleep?: (ms: number) => Promise<void>;
|
|
38
|
+
/** OAuth client id. Defaults to the GitHub Copilot Chat client id. */
|
|
39
|
+
clientId?: string;
|
|
40
|
+
/** Optional GitHub Enterprise hostname. Custom hosts require `allowEnterprise: true`. */
|
|
41
|
+
enterpriseUrl?: string;
|
|
42
|
+
/** Allow validated custom GitHub Enterprise hostnames. Defaults to false. */
|
|
43
|
+
allowEnterprise?: boolean;
|
|
44
|
+
/** Optional store that receives credentials after successful authorization. */
|
|
45
|
+
tokenStore?: TokenStore;
|
|
46
|
+
};
|
|
47
|
+
/** Shared settings for Copilot fetch and AI SDK provider creation. */
|
|
48
|
+
type GitHubCopilotOAuthSettings = {
|
|
49
|
+
/** Custom fetch implementation for both token exchange and Copilot API requests. */
|
|
50
|
+
fetch?: FetchLike;
|
|
51
|
+
/** Secure token storage used to load and save exchanged Copilot tokens. */
|
|
52
|
+
tokenStore?: TokenStore;
|
|
53
|
+
/** Inline tokens for scripts/tests. Prefer `tokenStore` for production apps. */
|
|
54
|
+
tokens?: GitHubCopilotOAuthTokens;
|
|
55
|
+
/** Optional GitHub Enterprise hostname. Custom hosts require `allowEnterprise: true`. */
|
|
56
|
+
enterpriseUrl?: string;
|
|
57
|
+
/** Allow validated custom GitHub Enterprise hostnames. Defaults to false. */
|
|
58
|
+
allowEnterprise?: boolean;
|
|
59
|
+
/** Copilot API base URL override. Defaults to GitHub's public Copilot API. */
|
|
60
|
+
baseURL?: string;
|
|
61
|
+
/** Browser proxy base URL for Copilot API requests. Defaults to `/api/proxy/github-copilot` in browsers. Pass `false` to disable. */
|
|
62
|
+
browserProxyBaseUrl?: string | false;
|
|
63
|
+
/** Additional headers sent to the Copilot API before Copilot auth headers are applied. */
|
|
64
|
+
headers?: Record<string, string>;
|
|
65
|
+
/** `X-Initiator` header. Defaults to `user`. */
|
|
66
|
+
initiator?: CopilotInitiator;
|
|
67
|
+
/** Force `Copilot-Vision-Request` on or off. When omitted, image inputs are detected from JSON bodies. */
|
|
68
|
+
vision?: boolean;
|
|
69
|
+
/** Refresh Copilot API token this many milliseconds before expiry. */
|
|
70
|
+
tokenRefreshMarginMs?: number;
|
|
71
|
+
/** Allow fallback to the GitHub OAuth token if Copilot token exchange fails or gets 401/403. Defaults to true. */
|
|
72
|
+
fallbackToGitHubToken?: boolean;
|
|
73
|
+
/** Called after a successful Copilot token exchange. */
|
|
74
|
+
onTokens?: (tokens: GitHubCopilotOAuthTokens) => void | Promise<void>;
|
|
75
|
+
};
|
|
76
|
+
/** Settings for the AI SDK provider factory. */
|
|
77
|
+
type GitHubCopilotProviderSettings = GitHubCopilotOAuthSettings & {
|
|
78
|
+
/** Provider name exposed to AI SDK telemetry and metadata. */
|
|
79
|
+
name?: string;
|
|
80
|
+
};
|
|
81
|
+
type CopilotInitiator = 'user' | 'agent';
|
|
82
|
+
/** Error class used for OAuth, token, and credential setup failures. */
|
|
83
|
+
declare class GitHubCopilotOAuthError extends Error {
|
|
84
|
+
readonly code: string;
|
|
85
|
+
constructor(code: string, message: string, options?: ErrorOptions);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { type CopilotInitiator as C, type FetchLike as F, type GitHubCopilotDeviceFlow as G, type TokenStore as T, type GitHubCopilotDeviceFlowOptions as a, GitHubCopilotOAuthError as b, type GitHubCopilotOAuthSettings as c, type GitHubCopilotOAuthTokens as d, type GitHubCopilotProviderSettings as e };
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "github-copilot-oauth",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "GitHub Copilot OAuth provider and token helpers for the Vercel AI SDK.",
|
|
5
|
+
"author": "respectmathias",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/RespectMathias/github-copilot-oauth.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/RespectMathias/github-copilot-oauth/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/RespectMathias/github-copilot-oauth#readme",
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE",
|
|
23
|
+
"NOTICE"
|
|
24
|
+
],
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js",
|
|
29
|
+
"default": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./proxy": {
|
|
32
|
+
"types": "./dist/proxy.d.ts",
|
|
33
|
+
"import": "./dist/proxy.js",
|
|
34
|
+
"default": "./dist/proxy.js"
|
|
35
|
+
},
|
|
36
|
+
"./package.json": "./package.json"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsup --tsconfig tsconfig.json",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"test": "vitest run"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@ai-sdk/openai": ">=3.0.0",
|
|
45
|
+
"@ai-sdk/provider": ">=3.0.0",
|
|
46
|
+
"ai": ">=6.0.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@ai-sdk/openai": "^3.0.64",
|
|
50
|
+
"@ai-sdk/provider": "^3.0.10",
|
|
51
|
+
"@types/node": "^20.17.24",
|
|
52
|
+
"ai": "^6.0.184",
|
|
53
|
+
"tsup": "^8.5.0",
|
|
54
|
+
"typescript": "^5.8.3",
|
|
55
|
+
"vitest": "^4.1.5"
|
|
56
|
+
},
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=18"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"provenance": false
|
|
62
|
+
},
|
|
63
|
+
"keywords": [
|
|
64
|
+
"ai-sdk",
|
|
65
|
+
"github",
|
|
66
|
+
"copilot",
|
|
67
|
+
"oauth",
|
|
68
|
+
"vercel",
|
|
69
|
+
"openai-compatible"
|
|
70
|
+
]
|
|
71
|
+
}
|