@dianshuv/copilot-api 0.21.6 → 0.21.8
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 +15 -2
- package/dist/main.mjs +35 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,6 +31,19 @@ npm install -g @dianshuv/copilot-api
|
|
|
31
31
|
copilot-api start
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
### GitHub Enterprise Cloud with data residency
|
|
35
|
+
|
|
36
|
+
Set the tenant hostname for both login and server startup. Omit
|
|
37
|
+
`COPILOT_GH_HOST` to use `github.com`.
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
COPILOT_GH_HOST=example.ghe.com npx @dianshuv/copilot-api login
|
|
41
|
+
COPILOT_GH_HOST=example.ghe.com npx @dianshuv/copilot-api start
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Only `github.com` and `*.ghe.com` hosts are accepted. OAuth uses the tenant web
|
|
45
|
+
host, GitHub REST calls use `api.<tenant>.ghe.com`, and the Copilot API endpoint
|
|
46
|
+
is discovered from the authenticated tenant.
|
|
34
47
|
|
|
35
48
|
## Development
|
|
36
49
|
|
|
@@ -67,7 +80,7 @@ make down
|
|
|
67
80
|
|
|
68
81
|
### Hidden Models
|
|
69
82
|
|
|
70
|
-
By default the proxy hides
|
|
83
|
+
By default the proxy hides 46 stale / duplicate / unused upstream model ids from
|
|
71
84
|
its **listing surfaces** (`/v1/models` and the startup banner).
|
|
72
85
|
|
|
73
86
|
**Important**: this is a *display* filter only. The remaining POST endpoints
|
|
@@ -88,7 +101,7 @@ Currently hidden (grouped):
|
|
|
88
101
|
- **Older / superseded GPT-5** — `gpt-5-mini`, `gpt-5.3-codex`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-nano`, `gpt-5.5`, `gpt-5.6-terra`
|
|
89
102
|
- **All embeddings** — `text-embedding-ada-002`, `text-embedding-3-small`, `text-embedding-3-small-inference`
|
|
90
103
|
- **Hidden Gemini** — `gemini-2.5-pro`, `gemini-3-flash-preview`, `gemini-3.1-pro-preview`, `gemini-3.5-flash`, `gemini-3.6-flash`, `gemini-3.7-flash`
|
|
91
|
-
- **Older Grok** — `grok-4.5`
|
|
104
|
+
- **Older Grok** — `grok-4.5`, `grok-4.6`
|
|
92
105
|
- **Older / variant Claude** — `claude-opus-4.5`, `claude-opus-4.6`, `claude-opus-4.7`, `claude-opus-4.8`, `claude-opus-4.7-high`, `claude-opus-4.7-xhigh`, `claude-sonnet-4.5`, `claude-sonnet-4.6`
|
|
93
106
|
- **Special-purpose** — `gpt-5.6-luna-utility`, `mai-code-1-flash-internal`, `mai-code-1-flash-picker`, `mai-code-1.1-flash`, `trajectory-compaction`
|
|
94
107
|
|
package/dist/main.mjs
CHANGED
|
@@ -135,6 +135,7 @@ function initProxyFromEnv() {
|
|
|
135
135
|
//#endregion
|
|
136
136
|
//#region src/lib/state.ts
|
|
137
137
|
const state = {
|
|
138
|
+
githubHost: "github.com",
|
|
138
139
|
accountType: "individual",
|
|
139
140
|
manualApprove: false,
|
|
140
141
|
showToken: false,
|
|
@@ -193,7 +194,26 @@ function copilotHeaders(state, visionOrOptions) {
|
|
|
193
194
|
if (options.vision) headers["copilot-vision-request"] = "true";
|
|
194
195
|
return headers;
|
|
195
196
|
}
|
|
196
|
-
const
|
|
197
|
+
const DEFAULT_GITHUB_HOST = "github.com";
|
|
198
|
+
const GHE_TENANT_HOST_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.ghe\.com$/;
|
|
199
|
+
function resolveGitHubHost(copilotGitHubHost) {
|
|
200
|
+
const configuredHost = copilotGitHubHost?.trim() || DEFAULT_GITHUB_HOST;
|
|
201
|
+
let hostUrl = configuredHost;
|
|
202
|
+
if (!configuredHost.includes("://")) hostUrl = `https://${configuredHost}`;
|
|
203
|
+
let parsed;
|
|
204
|
+
try {
|
|
205
|
+
parsed = new URL(hostUrl);
|
|
206
|
+
} catch {
|
|
207
|
+
throw new Error("GitHub host must be github.com or a .ghe.com tenant hostname.");
|
|
208
|
+
}
|
|
209
|
+
if (parsed.protocol !== "https:") throw new Error("GitHub host must use HTTPS.");
|
|
210
|
+
if (parsed.username || parsed.password || parsed.port || parsed.pathname !== "/" || parsed.search || parsed.hash) throw new Error("GitHub host must be a hostname without a path.");
|
|
211
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
212
|
+
if (hostname !== DEFAULT_GITHUB_HOST && !GHE_TENANT_HOST_PATTERN.test(hostname)) throw new Error("GitHub host must be github.com or a .ghe.com tenant.");
|
|
213
|
+
return hostname;
|
|
214
|
+
}
|
|
215
|
+
const githubBaseUrl = (state) => `https://${state.githubHost}`;
|
|
216
|
+
const githubApiBaseUrl = (state) => state.githubHost === "github.com" ? "https://api.github.com" : `https://api.${state.githubHost}`;
|
|
197
217
|
const githubHeaders = (state) => ({
|
|
198
218
|
accept: standardHeaders().accept,
|
|
199
219
|
authorization: `Bearer ${state.githubToken}`,
|
|
@@ -203,7 +223,6 @@ const githubOAuthHeaders = (state) => ({
|
|
|
203
223
|
...standardHeaders(),
|
|
204
224
|
"user-agent": userAgent(state)
|
|
205
225
|
});
|
|
206
|
-
const GITHUB_BASE_URL = "https://github.com";
|
|
207
226
|
const GITHUB_CLIENT_ID = "Ov23ctDVkRmgkPke0Mmm";
|
|
208
227
|
const GITHUB_APP_SCOPES = [
|
|
209
228
|
"read:user",
|
|
@@ -471,7 +490,7 @@ function forwardError(c, error) {
|
|
|
471
490
|
//#endregion
|
|
472
491
|
//#region src/services/github/get-device-code.ts
|
|
473
492
|
async function getDeviceCode() {
|
|
474
|
-
const response = await fetch(`${
|
|
493
|
+
const response = await fetch(`${githubBaseUrl(state)}/login/device/code`, {
|
|
475
494
|
method: "POST",
|
|
476
495
|
headers: githubOAuthHeaders(state),
|
|
477
496
|
body: JSON.stringify({
|
|
@@ -486,7 +505,7 @@ async function getDeviceCode() {
|
|
|
486
505
|
//#endregion
|
|
487
506
|
//#region src/services/github/get-user.ts
|
|
488
507
|
async function getGitHubUser() {
|
|
489
|
-
const response = await fetch(`${
|
|
508
|
+
const response = await fetch(`${githubApiBaseUrl(state)}/user`, { headers: githubHeaders(state) });
|
|
490
509
|
if (!response.ok) throw await HTTPError.fromResponse("Failed to get GitHub user", response);
|
|
491
510
|
return await response.json();
|
|
492
511
|
}
|
|
@@ -663,7 +682,7 @@ async function getCopilotCliVersion() {
|
|
|
663
682
|
//#endregion
|
|
664
683
|
//#region src/services/github/get-copilot-usage.ts
|
|
665
684
|
const getCopilotUsage = async () => {
|
|
666
|
-
const response = await fetch(`${
|
|
685
|
+
const response = await fetch(`${githubApiBaseUrl(state)}/copilot_internal/user`, { headers: githubHeaders(state) });
|
|
667
686
|
if (!response.ok) throw await HTTPError.fromResponse("Failed to get Copilot usage", response);
|
|
668
687
|
return await response.json();
|
|
669
688
|
};
|
|
@@ -715,6 +734,11 @@ const initCopilotIdentity = async () => {
|
|
|
715
734
|
* GitHub OAuth token at an arbitrary origin.
|
|
716
735
|
*/
|
|
717
736
|
const CAPI_HOST_PATTERN = /^api(?:\.[a-z0-9-]+)?\.githubcopilot\.com$/;
|
|
737
|
+
function isTrustedCopilotApiHost(hostname) {
|
|
738
|
+
if (CAPI_HOST_PATTERN.test(hostname)) return true;
|
|
739
|
+
if (!state.githubHost.endsWith(".ghe.com")) return false;
|
|
740
|
+
return hostname === state.githubHost || hostname.endsWith(`.${state.githubHost}`);
|
|
741
|
+
}
|
|
718
742
|
const bootstrapCopilotSession = async () => {
|
|
719
743
|
const usage = await getCopilotUsage();
|
|
720
744
|
if (usage === null || typeof usage !== "object") throw new Error("bootstrapCopilotSession: /copilot_internal/user body was not an object.");
|
|
@@ -730,7 +754,7 @@ const bootstrapCopilotSession = async () => {
|
|
|
730
754
|
}
|
|
731
755
|
if (parsed.protocol !== "https:") throw new Error(`bootstrapCopilotSession: endpoints.api must use https:// (got ${parsed.protocol}).`);
|
|
732
756
|
if (parsed.port !== "") throw new Error(`bootstrapCopilotSession: endpoints.api must use the default https port (got :${parsed.port}).`);
|
|
733
|
-
if (!
|
|
757
|
+
if (!isTrustedCopilotApiHost(parsed.hostname)) throw new Error(`bootstrapCopilotSession: endpoints.api host ${parsed.hostname} is not a trusted Copilot API host for ${state.githubHost}.`);
|
|
734
758
|
state.copilotApiEndpoint = parsed.origin;
|
|
735
759
|
state.copilotToken = state.githubToken;
|
|
736
760
|
};
|
|
@@ -742,7 +766,7 @@ async function pollAccessToken(deviceCode) {
|
|
|
742
766
|
consola.debug(`Polling access token with interval of ${sleepDuration}ms`);
|
|
743
767
|
const expiresAt = Date.now() + deviceCode.expires_in * 1e3;
|
|
744
768
|
while (Date.now() < expiresAt) {
|
|
745
|
-
const response = await fetch(`${
|
|
769
|
+
const response = await fetch(`${githubBaseUrl(state)}/login/oauth/access_token`, {
|
|
746
770
|
method: "POST",
|
|
747
771
|
headers: githubOAuthHeaders(state),
|
|
748
772
|
body: JSON.stringify({
|
|
@@ -997,7 +1021,7 @@ async function runLogout() {
|
|
|
997
1021
|
}
|
|
998
1022
|
}
|
|
999
1023
|
consola.warn("This only removes the local token file. The OAuth App authorization is still active on GitHub and the token remains valid until revoked.");
|
|
1000
|
-
consola.info(`To fully revoke access, visit
|
|
1024
|
+
consola.info(`To fully revoke access, visit ${githubBaseUrl(state)}/settings/connections/applications/${GITHUB_CLIENT_ID} and click "Revoke access".`);
|
|
1001
1025
|
}
|
|
1002
1026
|
const logout = defineCommand({
|
|
1003
1027
|
meta: {
|
|
@@ -1011,7 +1035,7 @@ const logout = defineCommand({
|
|
|
1011
1035
|
|
|
1012
1036
|
//#endregion
|
|
1013
1037
|
//#region package.json
|
|
1014
|
-
var version = "0.21.
|
|
1038
|
+
var version = "0.21.8";
|
|
1015
1039
|
|
|
1016
1040
|
//#endregion
|
|
1017
1041
|
//#region src/lib/event-loop-lag.ts
|
|
@@ -2507,6 +2531,7 @@ const HIDDEN_MODEL_IDS = new Set([
|
|
|
2507
2531
|
"gemini-3.6-flash",
|
|
2508
2532
|
"gemini-3.7-flash",
|
|
2509
2533
|
"grok-4.5",
|
|
2534
|
+
"grok-4.6",
|
|
2510
2535
|
"claude-opus-4.5",
|
|
2511
2536
|
"claude-opus-4.6",
|
|
2512
2537
|
"claude-opus-4.7",
|
|
@@ -9472,6 +9497,7 @@ const start = defineCommand({
|
|
|
9472
9497
|
//#endregion
|
|
9473
9498
|
//#region src/main.ts
|
|
9474
9499
|
consola.options.formatOptions.date = false;
|
|
9500
|
+
state.githubHost = resolveGitHubHost(process.env.COPILOT_GH_HOST);
|
|
9475
9501
|
await runMain(defineCommand({
|
|
9476
9502
|
meta: {
|
|
9477
9503
|
name: "copilot-api",
|