@boxiaolanya2008/pi-ai 0.60.4
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/README.md +1198 -0
- package/bedrock-provider.d.ts +1 -0
- package/bedrock-provider.js +1 -0
- package/dist/api-registry.d.ts +20 -0
- package/dist/api-registry.d.ts.map +1 -0
- package/dist/api-registry.js +44 -0
- package/dist/api-registry.js.map +1 -0
- package/dist/bedrock-provider.d.ts +5 -0
- package/dist/bedrock-provider.d.ts.map +1 -0
- package/dist/bedrock-provider.js +6 -0
- package/dist/bedrock-provider.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +113 -0
- package/dist/cli.js.map +1 -0
- package/dist/env-api-keys.d.ts +4 -0
- package/dist/env-api-keys.d.ts.map +1 -0
- package/dist/env-api-keys.js +85 -0
- package/dist/env-api-keys.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/models.d.ts +22 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.generated.d.ts +4 -0
- package/dist/models.generated.d.ts.map +1 -0
- package/dist/models.generated.js +11 -0
- package/dist/models.generated.js.map +1 -0
- package/dist/models.js +50 -0
- package/dist/models.js.map +1 -0
- package/dist/oauth.d.ts +2 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +2 -0
- package/dist/oauth.js.map +1 -0
- package/dist/providers/amazon-bedrock.d.ts +15 -0
- package/dist/providers/amazon-bedrock.d.ts.map +1 -0
- package/dist/providers/amazon-bedrock.js +552 -0
- package/dist/providers/amazon-bedrock.js.map +1 -0
- package/dist/providers/anthropic.d.ts +15 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +677 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/azure-openai-responses.d.ts +12 -0
- package/dist/providers/azure-openai-responses.d.ts.map +1 -0
- package/dist/providers/azure-openai-responses.js +181 -0
- package/dist/providers/azure-openai-responses.js.map +1 -0
- package/dist/providers/github-copilot-headers.d.ts +8 -0
- package/dist/providers/github-copilot-headers.d.ts.map +1 -0
- package/dist/providers/github-copilot-headers.js +26 -0
- package/dist/providers/github-copilot-headers.js.map +1 -0
- package/dist/providers/google-gemini-cli.d.ts +15 -0
- package/dist/providers/google-gemini-cli.d.ts.map +1 -0
- package/dist/providers/google-gemini-cli.js +43 -0
- package/dist/providers/google-gemini-cli.js.map +1 -0
- package/dist/providers/google-shared.d.ts +15 -0
- package/dist/providers/google-shared.d.ts.map +1 -0
- package/dist/providers/google-shared.js +226 -0
- package/dist/providers/google-shared.js.map +1 -0
- package/dist/providers/google-vertex.d.ts +15 -0
- package/dist/providers/google-vertex.d.ts.map +1 -0
- package/dist/providers/google-vertex.js +372 -0
- package/dist/providers/google-vertex.js.map +1 -0
- package/dist/providers/google.d.ts +13 -0
- package/dist/providers/google.d.ts.map +1 -0
- package/dist/providers/google.js +351 -0
- package/dist/providers/google.js.map +1 -0
- package/dist/providers/mistral.d.ts +13 -0
- package/dist/providers/mistral.d.ts.map +1 -0
- package/dist/providers/mistral.js +489 -0
- package/dist/providers/mistral.js.map +1 -0
- package/dist/providers/openai-codex-responses.d.ts +9 -0
- package/dist/providers/openai-codex-responses.d.ts.map +1 -0
- package/dist/providers/openai-codex-responses.js +672 -0
- package/dist/providers/openai-codex-responses.js.map +1 -0
- package/dist/providers/openai-completions.d.ts +15 -0
- package/dist/providers/openai-completions.d.ts.map +1 -0
- package/dist/providers/openai-completions.js +651 -0
- package/dist/providers/openai-completions.js.map +1 -0
- package/dist/providers/openai-responses-shared.d.ts +17 -0
- package/dist/providers/openai-responses-shared.d.ts.map +1 -0
- package/dist/providers/openai-responses-shared.js +420 -0
- package/dist/providers/openai-responses-shared.js.map +1 -0
- package/dist/providers/openai-responses.d.ts +10 -0
- package/dist/providers/openai-responses.d.ts.map +1 -0
- package/dist/providers/openai-responses.js +186 -0
- package/dist/providers/openai-responses.js.map +1 -0
- package/dist/providers/register-builtins.d.ts +11 -0
- package/dist/providers/register-builtins.d.ts.map +1 -0
- package/dist/providers/register-builtins.js +138 -0
- package/dist/providers/register-builtins.js.map +1 -0
- package/dist/providers/simple-options.d.ts +8 -0
- package/dist/providers/simple-options.d.ts.map +1 -0
- package/dist/providers/simple-options.js +35 -0
- package/dist/providers/simple-options.js.map +1 -0
- package/dist/providers/transform-messages.d.ts +3 -0
- package/dist/providers/transform-messages.d.ts.map +1 -0
- package/dist/providers/transform-messages.js +130 -0
- package/dist/providers/transform-messages.js.map +1 -0
- package/dist/stream.d.ts +8 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +27 -0
- package/dist/stream.js.map +1 -0
- package/dist/types.d.ts +213 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/event-stream.d.ts +20 -0
- package/dist/utils/event-stream.d.ts.map +1 -0
- package/dist/utils/event-stream.js +77 -0
- package/dist/utils/event-stream.js.map +1 -0
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +13 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/json-parse.d.ts +2 -0
- package/dist/utils/json-parse.d.ts.map +1 -0
- package/dist/utils/json-parse.js +19 -0
- package/dist/utils/json-parse.js.map +1 -0
- package/dist/utils/oauth/anthropic.d.ts +4 -0
- package/dist/utils/oauth/anthropic.d.ts.map +1 -0
- package/dist/utils/oauth/anthropic.js +61 -0
- package/dist/utils/oauth/anthropic.js.map +1 -0
- package/dist/utils/oauth/github-copilot.d.ts +16 -0
- package/dist/utils/oauth/github-copilot.d.ts.map +1 -0
- package/dist/utils/oauth/github-copilot.js +247 -0
- package/dist/utils/oauth/github-copilot.js.map +1 -0
- package/dist/utils/oauth/google-antigravity.d.ts +7 -0
- package/dist/utils/oauth/google-antigravity.d.ts.map +1 -0
- package/dist/utils/oauth/google-antigravity.js +55 -0
- package/dist/utils/oauth/google-antigravity.js.map +1 -0
- package/dist/utils/oauth/index.d.ts +18 -0
- package/dist/utils/oauth/index.d.ts.map +1 -0
- package/dist/utils/oauth/index.js +72 -0
- package/dist/utils/oauth/index.js.map +1 -0
- package/dist/utils/oauth/openai-codex.d.ts +14 -0
- package/dist/utils/oauth/openai-codex.d.ts.map +1 -0
- package/dist/utils/oauth/openai-codex.js +348 -0
- package/dist/utils/oauth/openai-codex.js.map +1 -0
- package/dist/utils/oauth/pkce.d.ts +5 -0
- package/dist/utils/oauth/pkce.d.ts.map +1 -0
- package/dist/utils/oauth/pkce.js +18 -0
- package/dist/utils/oauth/pkce.js.map +1 -0
- package/dist/utils/oauth/types.d.ts +40 -0
- package/dist/utils/oauth/types.d.ts.map +1 -0
- package/dist/utils/oauth/types.js +2 -0
- package/dist/utils/oauth/types.js.map +1 -0
- package/dist/utils/overflow.d.ts +4 -0
- package/dist/utils/overflow.d.ts.map +1 -0
- package/dist/utils/overflow.js +40 -0
- package/dist/utils/overflow.js.map +1 -0
- package/dist/utils/sanitize-unicode.d.ts +2 -0
- package/dist/utils/sanitize-unicode.d.ts.map +1 -0
- package/dist/utils/sanitize-unicode.js +4 -0
- package/dist/utils/sanitize-unicode.js.map +1 -0
- package/dist/utils/typebox-helpers.d.ts +6 -0
- package/dist/utils/typebox-helpers.d.ts.map +1 -0
- package/dist/utils/typebox-helpers.js +10 -0
- package/dist/utils/typebox-helpers.js.map +1 -0
- package/dist/utils/validation.d.ts +4 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +45 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +80 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { parse as partialParse } from "partial-json";
|
|
2
|
+
export function parseStreamingJson(partialJson) {
|
|
3
|
+
if (!partialJson || partialJson.trim() === "") {
|
|
4
|
+
return {};
|
|
5
|
+
}
|
|
6
|
+
try {
|
|
7
|
+
return JSON.parse(partialJson);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
try {
|
|
11
|
+
const result = partialParse(partialJson);
|
|
12
|
+
return (result ?? {});
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=json-parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-parse.js","sourceRoot":"","sources":["../../src/utils/json-parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,YAAY,EAAE,MAAM,cAAc,CAAC;AACrD,MAAM,UAAU,kBAAkB,CAAU,WAA+B,EAAK;IAC/E,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC/C,OAAO,EAAO,CAAC;IAChB,CAAC;IACD,IAAI,CAAC;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAM,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACR,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;YACzC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAM,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,EAAO,CAAC;QAChB,CAAC;IACF,CAAC;AAAA,CACD","sourcesContent":["import { parse as partialParse } from \"partial-json\";\nexport function parseStreamingJson<T = any>(partialJson: string | undefined): T {\n\tif (!partialJson || partialJson.trim() === \"\") {\n\t\treturn {} as T;\n\t}\n\ttry {\n\t\treturn JSON.parse(partialJson) as T;\n\t} catch {\n\t\ttry {\n\t\t\tconst result = partialParse(partialJson);\n\t\t\treturn (result ?? {}) as T;\n\t\t} catch {\n\t\t\treturn {} as T;\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from "./types.js";
|
|
2
|
+
export declare function loginAnthropic(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
|
|
3
|
+
export declare const AnthropicOAuthProvider: OAuthProviderInterface;
|
|
4
|
+
//# sourceMappingURL=anthropic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/anthropic.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAoDhG,wBAAsB,cAAc,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAE9F;AAUD,eAAO,MAAM,sBAAsB,EAAE,sBAMpC,CAAC","sourcesContent":["import { generatePKCE } from \"./pkce.js\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from \"./types.js\";\n\nconst decode = (s: string) => atob(s);\nconst CLIENT_ID = decode(\"OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl\");\nconst AUTHORIZE_URL = \"https://console.anthropic.com/oauth/authorize\";\nconst TOKEN_URL = \"https://api.anthropic.com/oauth/token\";\nconst REDIRECT_URI = \"https://pi.dev/oauth/anthropic\";\nconst SCOPES = \"org:create_api_key user:profile user:inference\";\n\nasync function performLogin(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst authParams = new URLSearchParams({\n\t\tcode: \"true\",\n\t\tclient_id: CLIENT_ID,\n\t\tresponse_type: \"code\",\n\t\tredirect_uri: REDIRECT_URI,\n\t\tscope: SCOPES,\n\t\tcode_challenge: challenge,\n\t\tcode_challenge_method: \"S256\",\n\t});\n\tconst authUrl = `${AUTHORIZE_URL}?${authParams.toString()}`;\n\tcallbacks.onAuth({ url: authUrl });\n\tconst code = await callbacks.onPrompt({ message: \"Enter the authorization code:\" });\n\tconst tokenParams = new URLSearchParams({\n\t\tgrant_type: \"authorization_code\",\n\t\tclient_id: CLIENT_ID,\n\t\tcode,\n\t\tredirect_uri: REDIRECT_URI,\n\t\tcode_verifier: verifier,\n\t});\n\tconst response = await fetch(TOKEN_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n\t\tbody: tokenParams.toString(),\n\t});\n\tif (!response.ok) {\n\t\tconst text = await response.text();\n\t\tthrow new Error(`Anthropic OAuth error: ${response.status} ${text}`);\n\t}\n\tconst data = (await response.json()) as {\n\t\taccess_token: string;\n\t\trefresh_token?: string;\n\t\ttoken_type: string;\n\t\texpires_in?: number;\n\t};\n\treturn {\n\t\taccess: data.access_token,\n\t\trefresh: data.refresh_token || \"\",\n\t\texpires: data.expires_in ? Date.now() + data.expires_in * 1000 : 0,\n\t};\n}\n\nexport async function loginAnthropic(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\treturn performLogin(callbacks);\n}\n\nasync function refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\treturn credentials;\n}\n\nfunction getApiKey(credentials: OAuthCredentials): string {\n\treturn credentials.access;\n}\n\nexport const AnthropicOAuthProvider: OAuthProviderInterface = {\n\tid: \"anthropic\",\n\tname: \"Anthropic\",\n\tlogin: performLogin,\n\trefreshToken,\n\tgetApiKey,\n};\n"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { generatePKCE } from "./pkce.js";
|
|
2
|
+
const decode = (s) => atob(s);
|
|
3
|
+
const CLIENT_ID = decode("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
|
|
4
|
+
const AUTHORIZE_URL = "https://console.anthropic.com/oauth/authorize";
|
|
5
|
+
const TOKEN_URL = "https://api.anthropic.com/oauth/token";
|
|
6
|
+
const REDIRECT_URI = "https://pi.dev/oauth/anthropic";
|
|
7
|
+
const SCOPES = "org:create_api_key user:profile user:inference";
|
|
8
|
+
async function performLogin(callbacks) {
|
|
9
|
+
const { verifier, challenge } = await generatePKCE();
|
|
10
|
+
const authParams = new URLSearchParams({
|
|
11
|
+
code: "true",
|
|
12
|
+
client_id: CLIENT_ID,
|
|
13
|
+
response_type: "code",
|
|
14
|
+
redirect_uri: REDIRECT_URI,
|
|
15
|
+
scope: SCOPES,
|
|
16
|
+
code_challenge: challenge,
|
|
17
|
+
code_challenge_method: "S256",
|
|
18
|
+
});
|
|
19
|
+
const authUrl = `${AUTHORIZE_URL}?${authParams.toString()}`;
|
|
20
|
+
callbacks.onAuth({ url: authUrl });
|
|
21
|
+
const code = await callbacks.onPrompt({ message: "Enter the authorization code:" });
|
|
22
|
+
const tokenParams = new URLSearchParams({
|
|
23
|
+
grant_type: "authorization_code",
|
|
24
|
+
client_id: CLIENT_ID,
|
|
25
|
+
code,
|
|
26
|
+
redirect_uri: REDIRECT_URI,
|
|
27
|
+
code_verifier: verifier,
|
|
28
|
+
});
|
|
29
|
+
const response = await fetch(TOKEN_URL, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
32
|
+
body: tokenParams.toString(),
|
|
33
|
+
});
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
const text = await response.text();
|
|
36
|
+
throw new Error(`Anthropic OAuth error: ${response.status} ${text}`);
|
|
37
|
+
}
|
|
38
|
+
const data = (await response.json());
|
|
39
|
+
return {
|
|
40
|
+
access: data.access_token,
|
|
41
|
+
refresh: data.refresh_token || "",
|
|
42
|
+
expires: data.expires_in ? Date.now() + data.expires_in * 1000 : 0,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export async function loginAnthropic(callbacks) {
|
|
46
|
+
return performLogin(callbacks);
|
|
47
|
+
}
|
|
48
|
+
async function refreshToken(credentials) {
|
|
49
|
+
return credentials;
|
|
50
|
+
}
|
|
51
|
+
function getApiKey(credentials) {
|
|
52
|
+
return credentials.access;
|
|
53
|
+
}
|
|
54
|
+
export const AnthropicOAuthProvider = {
|
|
55
|
+
id: "anthropic",
|
|
56
|
+
name: "Anthropic",
|
|
57
|
+
login: performLogin,
|
|
58
|
+
refreshToken,
|
|
59
|
+
getApiKey,
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=anthropic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/utils/oauth/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAGzC,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACtC,MAAM,SAAS,GAAG,MAAM,CAAC,kDAAkD,CAAC,CAAC;AAC7E,MAAM,aAAa,GAAG,+CAA+C,CAAC;AACtE,MAAM,SAAS,GAAG,uCAAuC,CAAC;AAC1D,MAAM,YAAY,GAAG,gCAAgC,CAAC;AACtD,MAAM,MAAM,GAAG,gDAAgD,CAAC;AAEhE,KAAK,UAAU,YAAY,CAAC,SAA8B,EAA6B;IACtF,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,YAAY,EAAE,CAAC;IACrD,MAAM,UAAU,GAAG,IAAI,eAAe,CAAC;QACtC,IAAI,EAAE,MAAM;QACZ,SAAS,EAAE,SAAS;QACpB,aAAa,EAAE,MAAM;QACrB,YAAY,EAAE,YAAY;QAC1B,KAAK,EAAE,MAAM;QACb,cAAc,EAAE,SAAS;QACzB,qBAAqB,EAAE,MAAM;KAC7B,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,GAAG,aAAa,IAAI,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC5D,SAAS,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,+BAA+B,EAAE,CAAC,CAAC;IACpF,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC;QACvC,UAAU,EAAE,oBAAoB;QAChC,SAAS,EAAE,SAAS;QACpB,IAAI;QACJ,YAAY,EAAE,YAAY;QAC1B,aAAa,EAAE,QAAQ;KACvB,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;QACvC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;QAChE,IAAI,EAAE,WAAW,CAAC,QAAQ,EAAE;KAC5B,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAKlC,CAAC;IACF,OAAO;QACN,MAAM,EAAE,IAAI,CAAC,YAAY;QACzB,OAAO,EAAE,IAAI,CAAC,aAAa,IAAI,EAAE;QACjC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;KAClE,CAAC;AAAA,CACF;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,SAA8B,EAA6B;IAC/F,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;AAAA,CAC/B;AAED,KAAK,UAAU,YAAY,CAAC,WAA6B,EAA6B;IACrF,OAAO,WAAW,CAAC;AAAA,CACnB;AAED,SAAS,SAAS,CAAC,WAA6B,EAAU;IACzD,OAAO,WAAW,CAAC,MAAM,CAAC;AAAA,CAC1B;AAED,MAAM,CAAC,MAAM,sBAAsB,GAA2B;IAC7D,EAAE,EAAE,WAAW;IACf,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,YAAY;IACnB,YAAY;IACZ,SAAS;CACT,CAAC","sourcesContent":["import { generatePKCE } from \"./pkce.js\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from \"./types.js\";\n\nconst decode = (s: string) => atob(s);\nconst CLIENT_ID = decode(\"OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl\");\nconst AUTHORIZE_URL = \"https://console.anthropic.com/oauth/authorize\";\nconst TOKEN_URL = \"https://api.anthropic.com/oauth/token\";\nconst REDIRECT_URI = \"https://pi.dev/oauth/anthropic\";\nconst SCOPES = \"org:create_api_key user:profile user:inference\";\n\nasync function performLogin(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst authParams = new URLSearchParams({\n\t\tcode: \"true\",\n\t\tclient_id: CLIENT_ID,\n\t\tresponse_type: \"code\",\n\t\tredirect_uri: REDIRECT_URI,\n\t\tscope: SCOPES,\n\t\tcode_challenge: challenge,\n\t\tcode_challenge_method: \"S256\",\n\t});\n\tconst authUrl = `${AUTHORIZE_URL}?${authParams.toString()}`;\n\tcallbacks.onAuth({ url: authUrl });\n\tconst code = await callbacks.onPrompt({ message: \"Enter the authorization code:\" });\n\tconst tokenParams = new URLSearchParams({\n\t\tgrant_type: \"authorization_code\",\n\t\tclient_id: CLIENT_ID,\n\t\tcode,\n\t\tredirect_uri: REDIRECT_URI,\n\t\tcode_verifier: verifier,\n\t});\n\tconst response = await fetch(TOKEN_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n\t\tbody: tokenParams.toString(),\n\t});\n\tif (!response.ok) {\n\t\tconst text = await response.text();\n\t\tthrow new Error(`Anthropic OAuth error: ${response.status} ${text}`);\n\t}\n\tconst data = (await response.json()) as {\n\t\taccess_token: string;\n\t\trefresh_token?: string;\n\t\ttoken_type: string;\n\t\texpires_in?: number;\n\t};\n\treturn {\n\t\taccess: data.access_token,\n\t\trefresh: data.refresh_token || \"\",\n\t\texpires: data.expires_in ? Date.now() + data.expires_in * 1000 : 0,\n\t};\n}\n\nexport async function loginAnthropic(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\treturn performLogin(callbacks);\n}\n\nasync function refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\treturn credentials;\n}\n\nfunction getApiKey(credentials: OAuthCredentials): string {\n\treturn credentials.access;\n}\n\nexport const AnthropicOAuthProvider: OAuthProviderInterface = {\n\tid: \"anthropic\",\n\tname: \"Anthropic\",\n\tlogin: performLogin,\n\trefreshToken,\n\tgetApiKey,\n};\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { OAuthCredentials, OAuthProviderInterface } from "./types.js";
|
|
2
|
+
export declare function normalizeDomain(input: string): string | null;
|
|
3
|
+
export declare function getGitHubCopilotBaseUrl(token?: string, enterpriseDomain?: string): string;
|
|
4
|
+
export declare function refreshGitHubCopilotToken(refreshToken: string, enterpriseDomain?: string): Promise<OAuthCredentials>;
|
|
5
|
+
export declare function loginGitHubCopilot(options: {
|
|
6
|
+
onAuth: (url: string, instructions?: string) => void;
|
|
7
|
+
onPrompt: (prompt: {
|
|
8
|
+
message: string;
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
allowEmpty?: boolean;
|
|
11
|
+
}) => Promise<string>;
|
|
12
|
+
onProgress?: (message: string) => void;
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
}): Promise<OAuthCredentials>;
|
|
15
|
+
export declare const githubCopilotOAuthProvider: OAuthProviderInterface;
|
|
16
|
+
//# sourceMappingURL=github-copilot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-copilot.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/github-copilot.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAuB,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAoChG,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS5D;AAsBD,wBAAgB,uBAAuB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAOzF;AAqHD,wBAAsB,yBAAyB,CAC9C,YAAY,EAAE,MAAM,EACpB,gBAAgB,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,gBAAgB,CAAC,CAwB3B;AAqCD,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IACjD,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACvG,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA4B5B;AAED,eAAO,MAAM,0BAA0B,EAAE,sBAwBxC,CAAC","sourcesContent":["import { getModels } from \"../../models.js\";\nimport type { Api, Model } from \"../../types.js\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from \"./types.js\";\n\ntype CopilotCredentials = OAuthCredentials & {\n\tenterpriseUrl?: string;\n};\n\nconst decode = (s: string) => atob(s);\nconst CLIENT_ID = decode(\"SXYxLmI1MDdhMDhjODdlY2ZlOTg=\");\n\nconst COPILOT_HEADERS = {\n\t\"User-Agent\": \"GitHubCopilotChat/0.35.0\",\n\t\"Editor-Version\": \"vscode/1.107.0\",\n\t\"Editor-Plugin-Version\": \"copilot-chat/0.35.0\",\n\t\"Copilot-Integration-Id\": \"vscode-chat\",\n} as const;\n\ntype DeviceCodeResponse = {\n\tdevice_code: string;\n\tuser_code: string;\n\tverification_uri: string;\n\tinterval: number;\n\texpires_in: number;\n};\n\ntype DeviceTokenSuccessResponse = {\n\taccess_token: string;\n\ttoken_type?: string;\n\tscope?: string;\n};\n\ntype DeviceTokenErrorResponse = {\n\terror: string;\n\terror_description?: string;\n\tinterval?: number;\n};\n\nexport function normalizeDomain(input: string): string | null {\n\tconst trimmed = input.trim();\n\tif (!trimmed) return null;\n\ttry {\n\t\tconst url = trimmed.includes(\":\") ? new URL(trimmed) : new URL(`https://${trimmed}`);\n\t\treturn url.hostname;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction getUrls(domain: string): {\n\tdeviceCodeUrl: string;\n\taccessTokenUrl: string;\n\tcopilotTokenUrl: string;\n} {\n\treturn {\n\t\tdeviceCodeUrl: `https://${domain}/login/device/code`,\n\t\taccessTokenUrl: `https://${domain}/login/oauth/access_token`,\n\t\tcopilotTokenUrl: `https://api.${domain}/copilot_internal/v2/token`,\n\t};\n}\n\nfunction getBaseUrlFromToken(token: string): string | null {\n\tconst match = token.match(/proxy-ep=([^;]+)/);\n\tif (!match) return null;\n\tconst proxyHost = match[1];\n\tconst apiHost = proxyHost.replace(/^proxy\\./, \"api.\");\n\treturn `https://${apiHost}`;\n}\n\nexport function getGitHubCopilotBaseUrl(token?: string, enterpriseDomain?: string): string {\n\tif (token) {\n\t\tconst urlFromToken = getBaseUrlFromToken(token);\n\t\tif (urlFromToken) return urlFromToken;\n\t}\n\tif (enterpriseDomain) return `https://api.${enterpriseDomain}`;\n\treturn \"https://api.github.com\";\n}\n\nasync function fetchJson(url: string, init: RequestInit): Promise<unknown> {\n\tconst response = await fetch(url, init);\n\tif (!response.ok) {\n\t\tconst text = await response.text();\n\t\tthrow new Error(`${response.status} ${response.statusText}: ${text}`);\n\t}\n\treturn response.json();\n}\n\nasync function startDeviceFlow(domain: string): Promise<DeviceCodeResponse> {\n\tconst urls = getUrls(domain);\n\tconst data = await fetchJson(urls.deviceCodeUrl, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\tAccept: \"application/json\",\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\"User-Agent\": \"GitHubCopilotChat/0.35.0\",\n\t\t},\n\t\tbody: JSON.stringify({\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tscope: \"read:user\",\n\t\t}),\n\t});\n\tif (!data || typeof data !== \"object\") {\n\t\tthrow new Error(\"Invalid device code response\");\n\t}\n\tconst deviceCode = (data as Record<string, unknown>).device_code;\n\tconst userCode = (data as Record<string, unknown>).user_code;\n\tconst verificationUri = (data as Record<string, unknown>).verification_uri;\n\tconst interval = (data as Record<string, unknown>).interval;\n\tconst expiresIn = (data as Record<string, unknown>).expires_in;\n\tif (\n\t\ttypeof deviceCode !== \"string\" ||\n\t\ttypeof userCode !== \"string\" ||\n\t\ttypeof verificationUri !== \"string\" ||\n\t\ttypeof interval !== \"number\" ||\n\t\ttypeof expiresIn !== \"number\"\n\t) {\n\t\tthrow new Error(\"Invalid device code response fields\");\n\t}\n\treturn {\n\t\tdevice_code: deviceCode,\n\t\tuser_code: userCode,\n\t\tverification_uri: verificationUri,\n\t\tinterval,\n\t\texpires_in: expiresIn,\n\t};\n}\n\nfunction abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(new Error(\"Login cancelled\"));\n\t\t\treturn;\n\t\t}\n\t\tconst timeout = setTimeout(resolve, ms);\n\t\tsignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\treject(new Error(\"Login cancelled\"));\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\nasync function pollForGitHubAccessToken(\n\tdomain: string,\n\tdeviceCode: string,\n\tintervalSeconds: number,\n\texpiresIn: number,\n\tsignal?: AbortSignal,\n) {\n\tconst urls = getUrls(domain);\n\tconst deadline = Date.now() + expiresIn * 1000;\n\tlet intervalMs = Math.max(1000, Math.floor(intervalSeconds * 1000));\n\twhile (Date.now() < deadline) {\n\t\tif (signal?.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tconst raw = await fetchJson(urls.accessTokenUrl, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\"User-Agent\": \"GitHubCopilotChat/0.35.0\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t\tdevice_code: deviceCode,\n\t\t\t\tgrant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n\t\t\t}),\n\t\t});\n\t\tif (raw && typeof raw === \"object\" && typeof (raw as DeviceTokenSuccessResponse).access_token === \"string\") {\n\t\t\treturn (raw as DeviceTokenSuccessResponse).access_token;\n\t\t}\n\t\tif (raw && typeof raw === \"object\" && typeof (raw as DeviceTokenErrorResponse).error === \"string\") {\n\t\t\tconst err = (raw as DeviceTokenErrorResponse).error;\n\t\t\tif (err === \"authorization_pending\") {\n\t\t\t\tawait abortableSleep(intervalMs, signal);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (err === \"slow_down\") {\n\t\t\t\tintervalMs += 5000;\n\t\t\t\tawait abortableSleep(intervalMs, signal);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow new Error(`Device flow failed: ${err}`);\n\t\t}\n\t\tawait abortableSleep(intervalMs, signal);\n\t}\n\tthrow new Error(\"Device flow timed out\");\n}\n\nexport async function refreshGitHubCopilotToken(\n\trefreshToken: string,\n\tenterpriseDomain?: string,\n): Promise<OAuthCredentials> {\n\tconst domain = enterpriseDomain || \"github.com\";\n\tconst urls = getUrls(domain);\n\tconst raw = await fetchJson(urls.copilotTokenUrl, {\n\t\theaders: {\n\t\t\tAccept: \"application/json\",\n\t\t\tAuthorization: `Bearer ${refreshToken}`,\n\t\t\t...COPILOT_HEADERS,\n\t\t},\n\t});\n\tif (!raw || typeof raw !== \"object\") {\n\t\tthrow new Error(\"Invalid Copilot token response\");\n\t}\n\tconst token = (raw as Record<string, unknown>).token;\n\tconst expiresAt = (raw as Record<string, unknown>).expires_at;\n\tif (typeof token !== \"string\" || typeof expiresAt !== \"number\") {\n\t\tthrow new Error(\"Invalid Copilot token response fields\");\n\t}\n\treturn {\n\t\trefresh: refreshToken,\n\t\taccess: token,\n\t\texpires: expiresAt * 1000 - 5 * 60 * 1000,\n\t\tenterpriseUrl: enterpriseDomain,\n\t};\n}\n\nasync function enableGitHubCopilotModel(token: string, modelId: string, enterpriseDomain?: string): Promise<boolean> {\n\tconst baseUrl = getGitHubCopilotBaseUrl(token, enterpriseDomain);\n\tconst url = `${baseUrl}/models/${modelId}/policy`;\n\ttry {\n\t\tconst response = await fetch(url, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t...COPILOT_HEADERS,\n\t\t\t\t\"openai-intent\": \"chat-policy\",\n\t\t\t\t\"x-interaction-type\": \"chat-policy\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({ state: \"enabled\" }),\n\t\t});\n\t\treturn response.ok;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function enableAllGitHubCopilotModels(\n\ttoken: string,\n\tenterpriseDomain?: string,\n\tonProgress?: (model: string, success: boolean) => void,\n): Promise<void> {\n\tconst models = getModels(\"github-copilot\");\n\tawait Promise.all(\n\t\tmodels.map(async (model) => {\n\t\t\tconst success = await enableGitHubCopilotModel(token, model.id, enterpriseDomain);\n\t\t\tonProgress?.(model.id, success);\n\t\t}),\n\t);\n}\n\nexport async function loginGitHubCopilot(options: {\n\tonAuth: (url: string, instructions?: string) => void;\n\tonPrompt: (prompt: { message: string; placeholder?: string; allowEmpty?: boolean }) => Promise<string>;\n\tonProgress?: (message: string) => void;\n\tsignal?: AbortSignal;\n}): Promise<OAuthCredentials> {\n\tconst input = await options.onPrompt({\n\t\tmessage: \"GitHub Enterprise URL/domain (blank for github.com)\",\n\t\tplaceholder: \"company.ghe.com\",\n\t\tallowEmpty: true,\n\t});\n\tif (options.signal?.aborted) {\n\t\tthrow new Error(\"Login cancelled\");\n\t}\n\tconst trimmed = input.trim();\n\tconst enterpriseDomain = normalizeDomain(input);\n\tif (trimmed && !enterpriseDomain) {\n\t\tthrow new Error(\"Invalid GitHub Enterprise URL/domain\");\n\t}\n\tconst domain = enterpriseDomain || \"github.com\";\n\tconst device = await startDeviceFlow(domain);\n\toptions.onAuth(device.verification_uri, `Enter code: ${device.user_code}`);\n\tconst githubAccessToken = await pollForGitHubAccessToken(\n\t\tdomain,\n\t\tdevice.device_code,\n\t\tdevice.interval,\n\t\tdevice.expires_in,\n\t\toptions.signal,\n\t);\n\tconst credentials = await refreshGitHubCopilotToken(githubAccessToken, enterpriseDomain ?? undefined);\n\toptions.onProgress?.(\"Enabling models...\");\n\tawait enableAllGitHubCopilotModels(credentials.access, enterpriseDomain ?? undefined);\n\treturn credentials;\n}\n\nexport const githubCopilotOAuthProvider: OAuthProviderInterface = {\n\tid: \"github-copilot\",\n\tname: \"GitHub Copilot\",\n\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\treturn loginGitHubCopilot({\n\t\t\tonAuth: (url, instructions) => callbacks.onAuth({ url, instructions }),\n\t\t\tonPrompt: callbacks.onPrompt,\n\t\t\tonProgress: callbacks.onProgress,\n\t\t\tsignal: callbacks.signal,\n\t\t});\n\t},\n\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\tconst creds = credentials as CopilotCredentials;\n\t\treturn refreshGitHubCopilotToken(creds.refresh, creds.enterpriseUrl);\n\t},\n\tgetApiKey(credentials: OAuthCredentials): string {\n\t\treturn credentials.access;\n\t},\n\tmodifyModels(models: Model<Api>[], credentials: OAuthCredentials): Model<Api>[] {\n\t\tconst creds = credentials as CopilotCredentials;\n\t\tconst domain = creds.enterpriseUrl ? (normalizeDomain(creds.enterpriseUrl) ?? undefined) : undefined;\n\t\tconst baseUrl = getGitHubCopilotBaseUrl(creds.access, domain);\n\t\treturn models.map((m) => (m.provider === \"github-copilot\" ? { ...m, baseUrl } : m));\n\t},\n};\n"]}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { getModels } from "../../models.js";
|
|
2
|
+
const decode = (s) => atob(s);
|
|
3
|
+
const CLIENT_ID = decode("SXYxLmI1MDdhMDhjODdlY2ZlOTg=");
|
|
4
|
+
const COPILOT_HEADERS = {
|
|
5
|
+
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
6
|
+
"Editor-Version": "vscode/1.107.0",
|
|
7
|
+
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
8
|
+
"Copilot-Integration-Id": "vscode-chat",
|
|
9
|
+
};
|
|
10
|
+
export function normalizeDomain(input) {
|
|
11
|
+
const trimmed = input.trim();
|
|
12
|
+
if (!trimmed)
|
|
13
|
+
return null;
|
|
14
|
+
try {
|
|
15
|
+
const url = trimmed.includes(":") ? new URL(trimmed) : new URL(`https://${trimmed}`);
|
|
16
|
+
return url.hostname;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function getUrls(domain) {
|
|
23
|
+
return {
|
|
24
|
+
deviceCodeUrl: `https://${domain}/login/device/code`,
|
|
25
|
+
accessTokenUrl: `https://${domain}/login/oauth/access_token`,
|
|
26
|
+
copilotTokenUrl: `https://api.${domain}/copilot_internal/v2/token`,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function getBaseUrlFromToken(token) {
|
|
30
|
+
const match = token.match(/proxy-ep=([^;]+)/);
|
|
31
|
+
if (!match)
|
|
32
|
+
return null;
|
|
33
|
+
const proxyHost = match[1];
|
|
34
|
+
const apiHost = proxyHost.replace(/^proxy\./, "api.");
|
|
35
|
+
return `https://${apiHost}`;
|
|
36
|
+
}
|
|
37
|
+
export function getGitHubCopilotBaseUrl(token, enterpriseDomain) {
|
|
38
|
+
if (token) {
|
|
39
|
+
const urlFromToken = getBaseUrlFromToken(token);
|
|
40
|
+
if (urlFromToken)
|
|
41
|
+
return urlFromToken;
|
|
42
|
+
}
|
|
43
|
+
if (enterpriseDomain)
|
|
44
|
+
return `https://api.${enterpriseDomain}`;
|
|
45
|
+
return "https://api.github.com";
|
|
46
|
+
}
|
|
47
|
+
async function fetchJson(url, init) {
|
|
48
|
+
const response = await fetch(url, init);
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
const text = await response.text();
|
|
51
|
+
throw new Error(`${response.status} ${response.statusText}: ${text}`);
|
|
52
|
+
}
|
|
53
|
+
return response.json();
|
|
54
|
+
}
|
|
55
|
+
async function startDeviceFlow(domain) {
|
|
56
|
+
const urls = getUrls(domain);
|
|
57
|
+
const data = await fetchJson(urls.deviceCodeUrl, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: {
|
|
60
|
+
Accept: "application/json",
|
|
61
|
+
"Content-Type": "application/json",
|
|
62
|
+
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify({
|
|
65
|
+
client_id: CLIENT_ID,
|
|
66
|
+
scope: "read:user",
|
|
67
|
+
}),
|
|
68
|
+
});
|
|
69
|
+
if (!data || typeof data !== "object") {
|
|
70
|
+
throw new Error("Invalid device code response");
|
|
71
|
+
}
|
|
72
|
+
const deviceCode = data.device_code;
|
|
73
|
+
const userCode = data.user_code;
|
|
74
|
+
const verificationUri = data.verification_uri;
|
|
75
|
+
const interval = data.interval;
|
|
76
|
+
const expiresIn = data.expires_in;
|
|
77
|
+
if (typeof deviceCode !== "string" ||
|
|
78
|
+
typeof userCode !== "string" ||
|
|
79
|
+
typeof verificationUri !== "string" ||
|
|
80
|
+
typeof interval !== "number" ||
|
|
81
|
+
typeof expiresIn !== "number") {
|
|
82
|
+
throw new Error("Invalid device code response fields");
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
device_code: deviceCode,
|
|
86
|
+
user_code: userCode,
|
|
87
|
+
verification_uri: verificationUri,
|
|
88
|
+
interval,
|
|
89
|
+
expires_in: expiresIn,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function abortableSleep(ms, signal) {
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
if (signal?.aborted) {
|
|
95
|
+
reject(new Error("Login cancelled"));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const timeout = setTimeout(resolve, ms);
|
|
99
|
+
signal?.addEventListener("abort", () => {
|
|
100
|
+
clearTimeout(timeout);
|
|
101
|
+
reject(new Error("Login cancelled"));
|
|
102
|
+
}, { once: true });
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
async function pollForGitHubAccessToken(domain, deviceCode, intervalSeconds, expiresIn, signal) {
|
|
106
|
+
const urls = getUrls(domain);
|
|
107
|
+
const deadline = Date.now() + expiresIn * 1000;
|
|
108
|
+
let intervalMs = Math.max(1000, Math.floor(intervalSeconds * 1000));
|
|
109
|
+
while (Date.now() < deadline) {
|
|
110
|
+
if (signal?.aborted) {
|
|
111
|
+
throw new Error("Login cancelled");
|
|
112
|
+
}
|
|
113
|
+
const raw = await fetchJson(urls.accessTokenUrl, {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers: {
|
|
116
|
+
Accept: "application/json",
|
|
117
|
+
"Content-Type": "application/json",
|
|
118
|
+
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
119
|
+
},
|
|
120
|
+
body: JSON.stringify({
|
|
121
|
+
client_id: CLIENT_ID,
|
|
122
|
+
device_code: deviceCode,
|
|
123
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
124
|
+
}),
|
|
125
|
+
});
|
|
126
|
+
if (raw && typeof raw === "object" && typeof raw.access_token === "string") {
|
|
127
|
+
return raw.access_token;
|
|
128
|
+
}
|
|
129
|
+
if (raw && typeof raw === "object" && typeof raw.error === "string") {
|
|
130
|
+
const err = raw.error;
|
|
131
|
+
if (err === "authorization_pending") {
|
|
132
|
+
await abortableSleep(intervalMs, signal);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (err === "slow_down") {
|
|
136
|
+
intervalMs += 5000;
|
|
137
|
+
await abortableSleep(intervalMs, signal);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
throw new Error(`Device flow failed: ${err}`);
|
|
141
|
+
}
|
|
142
|
+
await abortableSleep(intervalMs, signal);
|
|
143
|
+
}
|
|
144
|
+
throw new Error("Device flow timed out");
|
|
145
|
+
}
|
|
146
|
+
export async function refreshGitHubCopilotToken(refreshToken, enterpriseDomain) {
|
|
147
|
+
const domain = enterpriseDomain || "github.com";
|
|
148
|
+
const urls = getUrls(domain);
|
|
149
|
+
const raw = await fetchJson(urls.copilotTokenUrl, {
|
|
150
|
+
headers: {
|
|
151
|
+
Accept: "application/json",
|
|
152
|
+
Authorization: `Bearer ${refreshToken}`,
|
|
153
|
+
...COPILOT_HEADERS,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
if (!raw || typeof raw !== "object") {
|
|
157
|
+
throw new Error("Invalid Copilot token response");
|
|
158
|
+
}
|
|
159
|
+
const token = raw.token;
|
|
160
|
+
const expiresAt = raw.expires_at;
|
|
161
|
+
if (typeof token !== "string" || typeof expiresAt !== "number") {
|
|
162
|
+
throw new Error("Invalid Copilot token response fields");
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
refresh: refreshToken,
|
|
166
|
+
access: token,
|
|
167
|
+
expires: expiresAt * 1000 - 5 * 60 * 1000,
|
|
168
|
+
enterpriseUrl: enterpriseDomain,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
async function enableGitHubCopilotModel(token, modelId, enterpriseDomain) {
|
|
172
|
+
const baseUrl = getGitHubCopilotBaseUrl(token, enterpriseDomain);
|
|
173
|
+
const url = `${baseUrl}/models/${modelId}/policy`;
|
|
174
|
+
try {
|
|
175
|
+
const response = await fetch(url, {
|
|
176
|
+
method: "POST",
|
|
177
|
+
headers: {
|
|
178
|
+
"Content-Type": "application/json",
|
|
179
|
+
Authorization: `Bearer ${token}`,
|
|
180
|
+
...COPILOT_HEADERS,
|
|
181
|
+
"openai-intent": "chat-policy",
|
|
182
|
+
"x-interaction-type": "chat-policy",
|
|
183
|
+
},
|
|
184
|
+
body: JSON.stringify({ state: "enabled" }),
|
|
185
|
+
});
|
|
186
|
+
return response.ok;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
async function enableAllGitHubCopilotModels(token, enterpriseDomain, onProgress) {
|
|
193
|
+
const models = getModels("github-copilot");
|
|
194
|
+
await Promise.all(models.map(async (model) => {
|
|
195
|
+
const success = await enableGitHubCopilotModel(token, model.id, enterpriseDomain);
|
|
196
|
+
onProgress?.(model.id, success);
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
export async function loginGitHubCopilot(options) {
|
|
200
|
+
const input = await options.onPrompt({
|
|
201
|
+
message: "GitHub Enterprise URL/domain (blank for github.com)",
|
|
202
|
+
placeholder: "company.ghe.com",
|
|
203
|
+
allowEmpty: true,
|
|
204
|
+
});
|
|
205
|
+
if (options.signal?.aborted) {
|
|
206
|
+
throw new Error("Login cancelled");
|
|
207
|
+
}
|
|
208
|
+
const trimmed = input.trim();
|
|
209
|
+
const enterpriseDomain = normalizeDomain(input);
|
|
210
|
+
if (trimmed && !enterpriseDomain) {
|
|
211
|
+
throw new Error("Invalid GitHub Enterprise URL/domain");
|
|
212
|
+
}
|
|
213
|
+
const domain = enterpriseDomain || "github.com";
|
|
214
|
+
const device = await startDeviceFlow(domain);
|
|
215
|
+
options.onAuth(device.verification_uri, `Enter code: ${device.user_code}`);
|
|
216
|
+
const githubAccessToken = await pollForGitHubAccessToken(domain, device.device_code, device.interval, device.expires_in, options.signal);
|
|
217
|
+
const credentials = await refreshGitHubCopilotToken(githubAccessToken, enterpriseDomain ?? undefined);
|
|
218
|
+
options.onProgress?.("Enabling models...");
|
|
219
|
+
await enableAllGitHubCopilotModels(credentials.access, enterpriseDomain ?? undefined);
|
|
220
|
+
return credentials;
|
|
221
|
+
}
|
|
222
|
+
export const githubCopilotOAuthProvider = {
|
|
223
|
+
id: "github-copilot",
|
|
224
|
+
name: "GitHub Copilot",
|
|
225
|
+
async login(callbacks) {
|
|
226
|
+
return loginGitHubCopilot({
|
|
227
|
+
onAuth: (url, instructions) => callbacks.onAuth({ url, instructions }),
|
|
228
|
+
onPrompt: callbacks.onPrompt,
|
|
229
|
+
onProgress: callbacks.onProgress,
|
|
230
|
+
signal: callbacks.signal,
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
async refreshToken(credentials) {
|
|
234
|
+
const creds = credentials;
|
|
235
|
+
return refreshGitHubCopilotToken(creds.refresh, creds.enterpriseUrl);
|
|
236
|
+
},
|
|
237
|
+
getApiKey(credentials) {
|
|
238
|
+
return credentials.access;
|
|
239
|
+
},
|
|
240
|
+
modifyModels(models, credentials) {
|
|
241
|
+
const creds = credentials;
|
|
242
|
+
const domain = creds.enterpriseUrl ? (normalizeDomain(creds.enterpriseUrl) ?? undefined) : undefined;
|
|
243
|
+
const baseUrl = getGitHubCopilotBaseUrl(creds.access, domain);
|
|
244
|
+
return models.map((m) => (m.provider === "github-copilot" ? { ...m, baseUrl } : m));
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
//# sourceMappingURL=github-copilot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-copilot.js","sourceRoot":"","sources":["../../../src/utils/oauth/github-copilot.ts"],"names":[],"mappings":"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","sourcesContent":["import { getModels } from \"../../models.js\";\nimport type { Api, Model } from \"../../types.js\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from \"./types.js\";\n\ntype CopilotCredentials = OAuthCredentials & {\n\tenterpriseUrl?: string;\n};\n\nconst decode = (s: string) => atob(s);\nconst CLIENT_ID = decode(\"SXYxLmI1MDdhMDhjODdlY2ZlOTg=\");\n\nconst COPILOT_HEADERS = {\n\t\"User-Agent\": \"GitHubCopilotChat/0.35.0\",\n\t\"Editor-Version\": \"vscode/1.107.0\",\n\t\"Editor-Plugin-Version\": \"copilot-chat/0.35.0\",\n\t\"Copilot-Integration-Id\": \"vscode-chat\",\n} as const;\n\ntype DeviceCodeResponse = {\n\tdevice_code: string;\n\tuser_code: string;\n\tverification_uri: string;\n\tinterval: number;\n\texpires_in: number;\n};\n\ntype DeviceTokenSuccessResponse = {\n\taccess_token: string;\n\ttoken_type?: string;\n\tscope?: string;\n};\n\ntype DeviceTokenErrorResponse = {\n\terror: string;\n\terror_description?: string;\n\tinterval?: number;\n};\n\nexport function normalizeDomain(input: string): string | null {\n\tconst trimmed = input.trim();\n\tif (!trimmed) return null;\n\ttry {\n\t\tconst url = trimmed.includes(\":\") ? new URL(trimmed) : new URL(`https://${trimmed}`);\n\t\treturn url.hostname;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction getUrls(domain: string): {\n\tdeviceCodeUrl: string;\n\taccessTokenUrl: string;\n\tcopilotTokenUrl: string;\n} {\n\treturn {\n\t\tdeviceCodeUrl: `https://${domain}/login/device/code`,\n\t\taccessTokenUrl: `https://${domain}/login/oauth/access_token`,\n\t\tcopilotTokenUrl: `https://api.${domain}/copilot_internal/v2/token`,\n\t};\n}\n\nfunction getBaseUrlFromToken(token: string): string | null {\n\tconst match = token.match(/proxy-ep=([^;]+)/);\n\tif (!match) return null;\n\tconst proxyHost = match[1];\n\tconst apiHost = proxyHost.replace(/^proxy\\./, \"api.\");\n\treturn `https://${apiHost}`;\n}\n\nexport function getGitHubCopilotBaseUrl(token?: string, enterpriseDomain?: string): string {\n\tif (token) {\n\t\tconst urlFromToken = getBaseUrlFromToken(token);\n\t\tif (urlFromToken) return urlFromToken;\n\t}\n\tif (enterpriseDomain) return `https://api.${enterpriseDomain}`;\n\treturn \"https://api.github.com\";\n}\n\nasync function fetchJson(url: string, init: RequestInit): Promise<unknown> {\n\tconst response = await fetch(url, init);\n\tif (!response.ok) {\n\t\tconst text = await response.text();\n\t\tthrow new Error(`${response.status} ${response.statusText}: ${text}`);\n\t}\n\treturn response.json();\n}\n\nasync function startDeviceFlow(domain: string): Promise<DeviceCodeResponse> {\n\tconst urls = getUrls(domain);\n\tconst data = await fetchJson(urls.deviceCodeUrl, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\tAccept: \"application/json\",\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\"User-Agent\": \"GitHubCopilotChat/0.35.0\",\n\t\t},\n\t\tbody: JSON.stringify({\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tscope: \"read:user\",\n\t\t}),\n\t});\n\tif (!data || typeof data !== \"object\") {\n\t\tthrow new Error(\"Invalid device code response\");\n\t}\n\tconst deviceCode = (data as Record<string, unknown>).device_code;\n\tconst userCode = (data as Record<string, unknown>).user_code;\n\tconst verificationUri = (data as Record<string, unknown>).verification_uri;\n\tconst interval = (data as Record<string, unknown>).interval;\n\tconst expiresIn = (data as Record<string, unknown>).expires_in;\n\tif (\n\t\ttypeof deviceCode !== \"string\" ||\n\t\ttypeof userCode !== \"string\" ||\n\t\ttypeof verificationUri !== \"string\" ||\n\t\ttypeof interval !== \"number\" ||\n\t\ttypeof expiresIn !== \"number\"\n\t) {\n\t\tthrow new Error(\"Invalid device code response fields\");\n\t}\n\treturn {\n\t\tdevice_code: deviceCode,\n\t\tuser_code: userCode,\n\t\tverification_uri: verificationUri,\n\t\tinterval,\n\t\texpires_in: expiresIn,\n\t};\n}\n\nfunction abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(new Error(\"Login cancelled\"));\n\t\t\treturn;\n\t\t}\n\t\tconst timeout = setTimeout(resolve, ms);\n\t\tsignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\treject(new Error(\"Login cancelled\"));\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\nasync function pollForGitHubAccessToken(\n\tdomain: string,\n\tdeviceCode: string,\n\tintervalSeconds: number,\n\texpiresIn: number,\n\tsignal?: AbortSignal,\n) {\n\tconst urls = getUrls(domain);\n\tconst deadline = Date.now() + expiresIn * 1000;\n\tlet intervalMs = Math.max(1000, Math.floor(intervalSeconds * 1000));\n\twhile (Date.now() < deadline) {\n\t\tif (signal?.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tconst raw = await fetchJson(urls.accessTokenUrl, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\"User-Agent\": \"GitHubCopilotChat/0.35.0\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t\tdevice_code: deviceCode,\n\t\t\t\tgrant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n\t\t\t}),\n\t\t});\n\t\tif (raw && typeof raw === \"object\" && typeof (raw as DeviceTokenSuccessResponse).access_token === \"string\") {\n\t\t\treturn (raw as DeviceTokenSuccessResponse).access_token;\n\t\t}\n\t\tif (raw && typeof raw === \"object\" && typeof (raw as DeviceTokenErrorResponse).error === \"string\") {\n\t\t\tconst err = (raw as DeviceTokenErrorResponse).error;\n\t\t\tif (err === \"authorization_pending\") {\n\t\t\t\tawait abortableSleep(intervalMs, signal);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (err === \"slow_down\") {\n\t\t\t\tintervalMs += 5000;\n\t\t\t\tawait abortableSleep(intervalMs, signal);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow new Error(`Device flow failed: ${err}`);\n\t\t}\n\t\tawait abortableSleep(intervalMs, signal);\n\t}\n\tthrow new Error(\"Device flow timed out\");\n}\n\nexport async function refreshGitHubCopilotToken(\n\trefreshToken: string,\n\tenterpriseDomain?: string,\n): Promise<OAuthCredentials> {\n\tconst domain = enterpriseDomain || \"github.com\";\n\tconst urls = getUrls(domain);\n\tconst raw = await fetchJson(urls.copilotTokenUrl, {\n\t\theaders: {\n\t\t\tAccept: \"application/json\",\n\t\t\tAuthorization: `Bearer ${refreshToken}`,\n\t\t\t...COPILOT_HEADERS,\n\t\t},\n\t});\n\tif (!raw || typeof raw !== \"object\") {\n\t\tthrow new Error(\"Invalid Copilot token response\");\n\t}\n\tconst token = (raw as Record<string, unknown>).token;\n\tconst expiresAt = (raw as Record<string, unknown>).expires_at;\n\tif (typeof token !== \"string\" || typeof expiresAt !== \"number\") {\n\t\tthrow new Error(\"Invalid Copilot token response fields\");\n\t}\n\treturn {\n\t\trefresh: refreshToken,\n\t\taccess: token,\n\t\texpires: expiresAt * 1000 - 5 * 60 * 1000,\n\t\tenterpriseUrl: enterpriseDomain,\n\t};\n}\n\nasync function enableGitHubCopilotModel(token: string, modelId: string, enterpriseDomain?: string): Promise<boolean> {\n\tconst baseUrl = getGitHubCopilotBaseUrl(token, enterpriseDomain);\n\tconst url = `${baseUrl}/models/${modelId}/policy`;\n\ttry {\n\t\tconst response = await fetch(url, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t...COPILOT_HEADERS,\n\t\t\t\t\"openai-intent\": \"chat-policy\",\n\t\t\t\t\"x-interaction-type\": \"chat-policy\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({ state: \"enabled\" }),\n\t\t});\n\t\treturn response.ok;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function enableAllGitHubCopilotModels(\n\ttoken: string,\n\tenterpriseDomain?: string,\n\tonProgress?: (model: string, success: boolean) => void,\n): Promise<void> {\n\tconst models = getModels(\"github-copilot\");\n\tawait Promise.all(\n\t\tmodels.map(async (model) => {\n\t\t\tconst success = await enableGitHubCopilotModel(token, model.id, enterpriseDomain);\n\t\t\tonProgress?.(model.id, success);\n\t\t}),\n\t);\n}\n\nexport async function loginGitHubCopilot(options: {\n\tonAuth: (url: string, instructions?: string) => void;\n\tonPrompt: (prompt: { message: string; placeholder?: string; allowEmpty?: boolean }) => Promise<string>;\n\tonProgress?: (message: string) => void;\n\tsignal?: AbortSignal;\n}): Promise<OAuthCredentials> {\n\tconst input = await options.onPrompt({\n\t\tmessage: \"GitHub Enterprise URL/domain (blank for github.com)\",\n\t\tplaceholder: \"company.ghe.com\",\n\t\tallowEmpty: true,\n\t});\n\tif (options.signal?.aborted) {\n\t\tthrow new Error(\"Login cancelled\");\n\t}\n\tconst trimmed = input.trim();\n\tconst enterpriseDomain = normalizeDomain(input);\n\tif (trimmed && !enterpriseDomain) {\n\t\tthrow new Error(\"Invalid GitHub Enterprise URL/domain\");\n\t}\n\tconst domain = enterpriseDomain || \"github.com\";\n\tconst device = await startDeviceFlow(domain);\n\toptions.onAuth(device.verification_uri, `Enter code: ${device.user_code}`);\n\tconst githubAccessToken = await pollForGitHubAccessToken(\n\t\tdomain,\n\t\tdevice.device_code,\n\t\tdevice.interval,\n\t\tdevice.expires_in,\n\t\toptions.signal,\n\t);\n\tconst credentials = await refreshGitHubCopilotToken(githubAccessToken, enterpriseDomain ?? undefined);\n\toptions.onProgress?.(\"Enabling models...\");\n\tawait enableAllGitHubCopilotModels(credentials.access, enterpriseDomain ?? undefined);\n\treturn credentials;\n}\n\nexport const githubCopilotOAuthProvider: OAuthProviderInterface = {\n\tid: \"github-copilot\",\n\tname: \"GitHub Copilot\",\n\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\treturn loginGitHubCopilot({\n\t\t\tonAuth: (url, instructions) => callbacks.onAuth({ url, instructions }),\n\t\t\tonPrompt: callbacks.onPrompt,\n\t\t\tonProgress: callbacks.onProgress,\n\t\t\tsignal: callbacks.signal,\n\t\t});\n\t},\n\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\tconst creds = credentials as CopilotCredentials;\n\t\treturn refreshGitHubCopilotToken(creds.refresh, creds.enterpriseUrl);\n\t},\n\tgetApiKey(credentials: OAuthCredentials): string {\n\t\treturn credentials.access;\n\t},\n\tmodifyModels(models: Model<Api>[], credentials: OAuthCredentials): Model<Api>[] {\n\t\tconst creds = credentials as CopilotCredentials;\n\t\tconst domain = creds.enterpriseUrl ? (normalizeDomain(creds.enterpriseUrl) ?? undefined) : undefined;\n\t\tconst baseUrl = getGitHubCopilotBaseUrl(creds.access, domain);\n\t\treturn models.map((m) => (m.provider === \"github-copilot\" ? { ...m, baseUrl } : m));\n\t},\n};\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { OAuthProviderInterface } from "./types.js";
|
|
2
|
+
export interface GoogleAntigravityOptions {
|
|
3
|
+
clientId: string;
|
|
4
|
+
scopes?: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare function createGoogleAntigravityProvider(options: GoogleAntigravityOptions): OAuthProviderInterface;
|
|
7
|
+
//# sourceMappingURL=google-antigravity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-antigravity.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/google-antigravity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAyC,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAKhG,MAAM,WAAW,wBAAwB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,wBAAwB,GAAG,sBAAsB,CA+DzG","sourcesContent":["import { generatePKCE } from \"./pkce.js\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from \"./types.js\";\n\nconst OAUTH_BASE = \"https://oauth2.googleapis.com\";\nconst AUTH_BASE = \"https://accounts.google.com\";\n\nexport interface GoogleAntigravityOptions {\n\tclientId: string;\n\tscopes?: string[];\n}\n\nexport function createGoogleAntigravityProvider(options: GoogleAntigravityOptions): OAuthProviderInterface {\n\tconst { clientId, scopes = [\"openid\", \"email\", \"profile\"] } = options;\n\n\treturn {\n\t\tid: \"google-antigravity\",\n\t\tname: \"Google (Antigravity)\",\n\t\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\t\tconst { verifier, challenge } = await generatePKCE();\n\t\t\tconst authParams = new URLSearchParams({\n\t\t\t\tclient_id: clientId,\n\t\t\t\tresponse_type: \"code\",\n\t\t\t\tscope: scopes.join(\" \"),\n\t\t\t\tcode_challenge: challenge,\n\t\t\t\tcode_challenge_method: \"S256\",\n\t\t\t\tredirect_uri: \"http://localhost:8085/oauth/callback\",\n\t\t\t});\n\n\t\t\tconst authUrl = `${AUTH_BASE}/o/oauth2/v2/auth?${authParams.toString()}`;\n\t\t\tcallbacks.onAuth({ url: authUrl });\n\n\t\t\tconst code = await callbacks.onPrompt({\n\t\t\t\tmessage: \"Enter the authorization code:\",\n\t\t\t});\n\n\t\t\tconst tokenParams = new URLSearchParams({\n\t\t\t\tgrant_type: \"authorization_code\",\n\t\t\t\tclient_id: clientId,\n\t\t\t\tcode,\n\t\t\t\tredirect_uri: \"http://localhost:8085/oauth/callback\",\n\t\t\t\tcode_verifier: verifier,\n\t\t\t});\n\n\t\t\tconst response = await fetch(`${OAUTH_BASE}/token`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n\t\t\t\tbody: tokenParams.toString(),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text();\n\t\t\t\tthrow new Error(`Google OAuth error: ${response.status} ${text}`);\n\t\t\t}\n\n\t\t\tconst data = (await response.json()) as {\n\t\t\t\taccess_token: string;\n\t\t\t\ttoken_type: string;\n\t\t\t\texpires_in?: number;\n\t\t\t\trefresh_token?: string;\n\t\t\t};\n\n\t\t\treturn {\n\t\t\t\taccess: data.access_token,\n\t\t\t\trefresh: data.refresh_token || \"\",\n\t\t\t\texpires: data.expires_in ? Date.now() + data.expires_in * 1000 : 0,\n\t\t\t};\n\t\t},\n\t\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\t\treturn credentials;\n\t\t},\n\t\tgetApiKey(credentials: OAuthCredentials): string {\n\t\t\treturn credentials.access;\n\t\t},\n\t};\n}\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { generatePKCE } from "./pkce.js";
|
|
2
|
+
const OAUTH_BASE = "https://oauth2.googleapis.com";
|
|
3
|
+
const AUTH_BASE = "https://accounts.google.com";
|
|
4
|
+
export function createGoogleAntigravityProvider(options) {
|
|
5
|
+
const { clientId, scopes = ["openid", "email", "profile"] } = options;
|
|
6
|
+
return {
|
|
7
|
+
id: "google-antigravity",
|
|
8
|
+
name: "Google (Antigravity)",
|
|
9
|
+
async login(callbacks) {
|
|
10
|
+
const { verifier, challenge } = await generatePKCE();
|
|
11
|
+
const authParams = new URLSearchParams({
|
|
12
|
+
client_id: clientId,
|
|
13
|
+
response_type: "code",
|
|
14
|
+
scope: scopes.join(" "),
|
|
15
|
+
code_challenge: challenge,
|
|
16
|
+
code_challenge_method: "S256",
|
|
17
|
+
redirect_uri: "http://localhost:8085/oauth/callback",
|
|
18
|
+
});
|
|
19
|
+
const authUrl = `${AUTH_BASE}/o/oauth2/v2/auth?${authParams.toString()}`;
|
|
20
|
+
callbacks.onAuth({ url: authUrl });
|
|
21
|
+
const code = await callbacks.onPrompt({
|
|
22
|
+
message: "Enter the authorization code:",
|
|
23
|
+
});
|
|
24
|
+
const tokenParams = new URLSearchParams({
|
|
25
|
+
grant_type: "authorization_code",
|
|
26
|
+
client_id: clientId,
|
|
27
|
+
code,
|
|
28
|
+
redirect_uri: "http://localhost:8085/oauth/callback",
|
|
29
|
+
code_verifier: verifier,
|
|
30
|
+
});
|
|
31
|
+
const response = await fetch(`${OAUTH_BASE}/token`, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
34
|
+
body: tokenParams.toString(),
|
|
35
|
+
});
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
const text = await response.text();
|
|
38
|
+
throw new Error(`Google OAuth error: ${response.status} ${text}`);
|
|
39
|
+
}
|
|
40
|
+
const data = (await response.json());
|
|
41
|
+
return {
|
|
42
|
+
access: data.access_token,
|
|
43
|
+
refresh: data.refresh_token || "",
|
|
44
|
+
expires: data.expires_in ? Date.now() + data.expires_in * 1000 : 0,
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
async refreshToken(credentials) {
|
|
48
|
+
return credentials;
|
|
49
|
+
},
|
|
50
|
+
getApiKey(credentials) {
|
|
51
|
+
return credentials.access;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=google-antigravity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-antigravity.js","sourceRoot":"","sources":["../../../src/utils/oauth/google-antigravity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAGzC,MAAM,UAAU,GAAG,+BAA+B,CAAC;AACnD,MAAM,SAAS,GAAG,6BAA6B,CAAC;AAOhD,MAAM,UAAU,+BAA+B,CAAC,OAAiC,EAA0B;IAC1G,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,GAAG,OAAO,CAAC;IAEtE,OAAO;QACN,EAAE,EAAE,oBAAoB;QACxB,IAAI,EAAE,sBAAsB;QAC5B,KAAK,CAAC,KAAK,CAAC,SAA8B,EAA6B;YACtE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,YAAY,EAAE,CAAC;YACrD,MAAM,UAAU,GAAG,IAAI,eAAe,CAAC;gBACtC,SAAS,EAAE,QAAQ;gBACnB,aAAa,EAAE,MAAM;gBACrB,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;gBACvB,cAAc,EAAE,SAAS;gBACzB,qBAAqB,EAAE,MAAM;gBAC7B,YAAY,EAAE,sCAAsC;aACpD,CAAC,CAAC;YAEH,MAAM,OAAO,GAAG,GAAG,SAAS,qBAAqB,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAC;YACzE,SAAS,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAEnC,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC;gBACrC,OAAO,EAAE,+BAA+B;aACxC,CAAC,CAAC;YAEH,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC;gBACvC,UAAU,EAAE,oBAAoB;gBAChC,SAAS,EAAE,QAAQ;gBACnB,IAAI;gBACJ,YAAY,EAAE,sCAAsC;gBACpD,aAAa,EAAE,QAAQ;aACvB,CAAC,CAAC;YAEH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,UAAU,QAAQ,EAAE;gBACnD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;gBAChE,IAAI,EAAE,WAAW,CAAC,QAAQ,EAAE;aAC5B,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAClB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;YACnE,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAKlC,CAAC;YAEF,OAAO;gBACN,MAAM,EAAE,IAAI,CAAC,YAAY;gBACzB,OAAO,EAAE,IAAI,CAAC,aAAa,IAAI,EAAE;gBACjC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;aAClE,CAAC;QAAA,CACF;QACD,KAAK,CAAC,YAAY,CAAC,WAA6B,EAA6B;YAC5E,OAAO,WAAW,CAAC;QAAA,CACnB;QACD,SAAS,CAAC,WAA6B,EAAU;YAChD,OAAO,WAAW,CAAC,MAAM,CAAC;QAAA,CAC1B;KACD,CAAC;AAAA,CACF","sourcesContent":["import { generatePKCE } from \"./pkce.js\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from \"./types.js\";\n\nconst OAUTH_BASE = \"https://oauth2.googleapis.com\";\nconst AUTH_BASE = \"https://accounts.google.com\";\n\nexport interface GoogleAntigravityOptions {\n\tclientId: string;\n\tscopes?: string[];\n}\n\nexport function createGoogleAntigravityProvider(options: GoogleAntigravityOptions): OAuthProviderInterface {\n\tconst { clientId, scopes = [\"openid\", \"email\", \"profile\"] } = options;\n\n\treturn {\n\t\tid: \"google-antigravity\",\n\t\tname: \"Google (Antigravity)\",\n\t\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\t\tconst { verifier, challenge } = await generatePKCE();\n\t\t\tconst authParams = new URLSearchParams({\n\t\t\t\tclient_id: clientId,\n\t\t\t\tresponse_type: \"code\",\n\t\t\t\tscope: scopes.join(\" \"),\n\t\t\t\tcode_challenge: challenge,\n\t\t\t\tcode_challenge_method: \"S256\",\n\t\t\t\tredirect_uri: \"http://localhost:8085/oauth/callback\",\n\t\t\t});\n\n\t\t\tconst authUrl = `${AUTH_BASE}/o/oauth2/v2/auth?${authParams.toString()}`;\n\t\t\tcallbacks.onAuth({ url: authUrl });\n\n\t\t\tconst code = await callbacks.onPrompt({\n\t\t\t\tmessage: \"Enter the authorization code:\",\n\t\t\t});\n\n\t\t\tconst tokenParams = new URLSearchParams({\n\t\t\t\tgrant_type: \"authorization_code\",\n\t\t\t\tclient_id: clientId,\n\t\t\t\tcode,\n\t\t\t\tredirect_uri: \"http://localhost:8085/oauth/callback\",\n\t\t\t\tcode_verifier: verifier,\n\t\t\t});\n\n\t\t\tconst response = await fetch(`${OAUTH_BASE}/token`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n\t\t\t\tbody: tokenParams.toString(),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text();\n\t\t\t\tthrow new Error(`Google OAuth error: ${response.status} ${text}`);\n\t\t\t}\n\n\t\t\tconst data = (await response.json()) as {\n\t\t\t\taccess_token: string;\n\t\t\t\ttoken_type: string;\n\t\t\t\texpires_in?: number;\n\t\t\t\trefresh_token?: string;\n\t\t\t};\n\n\t\t\treturn {\n\t\t\t\taccess: data.access_token,\n\t\t\t\trefresh: data.refresh_token || \"\",\n\t\t\t\texpires: data.expires_in ? Date.now() + data.expires_in * 1000 : 0,\n\t\t\t};\n\t\t},\n\t\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\t\treturn credentials;\n\t\t},\n\t\tgetApiKey(credentials: OAuthCredentials): string {\n\t\t\treturn credentials.access;\n\t\t},\n\t};\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { AnthropicOAuthProvider, loginAnthropic } from "./anthropic.js";
|
|
2
|
+
export { getGitHubCopilotBaseUrl, githubCopilotOAuthProvider, loginGitHubCopilot, normalizeDomain, refreshGitHubCopilotToken, } from "./github-copilot.js";
|
|
3
|
+
export { createGoogleAntigravityProvider } from "./google-antigravity.js";
|
|
4
|
+
export { loginOpenAICodex, openaiCodexOAuthProvider, refreshOpenAICodexToken } from "./openai-codex.js";
|
|
5
|
+
export * from "./types.js";
|
|
6
|
+
import type { OAuthCredentials, OAuthProviderId, OAuthProviderInfo, OAuthProviderInterface } from "./types.js";
|
|
7
|
+
export declare function getOAuthProvider(id: OAuthProviderId): OAuthProviderInterface | undefined;
|
|
8
|
+
export declare function registerOAuthProvider(provider: OAuthProviderInterface): void;
|
|
9
|
+
export declare function unregisterOAuthProvider(id: string): void;
|
|
10
|
+
export declare function resetOAuthProviders(): void;
|
|
11
|
+
export declare function getOAuthProviders(): OAuthProviderInterface[];
|
|
12
|
+
export declare function getOAuthProviderInfoList(): OAuthProviderInfo[];
|
|
13
|
+
export declare function refreshOAuthToken(providerId: OAuthProviderId, credentials: OAuthCredentials): Promise<OAuthCredentials>;
|
|
14
|
+
export declare function getOAuthApiKey(providerId: OAuthProviderId, credentials: Record<string, OAuthCredentials>): Promise<{
|
|
15
|
+
newCredentials: OAuthCredentials;
|
|
16
|
+
apiKey: string;
|
|
17
|
+
} | null>;
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EACN,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,EAClB,eAAe,EACf,yBAAyB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,+BAA+B,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AACxG,cAAc,YAAY,CAAC;AAK3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAY/G,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,eAAe,GAAG,sBAAsB,GAAG,SAAS,CAExF;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI,CAE5E;AAED,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAOxD;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C;AAED,wBAAgB,iBAAiB,IAAI,sBAAsB,EAAE,CAE5D;AAED,wBAAgB,wBAAwB,IAAI,iBAAiB,EAAE,CAM9D;AAED,wBAAsB,iBAAiB,CACtC,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,gBAAgB,GAC3B,OAAO,CAAC,gBAAgB,CAAC,CAM3B;AAED,wBAAsB,cAAc,CACnC,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAC3C,OAAO,CAAC;IAAE,cAAc,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAkBtE","sourcesContent":["export { AnthropicOAuthProvider, loginAnthropic } from \"./anthropic.js\";\nexport {\n\tgetGitHubCopilotBaseUrl,\n\tgithubCopilotOAuthProvider,\n\tloginGitHubCopilot,\n\tnormalizeDomain,\n\trefreshGitHubCopilotToken,\n} from \"./github-copilot.js\";\nexport { createGoogleAntigravityProvider } from \"./google-antigravity.js\";\nexport { loginOpenAICodex, openaiCodexOAuthProvider, refreshOpenAICodexToken } from \"./openai-codex.js\";\nexport * from \"./types.js\";\n\nimport { AnthropicOAuthProvider } from \"./anthropic.js\";\nimport { githubCopilotOAuthProvider } from \"./github-copilot.js\";\nimport { openaiCodexOAuthProvider } from \"./openai-codex.js\";\nimport type { OAuthCredentials, OAuthProviderId, OAuthProviderInfo, OAuthProviderInterface } from \"./types.js\";\n\nconst BUILT_IN_OAUTH_PROVIDERS: OAuthProviderInterface[] = [\n\tAnthropicOAuthProvider,\n\tgithubCopilotOAuthProvider,\n\topenaiCodexOAuthProvider,\n];\n\nconst oauthProviderRegistry = new Map<string, OAuthProviderInterface>(\n\tBUILT_IN_OAUTH_PROVIDERS.map((provider) => [provider.id, provider]),\n);\n\nexport function getOAuthProvider(id: OAuthProviderId): OAuthProviderInterface | undefined {\n\treturn oauthProviderRegistry.get(id);\n}\n\nexport function registerOAuthProvider(provider: OAuthProviderInterface): void {\n\toauthProviderRegistry.set(provider.id, provider);\n}\n\nexport function unregisterOAuthProvider(id: string): void {\n\tconst builtInProvider = BUILT_IN_OAUTH_PROVIDERS.find((provider) => provider.id === id);\n\tif (builtInProvider) {\n\t\toauthProviderRegistry.set(id, builtInProvider);\n\t\treturn;\n\t}\n\toauthProviderRegistry.delete(id);\n}\n\nexport function resetOAuthProviders(): void {\n\toauthProviderRegistry.clear();\n\tfor (const provider of BUILT_IN_OAUTH_PROVIDERS) {\n\t\toauthProviderRegistry.set(provider.id, provider);\n\t}\n}\n\nexport function getOAuthProviders(): OAuthProviderInterface[] {\n\treturn Array.from(oauthProviderRegistry.values());\n}\n\nexport function getOAuthProviderInfoList(): OAuthProviderInfo[] {\n\treturn getOAuthProviders().map((p) => ({\n\t\tid: p.id,\n\t\tname: p.name,\n\t\tavailable: true,\n\t}));\n}\n\nexport async function refreshOAuthToken(\n\tproviderId: OAuthProviderId,\n\tcredentials: OAuthCredentials,\n): Promise<OAuthCredentials> {\n\tconst provider = getOAuthProvider(providerId);\n\tif (!provider) {\n\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t}\n\treturn provider.refreshToken(credentials);\n}\n\nexport async function getOAuthApiKey(\n\tproviderId: OAuthProviderId,\n\tcredentials: Record<string, OAuthCredentials>,\n): Promise<{ newCredentials: OAuthCredentials; apiKey: string } | null> {\n\tconst provider = getOAuthProvider(providerId);\n\tif (!provider) {\n\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t}\n\tlet creds = credentials[providerId];\n\tif (!creds) {\n\t\treturn null;\n\t}\n\tif (Date.now() >= creds.expires) {\n\t\ttry {\n\t\t\tcreds = await provider.refreshToken(creds);\n\t\t} catch (_error) {\n\t\t\tthrow new Error(`Failed to refresh OAuth token for ${providerId}`);\n\t\t}\n\t}\n\tconst apiKey = provider.getApiKey(creds);\n\treturn { newCredentials: creds, apiKey };\n}\n"]}
|