33 */
44export async function CopilotAuthPlugin ( { client } ) {
55 const CLIENT_ID = "Iv1.b507a08c87ecfe98" ;
6- const DEVICE_CODE_URL = "https://github.com/login/device/code" ;
7- const ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token" ;
8- const COPILOT_API_KEY_URL =
9- "https://api.github.com/copilot_internal/v2/token" ;
106 const HEADERS = {
117 "User-Agent" : "GitHubCopilotChat/0.35.0" ,
128 "Editor-Version" : "vscode/1.99.3" ,
139 "Editor-Plugin-Version" : "copilot-chat/0.35.0" ,
1410 "Copilot-Integration-Id" : "vscode-chat" ,
1511 } ;
1612
13+ function normalizeDomain ( url ) {
14+ return url
15+ . replace ( / ^ h t t p s ? : \/ \/ / , "" )
16+ . replace ( / \/ $ / , "" ) ;
17+ }
18+
19+ function getUrls ( domain ) {
20+ return {
21+ DEVICE_CODE_URL : `https://${ domain } /login/device/code` ,
22+ ACCESS_TOKEN_URL : `https://${ domain } /login/oauth/access_token` ,
23+ COPILOT_API_KEY_URL : `https://api.${ domain } /copilot_internal/v2/token` ,
24+ } ;
25+ }
26+
1727 return {
1828 auth : {
1929 provider : "github-copilot" ,
@@ -30,13 +40,25 @@ export async function CopilotAuthPlugin({ client }) {
3040 }
3141 }
3242
43+ // Set baseURL based on deployment type
44+ const enterpriseUrl = info . enterpriseUrl ;
45+ const baseURL = enterpriseUrl
46+ ? `https://copilot-api.${ normalizeDomain ( enterpriseUrl ) } `
47+ : "https://api.githubcopilot.com" ;
48+
3349 return {
50+ baseURL,
3451 apiKey : "" ,
3552 async fetch ( input , init ) {
3653 const info = await getAuth ( ) ;
3754 if ( info . type !== "oauth" ) return { } ;
3855 if ( ! info . access || info . expires < Date . now ( ) ) {
39- const response = await fetch ( COPILOT_API_KEY_URL , {
56+ const domain = info . enterpriseUrl
57+ ? normalizeDomain ( info . enterpriseUrl )
58+ : "github.com" ;
59+ const urls = getUrls ( domain ) ;
60+
61+ const response = await fetch ( urls . COPILOT_API_KEY_URL , {
4062 headers : {
4163 Accept : "application/json" ,
4264 Authorization : `Bearer ${ info . refresh } ` ,
@@ -48,15 +70,19 @@ export async function CopilotAuthPlugin({ client }) {
4870
4971 const tokenData = await response . json ( ) ;
5072
73+ const saveProviderID = info . enterpriseUrl
74+ ? "github-copilot-enterprise"
75+ : "github-copilot" ;
5176 await client . auth . set ( {
5277 path : {
53- id : provider . id ,
78+ id : saveProviderID ,
5479 } ,
5580 body : {
5681 type : "oauth" ,
5782 refresh : info . refresh ,
5883 access : tokenData . token ,
5984 expires : tokenData . expires_at * 1000 ,
85+ ...( info . enterpriseUrl && { enterpriseUrl : info . enterpriseUrl } ) ,
6086 } ,
6187 } ) ;
6288 info . access = tokenData . token ;
@@ -99,10 +125,62 @@ export async function CopilotAuthPlugin({ client }) {
99125 } ,
100126 methods : [
101127 {
102- label : "Login with GitHub" ,
103128 type : "oauth" ,
104- authorize : async ( ) => {
105- const deviceResponse = await fetch ( DEVICE_CODE_URL , {
129+ label : "Login with GitHub Copilot" ,
130+ prompts : [
131+ {
132+ type : "select" ,
133+ key : "deploymentType" ,
134+ message : "Select GitHub deployment type" ,
135+ options : [
136+ {
137+ label : "GitHub.com" ,
138+ value : "github.com" ,
139+ hint : "Public" ,
140+ } ,
141+ {
142+ label : "GitHub Enterprise" ,
143+ value : "enterprise" ,
144+ hint : "Data residency or self-hosted" ,
145+ } ,
146+ ] ,
147+ } ,
148+ {
149+ type : "text" ,
150+ key : "enterpriseUrl" ,
151+ message : "Enter your GitHub Enterprise URL or domain" ,
152+ placeholder : "company.ghe.com or https://company.ghe.com" ,
153+ condition : ( inputs ) => inputs . deploymentType === "enterprise" ,
154+ validate : ( value ) => {
155+ if ( ! value ) return "URL or domain is required" ;
156+ try {
157+ const url = value . includes ( "://" )
158+ ? new URL ( value )
159+ : new URL ( `https://${ value } ` ) ;
160+ if ( ! url . hostname )
161+ return "Please enter a valid URL or domain" ;
162+ return undefined ;
163+ } catch {
164+ return "Please enter a valid URL (e.g., company.ghe.com or https://company.ghe.com)" ;
165+ }
166+ } ,
167+ } ,
168+ ] ,
169+ async authorize ( inputs = { } ) {
170+ const deploymentType = inputs . deploymentType || "github.com" ;
171+
172+ let domain = "github.com" ;
173+ let actualProvider = "github-copilot" ;
174+
175+ if ( deploymentType === "enterprise" ) {
176+ const enterpriseUrl = inputs . enterpriseUrl ;
177+ domain = normalizeDomain ( enterpriseUrl ) ;
178+ actualProvider = "github-copilot-enterprise" ;
179+ }
180+
181+ const urls = getUrls ( domain ) ;
182+
183+ const deviceResponse = await fetch ( urls . DEVICE_CODE_URL , {
106184 method : "POST" ,
107185 headers : {
108186 Accept : "application/json" ,
@@ -114,14 +192,20 @@ export async function CopilotAuthPlugin({ client }) {
114192 scope : "read:user" ,
115193 } ) ,
116194 } ) ;
195+
196+ if ( ! deviceResponse . ok ) {
197+ throw new Error ( "Failed to initiate device authorization" ) ;
198+ }
199+
117200 const deviceData = await deviceResponse . json ( ) ;
201+
118202 return {
119203 url : deviceData . verification_uri ,
120204 instructions : `Enter code: ${ deviceData . user_code } ` ,
121205 method : "auto" ,
122206 callback : async ( ) => {
123207 while ( true ) {
124- const response = await fetch ( ACCESS_TOKEN_URL , {
208+ const response = await fetch ( urls . ACCESS_TOKEN_URL , {
125209 method : "POST" ,
126210 headers : {
127211 Accept : "application/json" ,
@@ -141,12 +225,19 @@ export async function CopilotAuthPlugin({ client }) {
141225 const data = await response . json ( ) ;
142226
143227 if ( data . access_token ) {
144- return {
228+ const result = {
145229 type : "success" ,
146230 refresh : data . access_token ,
147231 access : "" ,
148232 expires : 0 ,
149233 } ;
234+
235+ if ( actualProvider === "github-copilot-enterprise" ) {
236+ result . provider = "github-copilot-enterprise" ;
237+ result . enterpriseUrl = domain ;
238+ }
239+
240+ return result ;
150241 }
151242
152243 if ( data . error === "authorization_pending" ) {
0 commit comments