dsh-github-copilot 0.4.0-alpha.30 → 0.4.0-alpha.32
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/AGENTS.md +3 -1
- package/CHANGELOG.md +14 -0
- package/README.md +16 -7
- package/README.zh.md +16 -7
- package/deployment-baseline.json +40 -2
- package/docs/copilot-usage.md +47 -0
- package/docs/official-first-016-alpha2.md +6 -0
- package/lib/client.js +665 -42
- package/lib/client.js.map +1 -1
- package/lib/index.js +946 -436
- package/lib/remote.js +94 -2
- package/lib/routed-web.js +1 -1
- package/lib/types/account-model-auth.d.ts +1 -1
- package/lib/types/client.d.ts +1 -0
- package/lib/types/copilot-usage-card.d.ts +28 -0
- package/lib/types/copilot-usage-host.d.ts +48 -0
- package/lib/types/copilot-usage-normalize.d.ts +9 -0
- package/lib/types/copilot-usage-remote.d.ts +60 -0
- package/lib/types/copilot-usage-types.d.ts +19 -0
- package/lib/types/copilot-usage-ui.d.ts +4 -0
- package/lib/types/external-link.d.ts +3 -0
- package/lib/types/preview-provider.d.ts +2 -0
- package/package.json +2 -1
- package/lib/{search-routing-pFLux0W7.js → search-routing-DqLKem1c.js} +2 -2
package/lib/index.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import { C as currentSearchInitiator, D as GITHUB_COPILOT_CREDENTIAL_KEY, E as readCopilotCatalog, O as GITHUB_COPILOT_PREVIEW_PROVIDER_ID, S as currentChatRoute, T as isPluginPreviewProvider, _ as RESPONSES_WEB_SEARCH_TOOL_TYPE, a as COPILOT_HOSTED_SEARCH_PROVIDER_ID, b as candidatesForRoute, c as abortable$1, d as readBounded, f as applyRequestAuth, h as ANTHROPIC_WEB_SEARCH_TOOL_TYPE, i as describeSearchBackend, k as GITHUB_COPILOT_PROVIDER_ID, l as isAbortError, m as providerRequestHeaders, n as routeSessionSearch, o as GITHUB_COPILOT_HOSTED_SEARCH_PROVIDER_ID, p as normalizeRequestAuth, r as DescribedSearchFallbackError, s as createTraditionalSearchProvider, t as isCopilotSearchSelection, u as providerErrorMessage, v as SearchPlan, w as currentSearchSelection, x as sameCandidates, y as WEB_SEARCH_TOOL_TYPE } from "./search-routing-
|
|
1
|
+
import { C as currentSearchInitiator, D as GITHUB_COPILOT_CREDENTIAL_KEY, E as readCopilotCatalog, O as GITHUB_COPILOT_PREVIEW_PROVIDER_ID, S as currentChatRoute, T as isPluginPreviewProvider, _ as RESPONSES_WEB_SEARCH_TOOL_TYPE, a as COPILOT_HOSTED_SEARCH_PROVIDER_ID, b as candidatesForRoute, c as abortable$1, d as readBounded, f as applyRequestAuth, h as ANTHROPIC_WEB_SEARCH_TOOL_TYPE, i as describeSearchBackend, k as GITHUB_COPILOT_PROVIDER_ID, l as isAbortError, m as providerRequestHeaders, n as routeSessionSearch, o as GITHUB_COPILOT_HOSTED_SEARCH_PROVIDER_ID, p as normalizeRequestAuth, r as DescribedSearchFallbackError, s as createTraditionalSearchProvider, t as isCopilotSearchSelection, u as providerErrorMessage, v as SearchPlan, w as currentSearchSelection, x as sameCandidates, y as WEB_SEARCH_TOOL_TYPE } from "./search-routing-DqLKem1c.js";
|
|
2
2
|
import AuthorizationService from "@deepseek-ai/dsh-authorization";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import * as dshScope from "@deepseek-ai/dsh-scope";
|
|
5
5
|
import { Remote, RemoteError, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
6
6
|
import z from "@deepseek-ai/schemastery";
|
|
7
7
|
import { z as z$1 } from "zod";
|
|
8
|
+
import { createModels, getSupportedThinkingLevels, hasApi, lazyStream } from "@earendil-works/pi-ai";
|
|
9
|
+
import { githubCopilotProvider } from "@earendil-works/pi-ai/providers/github-copilot";
|
|
10
|
+
import { getBuiltinModels } from "@earendil-works/pi-ai/providers/all";
|
|
8
11
|
import * as dshLlm from "@deepseek-ai/dsh-llm";
|
|
9
12
|
import { CONTEXT_WINDOW_EXCEEDED_CODE, LlmError, ReasoningEffortId, attributionHeaders, callConfigEquals, contentHasImage, isAgentLoopRequest, resolveImageAttachmentAccess, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
10
13
|
import { WebError } from "@deepseek-ai/dsh-web";
|
|
11
14
|
import * as dshSettings from "@deepseek-ai/dsh-settings";
|
|
12
|
-
import { getBuiltinModels } from "@earendil-works/pi-ai/providers/all";
|
|
13
|
-
import { createModels, getSupportedThinkingLevels, hasApi, lazyStream } from "@earendil-works/pi-ai";
|
|
14
15
|
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
15
|
-
import { githubCopilotProvider } from "@earendil-works/pi-ai/providers/github-copilot";
|
|
16
16
|
import { Config as Config$1, PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
17
17
|
import { estimateContextTokens, estimateMessageTokens } from "@earendil-works/pi-ai/utils/estimate";
|
|
18
18
|
import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "@earendil-works/pi-ai/api/github-copilot-headers";
|
|
19
19
|
//#region lib/types/dual-model-host.js
|
|
20
20
|
/** Optional plugin-owned planner/executor sessions; no Core/default-model mutation. */
|
|
21
|
-
var __runInitializers$
|
|
21
|
+
var __runInitializers$3 = function(thisArg, initializers, value) {
|
|
22
22
|
var useValue = arguments.length > 2;
|
|
23
23
|
for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
24
24
|
return useValue ? value : void 0;
|
|
25
25
|
};
|
|
26
|
-
var __esDecorate$
|
|
26
|
+
var __esDecorate$3 = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
27
27
|
function accept(f) {
|
|
28
28
|
if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
|
|
29
29
|
return f;
|
|
@@ -305,7 +305,7 @@ let GitHubCopilotDualModel = (() => {
|
|
|
305
305
|
_view_decorators = [Remote];
|
|
306
306
|
_save_decorators = [Remote];
|
|
307
307
|
_create_decorators = [Remote];
|
|
308
|
-
__esDecorate$
|
|
308
|
+
__esDecorate$3(this, null, _view_decorators, {
|
|
309
309
|
kind: "method",
|
|
310
310
|
name: "view",
|
|
311
311
|
static: false,
|
|
@@ -316,7 +316,7 @@ let GitHubCopilotDualModel = (() => {
|
|
|
316
316
|
},
|
|
317
317
|
metadata: _metadata
|
|
318
318
|
}, null, _instanceExtraInitializers);
|
|
319
|
-
__esDecorate$
|
|
319
|
+
__esDecorate$3(this, null, _save_decorators, {
|
|
320
320
|
kind: "method",
|
|
321
321
|
name: "save",
|
|
322
322
|
static: false,
|
|
@@ -327,7 +327,7 @@ let GitHubCopilotDualModel = (() => {
|
|
|
327
327
|
},
|
|
328
328
|
metadata: _metadata
|
|
329
329
|
}, null, _instanceExtraInitializers);
|
|
330
|
-
__esDecorate$
|
|
330
|
+
__esDecorate$3(this, null, _create_decorators, {
|
|
331
331
|
kind: "method",
|
|
332
332
|
name: "create",
|
|
333
333
|
static: false,
|
|
@@ -345,7 +345,7 @@ let GitHubCopilotDualModel = (() => {
|
|
|
345
345
|
value: _metadata
|
|
346
346
|
});
|
|
347
347
|
}
|
|
348
|
-
owner = __runInitializers$
|
|
348
|
+
owner = __runInitializers$3(this, _instanceExtraInitializers);
|
|
349
349
|
lifetime = new AbortController();
|
|
350
350
|
creates = /* @__PURE__ */ new Map();
|
|
351
351
|
overlays = /* @__PURE__ */ new Map();
|
|
@@ -857,72 +857,847 @@ let GitHubCopilotDualModel = (() => {
|
|
|
857
857
|
throw error;
|
|
858
858
|
}
|
|
859
859
|
}
|
|
860
|
-
};
|
|
861
|
-
})();
|
|
862
|
-
//#endregion
|
|
863
|
-
//#region lib/types/search-routing-remote.js
|
|
864
|
-
const SearchProviderCatalogSchema = z$1.object({
|
|
865
|
-
supported: z$1.boolean(),
|
|
866
|
-
providers: z$1.array(z$1.object({ id: z$1.string().min(1).max(512) }).strict()).max(512)
|
|
867
|
-
}).strict();
|
|
868
|
-
//#endregion
|
|
869
|
-
//#region lib/types/search-routing-host.js
|
|
870
|
-
var __runInitializers$
|
|
871
|
-
var useValue = arguments.length > 2;
|
|
872
|
-
for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
873
|
-
return useValue ? value : void 0;
|
|
874
|
-
};
|
|
875
|
-
var __esDecorate$
|
|
876
|
-
function accept(f) {
|
|
877
|
-
if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
|
|
878
|
-
return f;
|
|
860
|
+
};
|
|
861
|
+
})();
|
|
862
|
+
//#endregion
|
|
863
|
+
//#region lib/types/search-routing-remote.js
|
|
864
|
+
const SearchProviderCatalogSchema = z$1.object({
|
|
865
|
+
supported: z$1.boolean(),
|
|
866
|
+
providers: z$1.array(z$1.object({ id: z$1.string().min(1).max(512) }).strict()).max(512)
|
|
867
|
+
}).strict();
|
|
868
|
+
//#endregion
|
|
869
|
+
//#region lib/types/search-routing-host.js
|
|
870
|
+
var __runInitializers$2 = function(thisArg, initializers, value) {
|
|
871
|
+
var useValue = arguments.length > 2;
|
|
872
|
+
for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
873
|
+
return useValue ? value : void 0;
|
|
874
|
+
};
|
|
875
|
+
var __esDecorate$2 = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
876
|
+
function accept(f) {
|
|
877
|
+
if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
|
|
878
|
+
return f;
|
|
879
|
+
}
|
|
880
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
881
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
882
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
883
|
+
var _, done = false;
|
|
884
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
885
|
+
var context = {};
|
|
886
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
887
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
888
|
+
context.addInitializer = function(f) {
|
|
889
|
+
if (done) throw new TypeError("Cannot add initializers after decoration has completed");
|
|
890
|
+
extraInitializers.push(accept(f || null));
|
|
891
|
+
};
|
|
892
|
+
var result = (0, decorators[i])(kind === "accessor" ? {
|
|
893
|
+
get: descriptor.get,
|
|
894
|
+
set: descriptor.set
|
|
895
|
+
} : descriptor[key], context);
|
|
896
|
+
if (kind === "accessor") {
|
|
897
|
+
if (result === void 0) continue;
|
|
898
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
899
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
900
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
901
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
902
|
+
} else if (_ = accept(result)) {
|
|
903
|
+
if (kind === "field") initializers.unshift(_);
|
|
904
|
+
else descriptor[key] = _;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
908
|
+
done = true;
|
|
909
|
+
};
|
|
910
|
+
let SearchRoutingController = (() => {
|
|
911
|
+
let _classSuper = TypertRemoteService;
|
|
912
|
+
let _instanceExtraInitializers = [];
|
|
913
|
+
let _providers_decorators;
|
|
914
|
+
return class SearchRoutingController extends _classSuper {
|
|
915
|
+
static {
|
|
916
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
917
|
+
_providers_decorators = [Remote];
|
|
918
|
+
__esDecorate$2(this, null, _providers_decorators, {
|
|
919
|
+
kind: "method",
|
|
920
|
+
name: "providers",
|
|
921
|
+
static: false,
|
|
922
|
+
private: false,
|
|
923
|
+
access: {
|
|
924
|
+
has: (obj) => "providers" in obj,
|
|
925
|
+
get: (obj) => obj.providers
|
|
926
|
+
},
|
|
927
|
+
metadata: _metadata
|
|
928
|
+
}, null, _instanceExtraInitializers);
|
|
929
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, {
|
|
930
|
+
enumerable: true,
|
|
931
|
+
configurable: true,
|
|
932
|
+
writable: true,
|
|
933
|
+
value: _metadata
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
constructor(ctx) {
|
|
937
|
+
super(ctx, "githubCopilotSearchRouting");
|
|
938
|
+
__runInitializers$2(this, _instanceExtraInitializers);
|
|
939
|
+
}
|
|
940
|
+
providers() {
|
|
941
|
+
const unavailable = {
|
|
942
|
+
supported: false,
|
|
943
|
+
providers: []
|
|
944
|
+
};
|
|
945
|
+
const catalog = this.ctx.get("githubCopilotSearchCatalog");
|
|
946
|
+
if (typeof catalog?.list !== "function") return unavailable;
|
|
947
|
+
try {
|
|
948
|
+
const ids = catalog.list();
|
|
949
|
+
const parsed = SearchProviderCatalogSchema.safeParse({
|
|
950
|
+
supported: true,
|
|
951
|
+
providers: ids.map((id) => ({ id }))
|
|
952
|
+
});
|
|
953
|
+
if (!parsed.success || new Set(ids).size !== ids.length) return unavailable;
|
|
954
|
+
parsed.data.providers.sort((a, b) => a.id.localeCompare(b.id));
|
|
955
|
+
return parsed.data;
|
|
956
|
+
} catch {
|
|
957
|
+
return unavailable;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
})();
|
|
962
|
+
//#endregion
|
|
963
|
+
//#region lib/types/copilot-grant.js
|
|
964
|
+
function nonEmptyOAuthString(value, field) {
|
|
965
|
+
if (typeof value !== "string" || value.trim().length === 0) throw new Error(`github-copilot: OAuth credential field "${field}" must be a non-empty string`);
|
|
966
|
+
return value;
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Rebuild pi-ai's GitHub Copilot grant as the exact JSON-safe provider shape.
|
|
970
|
+
* Prototype and unrelated extension members are intentionally not preserved.
|
|
971
|
+
*/
|
|
972
|
+
function normalizeGitHubCopilotOAuthCredential(credential) {
|
|
973
|
+
if (typeof credential !== "object" || credential === null) throw new Error("github-copilot: OAuth credential must be an object");
|
|
974
|
+
if (Reflect.get(credential, "type") !== "oauth") throw new Error("github-copilot: OAuth credential type must be \"oauth\"");
|
|
975
|
+
const refresh = nonEmptyOAuthString(Reflect.get(credential, "refresh"), "refresh");
|
|
976
|
+
const access = nonEmptyOAuthString(Reflect.get(credential, "access"), "access");
|
|
977
|
+
const expires = Reflect.get(credential, "expires");
|
|
978
|
+
if (typeof expires !== "number" || !Number.isFinite(expires)) throw new Error("github-copilot: OAuth credential field \"expires\" must be a finite number");
|
|
979
|
+
const enterpriseUrl = Reflect.get(credential, "enterpriseUrl");
|
|
980
|
+
if (enterpriseUrl !== void 0 && (typeof enterpriseUrl !== "string" || enterpriseUrl.trim().length === 0)) throw new Error("github-copilot: OAuth credential field \"enterpriseUrl\" must be a non-empty string");
|
|
981
|
+
const availableModelIds = Reflect.get(credential, "availableModelIds");
|
|
982
|
+
if (availableModelIds !== void 0 && (!Array.isArray(availableModelIds) || !availableModelIds.every((modelId) => typeof modelId === "string" && modelId.trim().length > 0))) throw new Error("github-copilot: OAuth credential field \"availableModelIds\" must be an array of non-empty strings");
|
|
983
|
+
return {
|
|
984
|
+
type: "oauth",
|
|
985
|
+
refresh,
|
|
986
|
+
access,
|
|
987
|
+
expires,
|
|
988
|
+
...enterpriseUrl === void 0 ? {} : { enterpriseUrl },
|
|
989
|
+
...availableModelIds === void 0 ? {} : { availableModelIds: [...new Set(availableModelIds)] }
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
//#endregion
|
|
993
|
+
//#region lib/types/copilot-auth.js
|
|
994
|
+
/**
|
|
995
|
+
* Provider-side GitHub Copilot auth resolution over the same credential record
|
|
996
|
+
* that DSH's built-in llm-pi-ai adapter owns.
|
|
997
|
+
*/
|
|
998
|
+
function credentialService(ctx) {
|
|
999
|
+
const candidate = ctx.get("credentials");
|
|
1000
|
+
if (typeof candidate !== "object" || candidate === null) throw new Error("github-copilot: DSH credentials service is unavailable");
|
|
1001
|
+
for (const method of [
|
|
1002
|
+
"readRecord",
|
|
1003
|
+
"listRecords",
|
|
1004
|
+
"modifyRecord",
|
|
1005
|
+
"deleteRecord"
|
|
1006
|
+
]) if (typeof Reflect.get(candidate, method) !== "function") throw new Error(`github-copilot: required DSH credentials API "credentials.${method}" is unavailable`);
|
|
1007
|
+
return candidate;
|
|
1008
|
+
}
|
|
1009
|
+
function toCredential(record) {
|
|
1010
|
+
if (record === void 0) return void 0;
|
|
1011
|
+
if (record.kind === "api-key") return {
|
|
1012
|
+
type: "api_key",
|
|
1013
|
+
...record.key === void 0 ? {} : { key: record.key },
|
|
1014
|
+
...record.env === void 0 ? {} : { env: { ...record.env } }
|
|
1015
|
+
};
|
|
1016
|
+
if (typeof record.payload !== "object" || record.payload === null) throw new Error("github-copilot: stored llm-pi-ai GitHub Copilot grant is not an object");
|
|
1017
|
+
return normalizeGitHubCopilotOAuthCredential(record.payload);
|
|
1018
|
+
}
|
|
1019
|
+
function toRecord(credential) {
|
|
1020
|
+
if (credential.type === "api_key") return {
|
|
1021
|
+
kind: "api-key",
|
|
1022
|
+
...credential.key === void 0 ? {} : { key: credential.key },
|
|
1023
|
+
...credential.env === void 0 ? {} : { env: { ...credential.env } }
|
|
1024
|
+
};
|
|
1025
|
+
return {
|
|
1026
|
+
kind: "grant",
|
|
1027
|
+
payload: normalizeGitHubCopilotOAuthCredential(credential)
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
/** Bind exactly one logical route to the canonical Host-owned credential record. */
|
|
1031
|
+
function createGitHubCopilotCredentialStore(ctx, logicalProviderId = GITHUB_COPILOT_PROVIDER_ID) {
|
|
1032
|
+
const assertProvider = (providerId) => {
|
|
1033
|
+
if (providerId !== logicalProviderId) throw new Error("COPILOT_CREDENTIAL_PROVIDER_MISMATCH");
|
|
1034
|
+
};
|
|
1035
|
+
return {
|
|
1036
|
+
read: async (providerId) => {
|
|
1037
|
+
assertProvider(providerId);
|
|
1038
|
+
return toCredential(await credentialService(ctx).readRecord(GITHUB_COPILOT_CREDENTIAL_KEY));
|
|
1039
|
+
},
|
|
1040
|
+
list: async () => {
|
|
1041
|
+
const record = await credentialService(ctx).readRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
|
|
1042
|
+
return record === void 0 ? [] : [{
|
|
1043
|
+
providerId: logicalProviderId,
|
|
1044
|
+
type: record.kind === "api-key" ? "api_key" : "oauth"
|
|
1045
|
+
}];
|
|
1046
|
+
},
|
|
1047
|
+
modify: async (providerId, mutate) => {
|
|
1048
|
+
assertProvider(providerId);
|
|
1049
|
+
return toCredential(await credentialService(ctx).modifyRecord(GITHUB_COPILOT_CREDENTIAL_KEY, async (current) => {
|
|
1050
|
+
const next = await mutate(toCredential(current));
|
|
1051
|
+
return next === void 0 ? void 0 : toRecord(next);
|
|
1052
|
+
}));
|
|
1053
|
+
},
|
|
1054
|
+
delete: async (providerId) => {
|
|
1055
|
+
assertProvider(providerId);
|
|
1056
|
+
await credentialService(ctx).deleteRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Build one resolver whose pi-ai collection refreshes OAuth credentials inside
|
|
1062
|
+
* the DSH record's serialized modify operation before exposing request auth.
|
|
1063
|
+
*/
|
|
1064
|
+
function createGitHubCopilotTokenResolver(ctx, onCredentialChanged) {
|
|
1065
|
+
const models = createModels({ credentials: createGitHubCopilotCredentialStore(ctx) });
|
|
1066
|
+
const provider = githubCopilotProvider();
|
|
1067
|
+
if (provider.auth.oauth === void 0) throw new Error("github-copilot: native OAuth method is unavailable");
|
|
1068
|
+
models.setProvider({
|
|
1069
|
+
...provider,
|
|
1070
|
+
auth: { oauth: provider.auth.oauth }
|
|
1071
|
+
});
|
|
1072
|
+
return async (modelId) => {
|
|
1073
|
+
const snapshot = readCopilotCatalog(ctx);
|
|
1074
|
+
const native = new Map(snapshot.models.map((model) => [model.id, model])).get(modelId);
|
|
1075
|
+
if (native === void 0) throw new Error(`github-copilot: pi-ai catalog has no GitHub Copilot model "${modelId}"`);
|
|
1076
|
+
const resolved = await models.getAuth("github-copilot");
|
|
1077
|
+
if (resolved === void 0) return void 0;
|
|
1078
|
+
if (onCredentialChanged !== void 0) try {
|
|
1079
|
+
await onCredentialChanged();
|
|
1080
|
+
} catch (error) {
|
|
1081
|
+
ctx.logger.warn("github-copilot: provider route reconciliation will retry after a later auth resolution");
|
|
1082
|
+
ctx.logger.warn(error);
|
|
1083
|
+
}
|
|
1084
|
+
const stored = await credentialService(ctx).readRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
|
|
1085
|
+
if (!(stored?.kind === "grant" && (normalizeGitHubCopilotOAuthCredential(stored.payload).availableModelIds ?? []).includes(modelId))) throw new Error(`github-copilot: model "${modelId}" is not available for the signed-in Copilot account`);
|
|
1086
|
+
const apiKey = resolved.auth.apiKey;
|
|
1087
|
+
if (apiKey === void 0) return void 0;
|
|
1088
|
+
return {
|
|
1089
|
+
apiKey,
|
|
1090
|
+
baseURL: trustedCopilotBaseUrl(resolved.auth.baseUrl ?? native.baseUrl, enterpriseDomainOf(stored)),
|
|
1091
|
+
...resolved.auth.headers === void 0 ? {} : { headers: resolved.auth.headers }
|
|
1092
|
+
};
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
function enterpriseDomainOf(record) {
|
|
1096
|
+
if (record?.kind !== "grant" || typeof record.payload !== "object" || record.payload === null) return void 0;
|
|
1097
|
+
const value = Reflect.get(record.payload, "enterpriseUrl");
|
|
1098
|
+
if (typeof value !== "string" || value.trim().length === 0) return void 0;
|
|
1099
|
+
try {
|
|
1100
|
+
return (value.includes("://") ? new URL(value) : new URL(`https://${value}`)).hostname.toLowerCase();
|
|
1101
|
+
} catch {
|
|
1102
|
+
throw new Error("github-copilot: stored enterpriseUrl is invalid");
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
/** Validate an OAuth-derived endpoint without disclosing its value or credential payload. */
|
|
1106
|
+
function trustedGitHubCopilotBaseUrl(value, grant) {
|
|
1107
|
+
return trustedCopilotBaseUrl(value, enterpriseDomainOf({
|
|
1108
|
+
kind: "grant",
|
|
1109
|
+
payload: normalizeGitHubCopilotOAuthCredential(grant)
|
|
1110
|
+
}));
|
|
1111
|
+
}
|
|
1112
|
+
function trustedCopilotBaseUrl(value, enterpriseDomain) {
|
|
1113
|
+
if (value === void 0) throw new Error("github-copilot: pi-ai resolved no Copilot API base URL");
|
|
1114
|
+
const url = new URL(value);
|
|
1115
|
+
const hostname = url.hostname.toLowerCase();
|
|
1116
|
+
const githubHosted = /^api(?:\.[a-z0-9-]+)+\.githubcopilot\.com$/u.test(hostname);
|
|
1117
|
+
const enterpriseHosted = enterpriseDomain !== void 0 && hostname === `copilot-api.${enterpriseDomain}`;
|
|
1118
|
+
if (!(url.protocol === "https:" && (githubHosted || enterpriseHosted)) || url.username.length > 0 || url.password.length > 0) throw new Error("github-copilot: pi-ai resolved an untrusted Copilot API base URL");
|
|
1119
|
+
return url.origin;
|
|
1120
|
+
}
|
|
1121
|
+
//#endregion
|
|
1122
|
+
//#region lib/types/account-model-auth.js
|
|
1123
|
+
/** Native getAuth renews within five minutes; reserve another thirty seconds for metadata preparation. */
|
|
1124
|
+
const ACCOUNT_MODEL_AUTH_MIN_VALIDITY_MS = 33e4;
|
|
1125
|
+
/** An opaque, process-local account key. Never a substitute for request credentials. */
|
|
1126
|
+
function copilotAccountKey(grant) {
|
|
1127
|
+
return createHash("sha256").update(`${grant.refresh.length}:`).update(grant.refresh).update(grant.enterpriseUrl ?? "").digest("hex");
|
|
1128
|
+
}
|
|
1129
|
+
/** Compare normalized permission snapshots without retaining model credentials. */
|
|
1130
|
+
function copilotEntitlementKey(grant) {
|
|
1131
|
+
const ids = grant.availableModelIds;
|
|
1132
|
+
const normalized = ids === void 0 ? "missing" : JSON.stringify([...new Set(ids)].sort());
|
|
1133
|
+
return createHash("sha256").update(normalized).digest("hex");
|
|
1134
|
+
}
|
|
1135
|
+
function authEntitlementKey(auth) {
|
|
1136
|
+
return copilotEntitlementKey({ ...auth.availableModelIds === void 0 ? {} : { availableModelIds: [...auth.availableModelIds] } });
|
|
1137
|
+
}
|
|
1138
|
+
function active(signal) {
|
|
1139
|
+
if (signal.aborted) throw new Error("COPILOT_ACCOUNT_AUTH_ABORTED");
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Bind model discovery to the existing native OAuth lifecycle. No model catalog,
|
|
1143
|
+
* login, environment fallback or separate credential record is introduced here.
|
|
1144
|
+
*/
|
|
1145
|
+
function createAccountModelAuth(credentials, renewRejectedCredential) {
|
|
1146
|
+
const native = githubCopilotProvider();
|
|
1147
|
+
const oauth = native.auth.oauth;
|
|
1148
|
+
if (oauth === void 0) throw new Error("COPILOT_ACCOUNT_OAUTH_UNAVAILABLE");
|
|
1149
|
+
const readGrant = async (signal) => {
|
|
1150
|
+
active(signal);
|
|
1151
|
+
const stored = await credentials.read(GITHUB_COPILOT_PROVIDER_ID);
|
|
1152
|
+
active(signal);
|
|
1153
|
+
if (stored?.type !== "oauth") throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
|
|
1154
|
+
return normalizeGitHubCopilotOAuthCredential(stored);
|
|
1155
|
+
};
|
|
1156
|
+
const matches = (grant, key) => {
|
|
1157
|
+
if (copilotAccountKey(grant) !== key) throw new Error("COPILOT_ACCOUNT_CHANGED");
|
|
1158
|
+
};
|
|
1159
|
+
const current = async (auth, signal) => {
|
|
1160
|
+
active(signal);
|
|
1161
|
+
const grant = await readGrant(signal);
|
|
1162
|
+
matches(grant, auth.accountKey);
|
|
1163
|
+
if (grant.access !== auth.apiKey || grant.expires <= Date.now()) throw new Error("COPILOT_ACCOUNT_TOKEN_CHANGED");
|
|
1164
|
+
if (copilotEntitlementKey(grant) !== authEntitlementKey(auth)) throw new Error("COPILOT_ACCOUNT_ENTITLEMENT_CHANGED");
|
|
1165
|
+
const derived = await oauth.toAuth(grant);
|
|
1166
|
+
active(signal);
|
|
1167
|
+
if (derived.apiKey !== auth.apiKey || trustedGitHubCopilotBaseUrl(derived.baseUrl, grant) !== auth.baseURL) throw new Error("COPILOT_ACCOUNT_AUTH_CHANGED");
|
|
1168
|
+
const latest = await readGrant(signal);
|
|
1169
|
+
matches(latest, auth.accountKey);
|
|
1170
|
+
if (latest.access !== auth.apiKey || latest.expires <= Date.now()) throw new Error("COPILOT_ACCOUNT_TOKEN_CHANGED");
|
|
1171
|
+
if (copilotEntitlementKey(latest) !== authEntitlementKey(auth)) throw new Error("COPILOT_ACCOUNT_ENTITLEMENT_CHANGED");
|
|
1172
|
+
};
|
|
1173
|
+
return {
|
|
1174
|
+
async resolveAuth(signal) {
|
|
1175
|
+
try {
|
|
1176
|
+
const before = await readGrant(signal);
|
|
1177
|
+
const key = copilotAccountKey(before);
|
|
1178
|
+
const rejectedAccess = renewRejectedCredential?.(before) === true ? before.access : void 0;
|
|
1179
|
+
const forResolution = (grant) => rejectedAccess !== void 0 && grant.access === rejectedAccess ? {
|
|
1180
|
+
...grant,
|
|
1181
|
+
expires: 0
|
|
1182
|
+
} : grant;
|
|
1183
|
+
const guardedOAuth = {
|
|
1184
|
+
...oauth,
|
|
1185
|
+
login: async () => {
|
|
1186
|
+
throw new Error("COPILOT_ACCOUNT_USE_CANONICAL_SIGN_IN");
|
|
1187
|
+
},
|
|
1188
|
+
async refresh(credential, requestSignal) {
|
|
1189
|
+
active(signal);
|
|
1190
|
+
matches(normalizeGitHubCopilotOAuthCredential(credential), key);
|
|
1191
|
+
const result = normalizeGitHubCopilotOAuthCredential(await oauth.refresh(credential, requestSignal === void 0 ? signal : AbortSignal.any([signal, requestSignal])));
|
|
1192
|
+
active(signal);
|
|
1193
|
+
matches(result, key);
|
|
1194
|
+
return result;
|
|
1195
|
+
},
|
|
1196
|
+
async toAuth(credential) {
|
|
1197
|
+
active(signal);
|
|
1198
|
+
const grant = normalizeGitHubCopilotOAuthCredential(credential);
|
|
1199
|
+
matches(grant, key);
|
|
1200
|
+
const result = await oauth.toAuth(grant);
|
|
1201
|
+
active(signal);
|
|
1202
|
+
return {
|
|
1203
|
+
...result,
|
|
1204
|
+
baseUrl: trustedGitHubCopilotBaseUrl(result.baseUrl, grant)
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
const models = createModels({ credentials: {
|
|
1209
|
+
read: async (provider) => {
|
|
1210
|
+
const value = await credentials.read(provider);
|
|
1211
|
+
active(signal);
|
|
1212
|
+
if (value?.type !== "oauth") throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
|
|
1213
|
+
const grant = normalizeGitHubCopilotOAuthCredential(value);
|
|
1214
|
+
matches(grant, key);
|
|
1215
|
+
return forResolution(grant);
|
|
1216
|
+
},
|
|
1217
|
+
list: () => credentials.list(),
|
|
1218
|
+
modify: (provider, mutate) => credentials.modify(provider, async (value) => {
|
|
1219
|
+
active(signal);
|
|
1220
|
+
if (value?.type !== "oauth") throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
|
|
1221
|
+
matches(normalizeGitHubCopilotOAuthCredential(value), key);
|
|
1222
|
+
const next = await mutate(forResolution(normalizeGitHubCopilotOAuthCredential(value)));
|
|
1223
|
+
active(signal);
|
|
1224
|
+
if (next !== void 0) matches(normalizeGitHubCopilotOAuthCredential(next), key);
|
|
1225
|
+
return next;
|
|
1226
|
+
}),
|
|
1227
|
+
delete: async () => {
|
|
1228
|
+
throw new Error("COPILOT_ACCOUNT_DISCOVERY_CANNOT_SIGN_OUT");
|
|
1229
|
+
}
|
|
1230
|
+
} });
|
|
1231
|
+
models.setProvider({
|
|
1232
|
+
...native,
|
|
1233
|
+
auth: { oauth: guardedOAuth }
|
|
1234
|
+
});
|
|
1235
|
+
const resolved = await models.getAuth(GITHUB_COPILOT_PROVIDER_ID, {
|
|
1236
|
+
signal,
|
|
1237
|
+
minOAuthValidityMs: ACCOUNT_MODEL_AUTH_MIN_VALIDITY_MS
|
|
1238
|
+
});
|
|
1239
|
+
active(signal);
|
|
1240
|
+
if (resolved?.auth.apiKey === void 0) throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
|
|
1241
|
+
const grant = await readGrant(signal);
|
|
1242
|
+
matches(grant, key);
|
|
1243
|
+
if (rejectedAccess !== void 0 && grant.access === rejectedAccess) throw new Error("COPILOT_ACCOUNT_TOKEN_REJECTED");
|
|
1244
|
+
const auth = {
|
|
1245
|
+
apiKey: resolved.auth.apiKey,
|
|
1246
|
+
baseURL: trustedGitHubCopilotBaseUrl(resolved.auth.baseUrl, grant),
|
|
1247
|
+
accountKey: key,
|
|
1248
|
+
...grant.availableModelIds === void 0 ? {} : { availableModelIds: Object.freeze([...grant.availableModelIds]) }
|
|
1249
|
+
};
|
|
1250
|
+
await current(auth, signal);
|
|
1251
|
+
return Object.freeze(auth);
|
|
1252
|
+
} catch {
|
|
1253
|
+
throw new Error(signal.aborted ? "COPILOT_ACCOUNT_AUTH_ABORTED" : "COPILOT_ACCOUNT_AUTH_FAILED");
|
|
1254
|
+
}
|
|
1255
|
+
},
|
|
1256
|
+
async assertAuthCurrent(auth, signal) {
|
|
1257
|
+
try {
|
|
1258
|
+
await current(auth, signal);
|
|
1259
|
+
} catch {
|
|
1260
|
+
throw new Error(signal.aborted ? "COPILOT_ACCOUNT_AUTH_ABORTED" : "COPILOT_ACCOUNT_AUTH_CHANGED");
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
//#endregion
|
|
1266
|
+
//#region lib/types/copilot-usage-types.js
|
|
1267
|
+
const COPILOT_USAGE_MAX_AMOUNT = 0xe8d4a51000;
|
|
1268
|
+
const COPILOT_USAGE_MAX_TIMESTAMP = 0xe677d21fdbff;
|
|
1269
|
+
//#endregion
|
|
1270
|
+
//#region lib/types/copilot-usage-normalize.js
|
|
1271
|
+
const amount = z$1.number().finite().min(0).max(COPILOT_USAGE_MAX_AMOUNT);
|
|
1272
|
+
const entitlement = z$1.union([
|
|
1273
|
+
amount,
|
|
1274
|
+
z$1.literal(-1),
|
|
1275
|
+
z$1.string().max(32).regex(/^(?:-1|(?:0|[1-9]\d*)(?:\.\d+)?)$/u)
|
|
1276
|
+
]).transform(Number).refine((value) => Number.isFinite(value) && value <= 0xe8d4a51000);
|
|
1277
|
+
const snapshot = z$1.object({
|
|
1278
|
+
unlimited: z$1.boolean(),
|
|
1279
|
+
entitlement: entitlement.optional(),
|
|
1280
|
+
percent_remaining: z$1.number().finite().min(0).max(100).optional(),
|
|
1281
|
+
quota_remaining: amount.optional(),
|
|
1282
|
+
credits_used: amount.optional(),
|
|
1283
|
+
has_quota: z$1.boolean().optional(),
|
|
1284
|
+
token_based_billing: z$1.boolean().optional(),
|
|
1285
|
+
quota_reset_at: z$1.number().int().min(0).max(Math.floor(COPILOT_USAGE_MAX_TIMESTAMP / 1e3)).optional()
|
|
1286
|
+
});
|
|
1287
|
+
const response = z$1.object({
|
|
1288
|
+
token_based_billing: z$1.boolean().optional(),
|
|
1289
|
+
access_type_sku: z$1.string().max(128).optional(),
|
|
1290
|
+
quota_reset_date: z$1.string().max(64).optional(),
|
|
1291
|
+
quota_reset_date_utc: z$1.string().max(64).optional(),
|
|
1292
|
+
limited_user_reset_date: z$1.string().max(64).optional(),
|
|
1293
|
+
quota_snapshots: z$1.object({
|
|
1294
|
+
chat: snapshot.optional(),
|
|
1295
|
+
premium_interactions: snapshot.optional()
|
|
1296
|
+
}).optional(),
|
|
1297
|
+
monthly_quotas: z$1.object({ chat: amount }).optional(),
|
|
1298
|
+
limited_user_quotas: z$1.object({ chat: amount }).optional()
|
|
1299
|
+
});
|
|
1300
|
+
function unavailableCopilotUsage(diagnostic, billing = "unknown", budget = "unknown") {
|
|
1301
|
+
return {
|
|
1302
|
+
state: diagnostic === "COPILOT_USAGE_SIGNED_OUT" ? "signed-out" : "unavailable",
|
|
1303
|
+
billing,
|
|
1304
|
+
budget,
|
|
1305
|
+
diagnostic
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
function resetDate(value) {
|
|
1309
|
+
if (!/^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2}))?$/u.test(value)) return void 0;
|
|
1310
|
+
const date = value.slice(0, 10);
|
|
1311
|
+
const day = Date.parse(`${date}T00:00:00Z`);
|
|
1312
|
+
if (!Number.isFinite(day) || new Date(day).toISOString().slice(0, 10) !== date) return void 0;
|
|
1313
|
+
const parsed = Date.parse(value);
|
|
1314
|
+
return Number.isFinite(parsed) && parsed >= 0 && parsed <= 0xe677d21fdbff ? parsed : void 0;
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Pinned VS Code 44825207: parseQuotas/getQuotaUsage and chatStatusDashboard
|
|
1318
|
+
* display account amounts directly. Only per-turn nano-AIU uses /1e9; it is
|
|
1319
|
+
* intentionally absent here. Pooled credits_used never shares an entitlement.
|
|
1320
|
+
*/
|
|
1321
|
+
function normalizeCopilotUsage(input, observedAt) {
|
|
1322
|
+
const parsed = response.safeParse(input);
|
|
1323
|
+
if (!parsed.success) return unavailableCopilotUsage("COPILOT_USAGE_INVALID_RESPONSE");
|
|
1324
|
+
const data = parsed.data;
|
|
1325
|
+
if (data.token_based_billing === void 0) return unavailableCopilotUsage("COPILOT_USAGE_BILLING_UNKNOWN");
|
|
1326
|
+
const billing = data.token_based_billing ? "credits" : "requests";
|
|
1327
|
+
const quota = data.access_type_sku === "free_limited_copilot" ? data.quota_snapshots?.chat : data.quota_snapshots?.premium_interactions;
|
|
1328
|
+
const reset = data.quota_reset_date_utc ?? data.quota_reset_date ?? data.limited_user_reset_date;
|
|
1329
|
+
const accountReset = reset === void 0 ? void 0 : resetDate(reset);
|
|
1330
|
+
if (reset !== void 0 && accountReset === void 0) return unavailableCopilotUsage("COPILOT_USAGE_INVALID_RESPONSE", billing);
|
|
1331
|
+
const resetAt = quota?.quota_reset_at === void 0 ? accountReset : quota.quota_reset_at * 1e3;
|
|
1332
|
+
const dates = {
|
|
1333
|
+
observedAt,
|
|
1334
|
+
...resetAt === void 0 ? {} : { resetAt }
|
|
1335
|
+
};
|
|
1336
|
+
if (!quota) {
|
|
1337
|
+
const limit = data.monthly_quotas?.chat, remaining = data.limited_user_quotas?.chat;
|
|
1338
|
+
if (billing === "requests" && limit !== void 0 && limit > 0 && remaining !== void 0 && remaining <= limit) return {
|
|
1339
|
+
state: "ready",
|
|
1340
|
+
billing,
|
|
1341
|
+
budget: "individual",
|
|
1342
|
+
limit,
|
|
1343
|
+
remaining,
|
|
1344
|
+
used: limit - remaining,
|
|
1345
|
+
percentUsed: (limit - remaining) / limit * 100,
|
|
1346
|
+
...dates
|
|
1347
|
+
};
|
|
1348
|
+
return unavailableCopilotUsage("COPILOT_USAGE_SNAPSHOT_MISSING", billing);
|
|
1349
|
+
}
|
|
1350
|
+
if (quota.token_based_billing !== void 0 && quota.token_based_billing !== data.token_based_billing) return unavailableCopilotUsage("COPILOT_USAGE_INVALID_RESPONSE", billing);
|
|
1351
|
+
if (quota.unlimited) {
|
|
1352
|
+
if (quota.has_quota === false) return unavailableCopilotUsage("COPILOT_USAGE_POOLED_EXHAUSTED", billing, "pooled");
|
|
1353
|
+
if (billing !== "credits" || quota.credits_used === void 0) return unavailableCopilotUsage("COPILOT_USAGE_POOLED_UNAVAILABLE", billing, "pooled");
|
|
1354
|
+
return {
|
|
1355
|
+
state: "ready",
|
|
1356
|
+
billing,
|
|
1357
|
+
budget: "pooled",
|
|
1358
|
+
used: quota.credits_used,
|
|
1359
|
+
...dates
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
const limit = quota.entitlement, percent = quota.percent_remaining;
|
|
1363
|
+
if (limit === 0) return unavailableCopilotUsage("COPILOT_USAGE_NO_ALLOCATION", billing, "individual");
|
|
1364
|
+
if (limit !== void 0 && limit < 0 || percent === void 0 || limit !== void 0 && quota.quota_remaining !== void 0 && quota.quota_remaining > limit) return unavailableCopilotUsage("COPILOT_USAGE_INVALID_RESPONSE", billing);
|
|
1365
|
+
if (limit !== void 0 && quota.quota_remaining !== void 0 && Math.abs(quota.quota_remaining / limit * 100 - percent) > 1) return unavailableCopilotUsage("COPILOT_USAGE_INVALID_RESPONSE", billing);
|
|
1366
|
+
const remaining = quota.quota_remaining;
|
|
1367
|
+
return {
|
|
1368
|
+
state: "ready",
|
|
1369
|
+
billing,
|
|
1370
|
+
budget: "individual",
|
|
1371
|
+
percentUsed: 100 - percent,
|
|
1372
|
+
...dates,
|
|
1373
|
+
...limit === void 0 ? {} : { limit },
|
|
1374
|
+
...remaining === void 0 ? {} : { remaining },
|
|
1375
|
+
...limit === void 0 || remaining === void 0 ? {} : { used: limit - remaining }
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
//#endregion
|
|
1379
|
+
//#region lib/types/copilot-usage-host.js
|
|
1380
|
+
var __runInitializers$1 = function(thisArg, initializers, value) {
|
|
1381
|
+
var useValue = arguments.length > 2;
|
|
1382
|
+
for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
1383
|
+
return useValue ? value : void 0;
|
|
1384
|
+
};
|
|
1385
|
+
var __esDecorate$1 = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
1386
|
+
function accept(f) {
|
|
1387
|
+
if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
|
|
1388
|
+
return f;
|
|
1389
|
+
}
|
|
1390
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
1391
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
1392
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
1393
|
+
var _, done = false;
|
|
1394
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
1395
|
+
var context = {};
|
|
1396
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
1397
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
1398
|
+
context.addInitializer = function(f) {
|
|
1399
|
+
if (done) throw new TypeError("Cannot add initializers after decoration has completed");
|
|
1400
|
+
extraInitializers.push(accept(f || null));
|
|
1401
|
+
};
|
|
1402
|
+
var result = (0, decorators[i])(kind === "accessor" ? {
|
|
1403
|
+
get: descriptor.get,
|
|
1404
|
+
set: descriptor.set
|
|
1405
|
+
} : descriptor[key], context);
|
|
1406
|
+
if (kind === "accessor") {
|
|
1407
|
+
if (result === void 0) continue;
|
|
1408
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
1409
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
1410
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
1411
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
1412
|
+
} else if (_ = accept(result)) {
|
|
1413
|
+
if (kind === "field") initializers.unshift(_);
|
|
1414
|
+
else descriptor[key] = _;
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
1418
|
+
done = true;
|
|
1419
|
+
};
|
|
1420
|
+
const COPILOT_USAGE_ENDPOINT = "https://api.github.com/copilot_internal/user";
|
|
1421
|
+
const TTL_MS = 6e4;
|
|
1422
|
+
const REFRESH_INTERVAL_MS = 1e4;
|
|
1423
|
+
const FAILURE_COOLDOWN_MS = 3e4;
|
|
1424
|
+
const TIMEOUT_MS = 1e4;
|
|
1425
|
+
const MAX_BODY_BYTES = 262144;
|
|
1426
|
+
var UsageFailure = class extends Error {
|
|
1427
|
+
diagnostic;
|
|
1428
|
+
constructor(diagnostic) {
|
|
1429
|
+
super(diagnostic);
|
|
1430
|
+
this.diagnostic = diagnostic;
|
|
1431
|
+
}
|
|
1432
|
+
};
|
|
1433
|
+
function failure$2(diagnostic) {
|
|
1434
|
+
throw new UsageFailure(diagnostic);
|
|
1435
|
+
}
|
|
1436
|
+
async function readBody(response, signal) {
|
|
1437
|
+
const length = response.headers.get("content-length");
|
|
1438
|
+
if (length !== null && (!/^\d+$/u.test(length) || Number(length) > MAX_BODY_BYTES)) {
|
|
1439
|
+
response.body?.cancel().catch(() => void 0);
|
|
1440
|
+
failure$2("COPILOT_USAGE_BODY_TOO_LARGE");
|
|
1441
|
+
}
|
|
1442
|
+
if (!response.body) failure$2("COPILOT_USAGE_INVALID_RESPONSE");
|
|
1443
|
+
const reader = response.body.getReader(), chunks = [];
|
|
1444
|
+
const cancel = () => {
|
|
1445
|
+
reader.cancel().catch(() => void 0);
|
|
1446
|
+
};
|
|
1447
|
+
signal.addEventListener("abort", cancel, { once: true });
|
|
1448
|
+
if (signal.aborted) cancel();
|
|
1449
|
+
let size = 0;
|
|
1450
|
+
try {
|
|
1451
|
+
while (true) {
|
|
1452
|
+
const next = await reader.read();
|
|
1453
|
+
if (next.done) break;
|
|
1454
|
+
size += next.value.byteLength;
|
|
1455
|
+
if (size > MAX_BODY_BYTES) {
|
|
1456
|
+
reader.cancel().catch(() => void 0);
|
|
1457
|
+
failure$2("COPILOT_USAGE_BODY_TOO_LARGE");
|
|
1458
|
+
}
|
|
1459
|
+
chunks.push(next.value);
|
|
1460
|
+
}
|
|
1461
|
+
} finally {
|
|
1462
|
+
signal.removeEventListener("abort", cancel);
|
|
1463
|
+
reader.releaseLock();
|
|
1464
|
+
}
|
|
1465
|
+
const bytes = new Uint8Array(size);
|
|
1466
|
+
let offset = 0;
|
|
1467
|
+
for (const chunk of chunks) {
|
|
1468
|
+
bytes.set(chunk, offset);
|
|
1469
|
+
offset += chunk.byteLength;
|
|
1470
|
+
}
|
|
1471
|
+
try {
|
|
1472
|
+
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
1473
|
+
} catch {
|
|
1474
|
+
failure$2("COPILOT_USAGE_INVALID_RESPONSE");
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
/** One Host-owned, memory-only account cache. No auth renewal, persistence or model transport. */
|
|
1478
|
+
var CopilotUsageSource = class {
|
|
1479
|
+
dependencies;
|
|
1480
|
+
fetcher;
|
|
1481
|
+
now;
|
|
1482
|
+
generation = 0;
|
|
1483
|
+
disposed = false;
|
|
1484
|
+
key;
|
|
1485
|
+
cached;
|
|
1486
|
+
last;
|
|
1487
|
+
nextRefreshAt = 0;
|
|
1488
|
+
failureUntil = 0;
|
|
1489
|
+
flight;
|
|
1490
|
+
constructor(dependencies) {
|
|
1491
|
+
this.dependencies = dependencies;
|
|
1492
|
+
this.fetcher = dependencies.fetch ?? globalThis.fetch;
|
|
1493
|
+
this.now = dependencies.now ?? Date.now;
|
|
1494
|
+
}
|
|
1495
|
+
invalidate() {
|
|
1496
|
+
this.generation++;
|
|
1497
|
+
this.flight?.abort.abort();
|
|
1498
|
+
this.flight = void 0;
|
|
1499
|
+
this.key = void 0;
|
|
1500
|
+
this.cached = void 0;
|
|
1501
|
+
this.last = void 0;
|
|
1502
|
+
this.nextRefreshAt = 0;
|
|
1503
|
+
this.failureUntil = 0;
|
|
1504
|
+
}
|
|
1505
|
+
dispose() {
|
|
1506
|
+
this.disposed = true;
|
|
1507
|
+
this.invalidate();
|
|
1508
|
+
}
|
|
1509
|
+
get() {
|
|
1510
|
+
return this.load(false);
|
|
1511
|
+
}
|
|
1512
|
+
refresh() {
|
|
1513
|
+
return this.load(true);
|
|
1514
|
+
}
|
|
1515
|
+
active(generation) {
|
|
1516
|
+
if (this.disposed) failure$2("COPILOT_USAGE_DISPOSED");
|
|
1517
|
+
if (generation !== this.generation) failure$2("COPILOT_USAGE_ACCOUNT_CHANGED");
|
|
1518
|
+
}
|
|
1519
|
+
async readAuth(generation) {
|
|
1520
|
+
this.active(generation);
|
|
1521
|
+
let stored;
|
|
1522
|
+
try {
|
|
1523
|
+
stored = await this.dependencies.readCredential();
|
|
1524
|
+
} catch {
|
|
1525
|
+
this.active(generation);
|
|
1526
|
+
failure$2("COPILOT_USAGE_CREDENTIALS_UNAVAILABLE");
|
|
1527
|
+
}
|
|
1528
|
+
this.active(generation);
|
|
1529
|
+
if (stored === void 0) failure$2("COPILOT_USAGE_SIGNED_OUT");
|
|
1530
|
+
let grant;
|
|
1531
|
+
try {
|
|
1532
|
+
grant = normalizeGitHubCopilotOAuthCredential(stored);
|
|
1533
|
+
} catch {
|
|
1534
|
+
failure$2("COPILOT_USAGE_INVALID_GRANT");
|
|
1535
|
+
}
|
|
1536
|
+
if (grant.enterpriseUrl !== void 0) failure$2("COPILOT_USAGE_ENTERPRISE_UNSUPPORTED");
|
|
1537
|
+
return {
|
|
1538
|
+
grant,
|
|
1539
|
+
key: copilotAccountKey(grant)
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
async assertCurrent(key, generation) {
|
|
1543
|
+
if ((await this.readAuth(generation)).key !== key) failure$2("COPILOT_USAGE_ACCOUNT_CHANGED");
|
|
1544
|
+
}
|
|
1545
|
+
diagnostic(error) {
|
|
1546
|
+
return error instanceof UsageFailure ? error.diagnostic : "COPILOT_USAGE_NETWORK";
|
|
1547
|
+
}
|
|
1548
|
+
async load(force) {
|
|
1549
|
+
let generation = this.generation;
|
|
1550
|
+
let auth;
|
|
1551
|
+
try {
|
|
1552
|
+
auth = await this.readAuth(generation);
|
|
1553
|
+
this.active(generation);
|
|
1554
|
+
} catch (error) {
|
|
1555
|
+
if (generation === this.generation) this.invalidate();
|
|
1556
|
+
return unavailableCopilotUsage(this.diagnostic(error));
|
|
1557
|
+
}
|
|
1558
|
+
if (this.key !== void 0 && this.key !== auth.key) {
|
|
1559
|
+
this.invalidate();
|
|
1560
|
+
generation = this.generation;
|
|
1561
|
+
}
|
|
1562
|
+
this.key = auth.key;
|
|
1563
|
+
if (this.flight) {
|
|
1564
|
+
const result = await this.flight.promise;
|
|
1565
|
+
if (this.disposed) return unavailableCopilotUsage("COPILOT_USAGE_DISPOSED");
|
|
1566
|
+
if (generation !== this.generation) return unavailableCopilotUsage("COPILOT_USAGE_ACCOUNT_CHANGED");
|
|
1567
|
+
return { ...result };
|
|
1568
|
+
}
|
|
1569
|
+
const now = this.now();
|
|
1570
|
+
if (this.last && now < this.failureUntil) return { ...this.last };
|
|
1571
|
+
if (this.cached && (!force && now - this.cached.observedAt < TTL_MS || now < this.nextRefreshAt)) return { ...this.cached };
|
|
1572
|
+
const abort = new AbortController();
|
|
1573
|
+
const promise = this.request(auth, generation, abort);
|
|
1574
|
+
this.flight = {
|
|
1575
|
+
generation,
|
|
1576
|
+
promise,
|
|
1577
|
+
abort
|
|
1578
|
+
};
|
|
1579
|
+
try {
|
|
1580
|
+
return { ...await promise };
|
|
1581
|
+
} finally {
|
|
1582
|
+
if (this.flight?.promise === promise) this.flight = void 0;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
async fetchQuota(auth, abort) {
|
|
1586
|
+
const response = await this.fetcher(COPILOT_USAGE_ENDPOINT, {
|
|
1587
|
+
method: "GET",
|
|
1588
|
+
redirect: "error",
|
|
1589
|
+
signal: abort.signal,
|
|
1590
|
+
cache: "no-store",
|
|
1591
|
+
headers: {
|
|
1592
|
+
Authorization: `token ${auth.grant.refresh}`,
|
|
1593
|
+
Accept: "application/json",
|
|
1594
|
+
"X-GitHub-Api-Version": "2025-04-01"
|
|
1595
|
+
}
|
|
1596
|
+
});
|
|
1597
|
+
if (response.redirected || response.status >= 300 && response.status < 400) {
|
|
1598
|
+
response.body?.cancel().catch(() => void 0);
|
|
1599
|
+
failure$2("COPILOT_USAGE_REDIRECT");
|
|
1600
|
+
}
|
|
1601
|
+
if (!response.ok) {
|
|
1602
|
+
response.body?.cancel().catch(() => void 0);
|
|
1603
|
+
if (response.status === 401 || response.status === 403) failure$2("COPILOT_USAGE_AUTH_REJECTED");
|
|
1604
|
+
if (response.status === 429) failure$2("COPILOT_USAGE_RATE_LIMITED");
|
|
1605
|
+
failure$2("COPILOT_USAGE_HTTP_ERROR");
|
|
1606
|
+
}
|
|
1607
|
+
return readBody(response, abort.signal);
|
|
879
1608
|
}
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1609
|
+
async request(auth, generation, abort) {
|
|
1610
|
+
let timer;
|
|
1611
|
+
let removeAbort;
|
|
1612
|
+
let value;
|
|
1613
|
+
let error;
|
|
1614
|
+
try {
|
|
1615
|
+
await this.assertCurrent(auth.key, generation);
|
|
1616
|
+
this.active(generation);
|
|
1617
|
+
this.nextRefreshAt = this.now() + REFRESH_INTERVAL_MS;
|
|
1618
|
+
const cancellation = new Promise((_resolve, reject) => {
|
|
1619
|
+
const cancel = () => reject(new UsageFailure(this.disposed ? "COPILOT_USAGE_DISPOSED" : "COPILOT_USAGE_ACCOUNT_CHANGED"));
|
|
1620
|
+
abort.signal.addEventListener("abort", cancel, { once: true });
|
|
1621
|
+
removeAbort = () => abort.signal.removeEventListener("abort", cancel);
|
|
1622
|
+
timer = setTimeout(() => {
|
|
1623
|
+
reject(new UsageFailure("COPILOT_USAGE_TIMEOUT"));
|
|
1624
|
+
abort.abort();
|
|
1625
|
+
}, TIMEOUT_MS);
|
|
1626
|
+
});
|
|
1627
|
+
value = normalizeCopilotUsage(await Promise.race([this.fetchQuota(auth, abort), cancellation]), this.now());
|
|
1628
|
+
} catch (caught) {
|
|
1629
|
+
error = caught;
|
|
1630
|
+
} finally {
|
|
1631
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1632
|
+
removeAbort?.();
|
|
1633
|
+
}
|
|
1634
|
+
try {
|
|
1635
|
+
await this.assertCurrent(auth.key, generation);
|
|
1636
|
+
this.active(generation);
|
|
1637
|
+
} catch (caught) {
|
|
1638
|
+
if (generation === this.generation) this.invalidate();
|
|
1639
|
+
return unavailableCopilotUsage(this.diagnostic(caught));
|
|
905
1640
|
}
|
|
1641
|
+
if (error !== void 0) {
|
|
1642
|
+
const diagnostic = this.diagnostic(error);
|
|
1643
|
+
if (diagnostic === "COPILOT_USAGE_ACCOUNT_CHANGED" || diagnostic === "COPILOT_USAGE_SIGNED_OUT" || diagnostic === "COPILOT_USAGE_INVALID_GRANT" || diagnostic === "COPILOT_USAGE_ENTERPRISE_UNSUPPORTED" || diagnostic === "COPILOT_USAGE_CREDENTIALS_UNAVAILABLE") {
|
|
1644
|
+
this.invalidate();
|
|
1645
|
+
return unavailableCopilotUsage(diagnostic);
|
|
1646
|
+
}
|
|
1647
|
+
const staleAllowed = [
|
|
1648
|
+
"COPILOT_USAGE_NETWORK",
|
|
1649
|
+
"COPILOT_USAGE_TIMEOUT",
|
|
1650
|
+
"COPILOT_USAGE_HTTP_ERROR",
|
|
1651
|
+
"COPILOT_USAGE_RATE_LIMITED"
|
|
1652
|
+
].includes(diagnostic);
|
|
1653
|
+
if (!staleAllowed) this.cached = void 0;
|
|
1654
|
+
value = staleAllowed && this.cached ? {
|
|
1655
|
+
...this.cached,
|
|
1656
|
+
state: "stale",
|
|
1657
|
+
diagnostic
|
|
1658
|
+
} : unavailableCopilotUsage(diagnostic);
|
|
1659
|
+
}
|
|
1660
|
+
if (value === void 0) return unavailableCopilotUsage("COPILOT_USAGE_INVALID_RESPONSE");
|
|
1661
|
+
this.last = value;
|
|
1662
|
+
if (value.state === "ready") {
|
|
1663
|
+
this.cached = value;
|
|
1664
|
+
this.failureUntil = 0;
|
|
1665
|
+
} else {
|
|
1666
|
+
if (value.state !== "stale") this.cached = void 0;
|
|
1667
|
+
this.failureUntil = this.now() + FAILURE_COOLDOWN_MS;
|
|
1668
|
+
}
|
|
1669
|
+
return value;
|
|
906
1670
|
}
|
|
907
|
-
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
908
|
-
done = true;
|
|
909
1671
|
};
|
|
910
|
-
let
|
|
1672
|
+
let GitHubCopilotUsageController = (() => {
|
|
911
1673
|
let _classSuper = TypertRemoteService;
|
|
912
1674
|
let _instanceExtraInitializers = [];
|
|
913
|
-
let
|
|
914
|
-
|
|
1675
|
+
let _get_decorators;
|
|
1676
|
+
let _refresh_decorators;
|
|
1677
|
+
return class GitHubCopilotUsageController extends _classSuper {
|
|
915
1678
|
static {
|
|
916
1679
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
917
|
-
|
|
918
|
-
|
|
1680
|
+
_get_decorators = [Remote];
|
|
1681
|
+
_refresh_decorators = [Remote];
|
|
1682
|
+
__esDecorate$1(this, null, _get_decorators, {
|
|
919
1683
|
kind: "method",
|
|
920
|
-
name: "
|
|
1684
|
+
name: "get",
|
|
921
1685
|
static: false,
|
|
922
1686
|
private: false,
|
|
923
1687
|
access: {
|
|
924
|
-
has: (obj) => "
|
|
925
|
-
get: (obj) => obj.
|
|
1688
|
+
has: (obj) => "get" in obj,
|
|
1689
|
+
get: (obj) => obj.get
|
|
1690
|
+
},
|
|
1691
|
+
metadata: _metadata
|
|
1692
|
+
}, null, _instanceExtraInitializers);
|
|
1693
|
+
__esDecorate$1(this, null, _refresh_decorators, {
|
|
1694
|
+
kind: "method",
|
|
1695
|
+
name: "refresh",
|
|
1696
|
+
static: false,
|
|
1697
|
+
private: false,
|
|
1698
|
+
access: {
|
|
1699
|
+
has: (obj) => "refresh" in obj,
|
|
1700
|
+
get: (obj) => obj.refresh
|
|
926
1701
|
},
|
|
927
1702
|
metadata: _metadata
|
|
928
1703
|
}, null, _instanceExtraInitializers);
|
|
@@ -933,36 +1708,31 @@ let SearchRoutingController = (() => {
|
|
|
933
1708
|
value: _metadata
|
|
934
1709
|
});
|
|
935
1710
|
}
|
|
1711
|
+
source = __runInitializers$1(this, _instanceExtraInitializers);
|
|
936
1712
|
constructor(ctx) {
|
|
937
|
-
super(ctx, "
|
|
938
|
-
|
|
1713
|
+
super(ctx, "githubCopilotUsage");
|
|
1714
|
+
const credentials = createGitHubCopilotCredentialStore(ctx);
|
|
1715
|
+
this.source = new CopilotUsageSource({ readCredential: () => credentials.read(GITHUB_COPILOT_PROVIDER_ID) });
|
|
1716
|
+
ctx.on("credentials/record-updated", (key) => {
|
|
1717
|
+
if (key === "llm-pi-ai/github-copilot") this.source.invalidate();
|
|
1718
|
+
});
|
|
1719
|
+
ctx.on("settings/updated", (namespace) => {
|
|
1720
|
+
if (namespace === "github-copilot" || namespace === "llm-pi-ai") this.source.invalidate();
|
|
1721
|
+
});
|
|
1722
|
+
ctx.effect(() => () => this.source.dispose());
|
|
939
1723
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
const catalog = this.ctx.get("githubCopilotSearchCatalog");
|
|
946
|
-
if (typeof catalog?.list !== "function") return unavailable;
|
|
947
|
-
try {
|
|
948
|
-
const ids = catalog.list();
|
|
949
|
-
const parsed = SearchProviderCatalogSchema.safeParse({
|
|
950
|
-
supported: true,
|
|
951
|
-
providers: ids.map((id) => ({ id }))
|
|
952
|
-
});
|
|
953
|
-
if (!parsed.success || new Set(ids).size !== ids.length) return unavailable;
|
|
954
|
-
parsed.data.providers.sort((a, b) => a.id.localeCompare(b.id));
|
|
955
|
-
return parsed.data;
|
|
956
|
-
} catch {
|
|
957
|
-
return unavailable;
|
|
958
|
-
}
|
|
1724
|
+
async get() {
|
|
1725
|
+
return this.source.get();
|
|
1726
|
+
}
|
|
1727
|
+
async refresh() {
|
|
1728
|
+
return this.source.refresh();
|
|
959
1729
|
}
|
|
960
1730
|
};
|
|
961
1731
|
})();
|
|
962
1732
|
//#endregion
|
|
963
1733
|
//#region package.json
|
|
964
1734
|
var name$1 = "dsh-github-copilot";
|
|
965
|
-
var version = "0.4.0-alpha.
|
|
1735
|
+
var version = "0.4.0-alpha.32";
|
|
966
1736
|
//#endregion
|
|
967
1737
|
//#region lib/types/probe.js
|
|
968
1738
|
/**
|
|
@@ -3127,36 +3897,6 @@ function assertDshCompatibility(ctx) {
|
|
|
3127
3897
|
if (typeof ctx["plugin"] !== "function") incompatible("context.plugin");
|
|
3128
3898
|
}
|
|
3129
3899
|
//#endregion
|
|
3130
|
-
//#region lib/types/copilot-grant.js
|
|
3131
|
-
function nonEmptyOAuthString(value, field) {
|
|
3132
|
-
if (typeof value !== "string" || value.trim().length === 0) throw new Error(`github-copilot: OAuth credential field "${field}" must be a non-empty string`);
|
|
3133
|
-
return value;
|
|
3134
|
-
}
|
|
3135
|
-
/**
|
|
3136
|
-
* Rebuild pi-ai's GitHub Copilot grant as the exact JSON-safe provider shape.
|
|
3137
|
-
* Prototype and unrelated extension members are intentionally not preserved.
|
|
3138
|
-
*/
|
|
3139
|
-
function normalizeGitHubCopilotOAuthCredential(credential) {
|
|
3140
|
-
if (typeof credential !== "object" || credential === null) throw new Error("github-copilot: OAuth credential must be an object");
|
|
3141
|
-
if (Reflect.get(credential, "type") !== "oauth") throw new Error("github-copilot: OAuth credential type must be \"oauth\"");
|
|
3142
|
-
const refresh = nonEmptyOAuthString(Reflect.get(credential, "refresh"), "refresh");
|
|
3143
|
-
const access = nonEmptyOAuthString(Reflect.get(credential, "access"), "access");
|
|
3144
|
-
const expires = Reflect.get(credential, "expires");
|
|
3145
|
-
if (typeof expires !== "number" || !Number.isFinite(expires)) throw new Error("github-copilot: OAuth credential field \"expires\" must be a finite number");
|
|
3146
|
-
const enterpriseUrl = Reflect.get(credential, "enterpriseUrl");
|
|
3147
|
-
if (enterpriseUrl !== void 0 && (typeof enterpriseUrl !== "string" || enterpriseUrl.trim().length === 0)) throw new Error("github-copilot: OAuth credential field \"enterpriseUrl\" must be a non-empty string");
|
|
3148
|
-
const availableModelIds = Reflect.get(credential, "availableModelIds");
|
|
3149
|
-
if (availableModelIds !== void 0 && (!Array.isArray(availableModelIds) || !availableModelIds.every((modelId) => typeof modelId === "string" && modelId.trim().length > 0))) throw new Error("github-copilot: OAuth credential field \"availableModelIds\" must be an array of non-empty strings");
|
|
3150
|
-
return {
|
|
3151
|
-
type: "oauth",
|
|
3152
|
-
refresh,
|
|
3153
|
-
access,
|
|
3154
|
-
expires,
|
|
3155
|
-
...enterpriseUrl === void 0 ? {} : { enterpriseUrl },
|
|
3156
|
-
...availableModelIds === void 0 ? {} : { availableModelIds: [...new Set(availableModelIds)] }
|
|
3157
|
-
};
|
|
3158
|
-
}
|
|
3159
|
-
//#endregion
|
|
3160
3900
|
//#region lib/types/temporary-models.js
|
|
3161
3901
|
const GPT_6_ASTRA = {
|
|
3162
3902
|
id: "gpt-6-astra",
|
|
@@ -4061,189 +4801,60 @@ let GitHubCopilotAuthorizationController = (() => {
|
|
|
4061
4801
|
},
|
|
4062
4802
|
prompt: (prompt) => {
|
|
4063
4803
|
if (prompt.kind === "text" && /GitHub Enterprise URL\/domain/i.test(prompt.message)) return Promise.resolve("");
|
|
4064
|
-
return Promise.reject(/* @__PURE__ */ new Error(`github-copilot: this browser bridge cannot answer authorization prompt "${prompt.message}"`));
|
|
4065
|
-
}
|
|
4066
|
-
}
|
|
4067
|
-
}).then(async (outcome) => {
|
|
4068
|
-
if (outcome.status === "authorized") {
|
|
4069
|
-
this.notices = [];
|
|
4070
|
-
await this.ensureProviderProfile();
|
|
4071
|
-
return;
|
|
4072
|
-
}
|
|
4073
|
-
this.notices = [];
|
|
4074
|
-
}).catch((error) => {
|
|
4075
|
-
this.notices = [];
|
|
4076
|
-
this.failure = messageOf(error);
|
|
4077
|
-
this.ctx.logger.error("github-copilot: GitHub Copilot authorization failed");
|
|
4078
|
-
this.ctx.logger.error(error);
|
|
4079
|
-
}).finally(() => {
|
|
4080
|
-
this.attempt = void 0;
|
|
4081
|
-
});
|
|
4082
|
-
this.attempt = running;
|
|
4083
|
-
return this.status();
|
|
4084
|
-
}
|
|
4085
|
-
async cancel() {
|
|
4086
|
-
const authorization = service(this.ctx, "authorization", [
|
|
4087
|
-
"describe",
|
|
4088
|
-
"begin",
|
|
4089
|
-
"cancel"
|
|
4090
|
-
]);
|
|
4091
|
-
this.notices = [];
|
|
4092
|
-
authorization.cancel(GITHUB_COPILOT_CREDENTIAL_KEY);
|
|
4093
|
-
return this.status();
|
|
4094
|
-
}
|
|
4095
|
-
async signOut() {
|
|
4096
|
-
if (service(this.ctx, "authorization", [
|
|
4097
|
-
"describe",
|
|
4098
|
-
"begin",
|
|
4099
|
-
"cancel"
|
|
4100
|
-
]).describe("llm-pi-ai/github-copilot")?.inFlight === true) throw new Error("github-copilot: cancel the active sign-in attempt before signing out");
|
|
4101
|
-
await service(this.ctx, "credentials", ["describeRecord", "deleteRecord"]).deleteRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
|
|
4102
|
-
this.notices = [];
|
|
4103
|
-
this.failure = void 0;
|
|
4104
|
-
this.reconciliationFailed = false;
|
|
4105
|
-
return this.status();
|
|
4106
|
-
}
|
|
4107
|
-
async ensureProviderProfile() {
|
|
4108
|
-
try {
|
|
4109
|
-
await ensureGitHubCopilotProviderProfile(this.ctx);
|
|
4110
|
-
this.reconciliationFailed = false;
|
|
4111
|
-
} catch {
|
|
4112
|
-
this.reconciliationFailed = true;
|
|
4113
|
-
this.ctx.logger.warn("github-copilot: route reconciliation failed; review route status before retrying");
|
|
4114
|
-
}
|
|
4115
|
-
}
|
|
4116
|
-
};
|
|
4117
|
-
})();
|
|
4118
|
-
//#endregion
|
|
4119
|
-
//#region lib/types/copilot-auth.js
|
|
4120
|
-
/**
|
|
4121
|
-
* Provider-side GitHub Copilot auth resolution over the same credential record
|
|
4122
|
-
* that DSH's built-in llm-pi-ai adapter owns.
|
|
4123
|
-
*/
|
|
4124
|
-
function credentialService(ctx) {
|
|
4125
|
-
const candidate = ctx.get("credentials");
|
|
4126
|
-
if (typeof candidate !== "object" || candidate === null) throw new Error("github-copilot: DSH credentials service is unavailable");
|
|
4127
|
-
for (const method of [
|
|
4128
|
-
"readRecord",
|
|
4129
|
-
"listRecords",
|
|
4130
|
-
"modifyRecord",
|
|
4131
|
-
"deleteRecord"
|
|
4132
|
-
]) if (typeof Reflect.get(candidate, method) !== "function") throw new Error(`github-copilot: required DSH credentials API "credentials.${method}" is unavailable`);
|
|
4133
|
-
return candidate;
|
|
4134
|
-
}
|
|
4135
|
-
function toCredential(record) {
|
|
4136
|
-
if (record === void 0) return void 0;
|
|
4137
|
-
if (record.kind === "api-key") return {
|
|
4138
|
-
type: "api_key",
|
|
4139
|
-
...record.key === void 0 ? {} : { key: record.key },
|
|
4140
|
-
...record.env === void 0 ? {} : { env: { ...record.env } }
|
|
4141
|
-
};
|
|
4142
|
-
if (typeof record.payload !== "object" || record.payload === null) throw new Error("github-copilot: stored llm-pi-ai GitHub Copilot grant is not an object");
|
|
4143
|
-
return normalizeGitHubCopilotOAuthCredential(record.payload);
|
|
4144
|
-
}
|
|
4145
|
-
function toRecord(credential) {
|
|
4146
|
-
if (credential.type === "api_key") return {
|
|
4147
|
-
kind: "api-key",
|
|
4148
|
-
...credential.key === void 0 ? {} : { key: credential.key },
|
|
4149
|
-
...credential.env === void 0 ? {} : { env: { ...credential.env } }
|
|
4150
|
-
};
|
|
4151
|
-
return {
|
|
4152
|
-
kind: "grant",
|
|
4153
|
-
payload: normalizeGitHubCopilotOAuthCredential(credential)
|
|
4154
|
-
};
|
|
4155
|
-
}
|
|
4156
|
-
/** Bind exactly one logical route to the canonical Host-owned credential record. */
|
|
4157
|
-
function createGitHubCopilotCredentialStore(ctx, logicalProviderId = GITHUB_COPILOT_PROVIDER_ID) {
|
|
4158
|
-
const assertProvider = (providerId) => {
|
|
4159
|
-
if (providerId !== logicalProviderId) throw new Error("COPILOT_CREDENTIAL_PROVIDER_MISMATCH");
|
|
4160
|
-
};
|
|
4161
|
-
return {
|
|
4162
|
-
read: async (providerId) => {
|
|
4163
|
-
assertProvider(providerId);
|
|
4164
|
-
return toCredential(await credentialService(ctx).readRecord(GITHUB_COPILOT_CREDENTIAL_KEY));
|
|
4165
|
-
},
|
|
4166
|
-
list: async () => {
|
|
4167
|
-
const record = await credentialService(ctx).readRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
|
|
4168
|
-
return record === void 0 ? [] : [{
|
|
4169
|
-
providerId: logicalProviderId,
|
|
4170
|
-
type: record.kind === "api-key" ? "api_key" : "oauth"
|
|
4171
|
-
}];
|
|
4172
|
-
},
|
|
4173
|
-
modify: async (providerId, mutate) => {
|
|
4174
|
-
assertProvider(providerId);
|
|
4175
|
-
return toCredential(await credentialService(ctx).modifyRecord(GITHUB_COPILOT_CREDENTIAL_KEY, async (current) => {
|
|
4176
|
-
const next = await mutate(toCredential(current));
|
|
4177
|
-
return next === void 0 ? void 0 : toRecord(next);
|
|
4178
|
-
}));
|
|
4179
|
-
},
|
|
4180
|
-
delete: async (providerId) => {
|
|
4181
|
-
assertProvider(providerId);
|
|
4182
|
-
await credentialService(ctx).deleteRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
|
|
4804
|
+
return Promise.reject(/* @__PURE__ */ new Error(`github-copilot: this browser bridge cannot answer authorization prompt "${prompt.message}"`));
|
|
4805
|
+
}
|
|
4806
|
+
}
|
|
4807
|
+
}).then(async (outcome) => {
|
|
4808
|
+
if (outcome.status === "authorized") {
|
|
4809
|
+
this.notices = [];
|
|
4810
|
+
await this.ensureProviderProfile();
|
|
4811
|
+
return;
|
|
4812
|
+
}
|
|
4813
|
+
this.notices = [];
|
|
4814
|
+
}).catch((error) => {
|
|
4815
|
+
this.notices = [];
|
|
4816
|
+
this.failure = messageOf(error);
|
|
4817
|
+
this.ctx.logger.error("github-copilot: GitHub Copilot authorization failed");
|
|
4818
|
+
this.ctx.logger.error(error);
|
|
4819
|
+
}).finally(() => {
|
|
4820
|
+
this.attempt = void 0;
|
|
4821
|
+
});
|
|
4822
|
+
this.attempt = running;
|
|
4823
|
+
return this.status();
|
|
4183
4824
|
}
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4825
|
+
async cancel() {
|
|
4826
|
+
const authorization = service(this.ctx, "authorization", [
|
|
4827
|
+
"describe",
|
|
4828
|
+
"begin",
|
|
4829
|
+
"cancel"
|
|
4830
|
+
]);
|
|
4831
|
+
this.notices = [];
|
|
4832
|
+
authorization.cancel(GITHUB_COPILOT_CREDENTIAL_KEY);
|
|
4833
|
+
return this.status();
|
|
4834
|
+
}
|
|
4835
|
+
async signOut() {
|
|
4836
|
+
if (service(this.ctx, "authorization", [
|
|
4837
|
+
"describe",
|
|
4838
|
+
"begin",
|
|
4839
|
+
"cancel"
|
|
4840
|
+
]).describe("llm-pi-ai/github-copilot")?.inFlight === true) throw new Error("github-copilot: cancel the active sign-in attempt before signing out");
|
|
4841
|
+
await service(this.ctx, "credentials", ["describeRecord", "deleteRecord"]).deleteRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
|
|
4842
|
+
this.notices = [];
|
|
4843
|
+
this.failure = void 0;
|
|
4844
|
+
this.reconciliationFailed = false;
|
|
4845
|
+
return this.status();
|
|
4846
|
+
}
|
|
4847
|
+
async ensureProviderProfile() {
|
|
4848
|
+
try {
|
|
4849
|
+
await ensureGitHubCopilotProviderProfile(this.ctx);
|
|
4850
|
+
this.reconciliationFailed = false;
|
|
4851
|
+
} catch {
|
|
4852
|
+
this.reconciliationFailed = true;
|
|
4853
|
+
this.ctx.logger.warn("github-copilot: route reconciliation failed; review route status before retrying");
|
|
4854
|
+
}
|
|
4209
4855
|
}
|
|
4210
|
-
const stored = await credentialService(ctx).readRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
|
|
4211
|
-
if (!(stored?.kind === "grant" && (normalizeGitHubCopilotOAuthCredential(stored.payload).availableModelIds ?? []).includes(modelId))) throw new Error(`github-copilot: model "${modelId}" is not available for the signed-in Copilot account`);
|
|
4212
|
-
const apiKey = resolved.auth.apiKey;
|
|
4213
|
-
if (apiKey === void 0) return void 0;
|
|
4214
|
-
return {
|
|
4215
|
-
apiKey,
|
|
4216
|
-
baseURL: trustedCopilotBaseUrl(resolved.auth.baseUrl ?? native.baseUrl, enterpriseDomainOf(stored)),
|
|
4217
|
-
...resolved.auth.headers === void 0 ? {} : { headers: resolved.auth.headers }
|
|
4218
|
-
};
|
|
4219
4856
|
};
|
|
4220
|
-
}
|
|
4221
|
-
function enterpriseDomainOf(record) {
|
|
4222
|
-
if (record?.kind !== "grant" || typeof record.payload !== "object" || record.payload === null) return void 0;
|
|
4223
|
-
const value = Reflect.get(record.payload, "enterpriseUrl");
|
|
4224
|
-
if (typeof value !== "string" || value.trim().length === 0) return void 0;
|
|
4225
|
-
try {
|
|
4226
|
-
return (value.includes("://") ? new URL(value) : new URL(`https://${value}`)).hostname.toLowerCase();
|
|
4227
|
-
} catch {
|
|
4228
|
-
throw new Error("github-copilot: stored enterpriseUrl is invalid");
|
|
4229
|
-
}
|
|
4230
|
-
}
|
|
4231
|
-
/** Validate an OAuth-derived endpoint without disclosing its value or credential payload. */
|
|
4232
|
-
function trustedGitHubCopilotBaseUrl(value, grant) {
|
|
4233
|
-
return trustedCopilotBaseUrl(value, enterpriseDomainOf({
|
|
4234
|
-
kind: "grant",
|
|
4235
|
-
payload: normalizeGitHubCopilotOAuthCredential(grant)
|
|
4236
|
-
}));
|
|
4237
|
-
}
|
|
4238
|
-
function trustedCopilotBaseUrl(value, enterpriseDomain) {
|
|
4239
|
-
if (value === void 0) throw new Error("github-copilot: pi-ai resolved no Copilot API base URL");
|
|
4240
|
-
const url = new URL(value);
|
|
4241
|
-
const hostname = url.hostname.toLowerCase();
|
|
4242
|
-
const githubHosted = /^api(?:\.[a-z0-9-]+)+\.githubcopilot\.com$/u.test(hostname);
|
|
4243
|
-
const enterpriseHosted = enterpriseDomain !== void 0 && hostname === `copilot-api.${enterpriseDomain}`;
|
|
4244
|
-
if (!(url.protocol === "https:" && (githubHosted || enterpriseHosted)) || url.username.length > 0 || url.password.length > 0) throw new Error("github-copilot: pi-ai resolved an untrusted Copilot API base URL");
|
|
4245
|
-
return url.origin;
|
|
4246
|
-
}
|
|
4857
|
+
})();
|
|
4247
4858
|
//#endregion
|
|
4248
4859
|
//#region lib/types/tool-schema-compat.js
|
|
4249
4860
|
const ESCALATION_FIELDS = /* @__PURE__ */ new Set(["sandbox_permissions", "justification"]);
|
|
@@ -4603,15 +5214,24 @@ function createAccountProvider(descriptors, guard, baseURL) {
|
|
|
4603
5214
|
}));
|
|
4604
5215
|
headers.authorization = `Bearer ${options.apiKey}`;
|
|
4605
5216
|
headers["x-api-key"] = null;
|
|
5217
|
+
let unauthorized = false;
|
|
5218
|
+
const fetch = options.fetch ?? globalThis.fetch;
|
|
5219
|
+
const observeResponse = async (input, init) => {
|
|
5220
|
+
const response = await fetch(input, init);
|
|
5221
|
+
if (response.status === 401) unauthorized = true;
|
|
5222
|
+
return response;
|
|
5223
|
+
};
|
|
4606
5224
|
const wireOptions = model.api === "anthropic-messages" ? {
|
|
4607
5225
|
...options,
|
|
4608
5226
|
signal: lease.signal,
|
|
4609
5227
|
apiKey: void 0,
|
|
4610
|
-
headers
|
|
5228
|
+
headers,
|
|
5229
|
+
fetch: observeResponse
|
|
4611
5230
|
} : {
|
|
4612
5231
|
...options,
|
|
4613
5232
|
signal: lease.signal,
|
|
4614
|
-
headers
|
|
5233
|
+
headers,
|
|
5234
|
+
fetch: observeResponse
|
|
4615
5235
|
};
|
|
4616
5236
|
if (!hasApi(model, "openai-responses") && !hasApi(model, "openai-completions") && !hasApi(model, "anthropic-messages")) {
|
|
4617
5237
|
lease.release();
|
|
@@ -4622,6 +5242,9 @@ function createAccountProvider(descriptors, guard, baseURL) {
|
|
|
4622
5242
|
yield* native.streamSimple(model, context, wireOptions);
|
|
4623
5243
|
} finally {
|
|
4624
5244
|
lease.release();
|
|
5245
|
+
if (unauthorized && !guard.signal.aborted && !options.signal?.aborted) try {
|
|
5246
|
+
guard.onUnauthorized?.();
|
|
5247
|
+
} catch {}
|
|
4625
5248
|
}
|
|
4626
5249
|
})();
|
|
4627
5250
|
})
|
|
@@ -4632,142 +5255,6 @@ function createAccountProvider(descriptors, guard, baseURL) {
|
|
|
4632
5255
|
};
|
|
4633
5256
|
}
|
|
4634
5257
|
//#endregion
|
|
4635
|
-
//#region lib/types/account-model-auth.js
|
|
4636
|
-
/** Native getAuth renews within five minutes; reserve another thirty seconds for metadata preparation. */
|
|
4637
|
-
const ACCOUNT_MODEL_AUTH_MIN_VALIDITY_MS = 33e4;
|
|
4638
|
-
/** An opaque, process-local account key. Never a substitute for request credentials. */
|
|
4639
|
-
function copilotAccountKey(grant) {
|
|
4640
|
-
return createHash("sha256").update(`${grant.refresh.length}:`).update(grant.refresh).update(grant.enterpriseUrl ?? "").digest("hex");
|
|
4641
|
-
}
|
|
4642
|
-
/** Compare normalized permission snapshots without retaining model credentials. */
|
|
4643
|
-
function copilotEntitlementKey(grant) {
|
|
4644
|
-
const ids = grant.availableModelIds;
|
|
4645
|
-
const normalized = ids === void 0 ? "missing" : JSON.stringify([...new Set(ids)].sort());
|
|
4646
|
-
return createHash("sha256").update(normalized).digest("hex");
|
|
4647
|
-
}
|
|
4648
|
-
function authEntitlementKey(auth) {
|
|
4649
|
-
return copilotEntitlementKey({ ...auth.availableModelIds === void 0 ? {} : { availableModelIds: [...auth.availableModelIds] } });
|
|
4650
|
-
}
|
|
4651
|
-
function active(signal) {
|
|
4652
|
-
if (signal.aborted) throw new Error("COPILOT_ACCOUNT_AUTH_ABORTED");
|
|
4653
|
-
}
|
|
4654
|
-
/**
|
|
4655
|
-
* Bind model discovery to the existing native OAuth lifecycle. No model catalog,
|
|
4656
|
-
* login, environment fallback or separate credential record is introduced here.
|
|
4657
|
-
*/
|
|
4658
|
-
function createAccountModelAuth(credentials) {
|
|
4659
|
-
const native = githubCopilotProvider();
|
|
4660
|
-
const oauth = native.auth.oauth;
|
|
4661
|
-
if (oauth === void 0) throw new Error("COPILOT_ACCOUNT_OAUTH_UNAVAILABLE");
|
|
4662
|
-
const readGrant = async (signal) => {
|
|
4663
|
-
active(signal);
|
|
4664
|
-
const stored = await credentials.read(GITHUB_COPILOT_PROVIDER_ID);
|
|
4665
|
-
active(signal);
|
|
4666
|
-
if (stored?.type !== "oauth") throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
|
|
4667
|
-
return normalizeGitHubCopilotOAuthCredential(stored);
|
|
4668
|
-
};
|
|
4669
|
-
const matches = (grant, key) => {
|
|
4670
|
-
if (copilotAccountKey(grant) !== key) throw new Error("COPILOT_ACCOUNT_CHANGED");
|
|
4671
|
-
};
|
|
4672
|
-
const current = async (auth, signal) => {
|
|
4673
|
-
active(signal);
|
|
4674
|
-
const grant = await readGrant(signal);
|
|
4675
|
-
matches(grant, auth.accountKey);
|
|
4676
|
-
if (grant.access !== auth.apiKey || grant.expires <= Date.now()) throw new Error("COPILOT_ACCOUNT_TOKEN_CHANGED");
|
|
4677
|
-
if (copilotEntitlementKey(grant) !== authEntitlementKey(auth)) throw new Error("COPILOT_ACCOUNT_ENTITLEMENT_CHANGED");
|
|
4678
|
-
const derived = await oauth.toAuth(grant);
|
|
4679
|
-
active(signal);
|
|
4680
|
-
if (derived.apiKey !== auth.apiKey || trustedGitHubCopilotBaseUrl(derived.baseUrl, grant) !== auth.baseURL) throw new Error("COPILOT_ACCOUNT_AUTH_CHANGED");
|
|
4681
|
-
const latest = await readGrant(signal);
|
|
4682
|
-
matches(latest, auth.accountKey);
|
|
4683
|
-
if (latest.access !== auth.apiKey || latest.expires <= Date.now()) throw new Error("COPILOT_ACCOUNT_TOKEN_CHANGED");
|
|
4684
|
-
if (copilotEntitlementKey(latest) !== authEntitlementKey(auth)) throw new Error("COPILOT_ACCOUNT_ENTITLEMENT_CHANGED");
|
|
4685
|
-
};
|
|
4686
|
-
return {
|
|
4687
|
-
async resolveAuth(signal) {
|
|
4688
|
-
try {
|
|
4689
|
-
const key = copilotAccountKey(await readGrant(signal));
|
|
4690
|
-
const guardedOAuth = {
|
|
4691
|
-
...oauth,
|
|
4692
|
-
login: async () => {
|
|
4693
|
-
throw new Error("COPILOT_ACCOUNT_USE_CANONICAL_SIGN_IN");
|
|
4694
|
-
},
|
|
4695
|
-
async refresh(credential, requestSignal) {
|
|
4696
|
-
active(signal);
|
|
4697
|
-
matches(normalizeGitHubCopilotOAuthCredential(credential), key);
|
|
4698
|
-
const result = normalizeGitHubCopilotOAuthCredential(await oauth.refresh(credential, requestSignal === void 0 ? signal : AbortSignal.any([signal, requestSignal])));
|
|
4699
|
-
active(signal);
|
|
4700
|
-
matches(result, key);
|
|
4701
|
-
return result;
|
|
4702
|
-
},
|
|
4703
|
-
async toAuth(credential) {
|
|
4704
|
-
active(signal);
|
|
4705
|
-
const grant = normalizeGitHubCopilotOAuthCredential(credential);
|
|
4706
|
-
matches(grant, key);
|
|
4707
|
-
const result = await oauth.toAuth(grant);
|
|
4708
|
-
active(signal);
|
|
4709
|
-
return {
|
|
4710
|
-
...result,
|
|
4711
|
-
baseUrl: trustedGitHubCopilotBaseUrl(result.baseUrl, grant)
|
|
4712
|
-
};
|
|
4713
|
-
}
|
|
4714
|
-
};
|
|
4715
|
-
const models = createModels({ credentials: {
|
|
4716
|
-
read: async (provider) => {
|
|
4717
|
-
const value = await credentials.read(provider);
|
|
4718
|
-
active(signal);
|
|
4719
|
-
if (value?.type !== "oauth") throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
|
|
4720
|
-
matches(normalizeGitHubCopilotOAuthCredential(value), key);
|
|
4721
|
-
return value;
|
|
4722
|
-
},
|
|
4723
|
-
list: () => credentials.list(),
|
|
4724
|
-
modify: (provider, mutate) => credentials.modify(provider, async (value) => {
|
|
4725
|
-
active(signal);
|
|
4726
|
-
if (value?.type !== "oauth") throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
|
|
4727
|
-
matches(normalizeGitHubCopilotOAuthCredential(value), key);
|
|
4728
|
-
const next = await mutate(value);
|
|
4729
|
-
active(signal);
|
|
4730
|
-
if (next !== void 0) matches(normalizeGitHubCopilotOAuthCredential(next), key);
|
|
4731
|
-
return next;
|
|
4732
|
-
}),
|
|
4733
|
-
delete: async () => {
|
|
4734
|
-
throw new Error("COPILOT_ACCOUNT_DISCOVERY_CANNOT_SIGN_OUT");
|
|
4735
|
-
}
|
|
4736
|
-
} });
|
|
4737
|
-
models.setProvider({
|
|
4738
|
-
...native,
|
|
4739
|
-
auth: { oauth: guardedOAuth }
|
|
4740
|
-
});
|
|
4741
|
-
const resolved = await models.getAuth(GITHUB_COPILOT_PROVIDER_ID, {
|
|
4742
|
-
signal,
|
|
4743
|
-
minOAuthValidityMs: ACCOUNT_MODEL_AUTH_MIN_VALIDITY_MS
|
|
4744
|
-
});
|
|
4745
|
-
active(signal);
|
|
4746
|
-
if (resolved?.auth.apiKey === void 0) throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
|
|
4747
|
-
const grant = await readGrant(signal);
|
|
4748
|
-
matches(grant, key);
|
|
4749
|
-
const auth = {
|
|
4750
|
-
apiKey: resolved.auth.apiKey,
|
|
4751
|
-
baseURL: trustedGitHubCopilotBaseUrl(resolved.auth.baseUrl, grant),
|
|
4752
|
-
accountKey: key,
|
|
4753
|
-
...grant.availableModelIds === void 0 ? {} : { availableModelIds: Object.freeze([...grant.availableModelIds]) }
|
|
4754
|
-
};
|
|
4755
|
-
await current(auth, signal);
|
|
4756
|
-
return Object.freeze(auth);
|
|
4757
|
-
} catch {
|
|
4758
|
-
throw new Error(signal.aborted ? "COPILOT_ACCOUNT_AUTH_ABORTED" : "COPILOT_ACCOUNT_AUTH_FAILED");
|
|
4759
|
-
}
|
|
4760
|
-
},
|
|
4761
|
-
async assertAuthCurrent(auth, signal) {
|
|
4762
|
-
try {
|
|
4763
|
-
await current(auth, signal);
|
|
4764
|
-
} catch {
|
|
4765
|
-
throw new Error(signal.aborted ? "COPILOT_ACCOUNT_AUTH_ABORTED" : "COPILOT_ACCOUNT_AUTH_CHANGED");
|
|
4766
|
-
}
|
|
4767
|
-
}
|
|
4768
|
-
};
|
|
4769
|
-
}
|
|
4770
|
-
//#endregion
|
|
4771
5258
|
//#region lib/types/account-model-catalog.js
|
|
4772
5259
|
/** Pure normalization of account /models metadata; no transport, credentials, or model-name rules. */
|
|
4773
5260
|
const ACCOUNT_MODEL_CATALOG_LIMITS = Object.freeze({
|
|
@@ -5890,7 +6377,21 @@ function apply$1(ctx, config = {}) {
|
|
|
5890
6377
|
const budgetSettings = requestBudgetSettings ?? (() => requestBudget ?? {});
|
|
5891
6378
|
resolveRequestBudgetPolicy(budgetSettings());
|
|
5892
6379
|
const store = createGitHubCopilotCredentialStore(ctx, GITHUB_COPILOT_PREVIEW_PROVIDER_ID);
|
|
5893
|
-
|
|
6380
|
+
let rejectedAuth;
|
|
6381
|
+
let authRecovery;
|
|
6382
|
+
const nativeAuth = createAccountModelAuth(createGitHubCopilotCredentialStore(ctx), (grant) => {
|
|
6383
|
+
const accountKey = copilotAccountKey(grant);
|
|
6384
|
+
if (rejectedAuth?.accountKey !== accountKey || rejectedAuth.tokenFingerprint !== tokenFingerprint(grant.access)) return false;
|
|
6385
|
+
const now = Date.now();
|
|
6386
|
+
const cooldown = Math.max(1e3, cacheSettings().accountModelFailureCooldownMs ?? 3e5);
|
|
6387
|
+
const age = authRecovery === void 0 ? void 0 : now - authRecovery.at;
|
|
6388
|
+
if (!Number.isFinite(now) || authRecovery?.accountKey === accountKey && (age === void 0 || !Number.isFinite(age) || age < cooldown)) throw failure("COPILOT_PREVIEW_AUTH_RECOVERY_COOLDOWN");
|
|
6389
|
+
authRecovery = {
|
|
6390
|
+
accountKey,
|
|
6391
|
+
at: now
|
|
6392
|
+
};
|
|
6393
|
+
return true;
|
|
6394
|
+
});
|
|
5894
6395
|
const nativeModels = getBuiltinModels("github-copilot");
|
|
5895
6396
|
const nativeApis = new Map(nativeModels.map((model) => [model.id, model.api]));
|
|
5896
6397
|
let lastValidatedProof;
|
|
@@ -6086,7 +6587,15 @@ function apply$1(ctx, config = {}) {
|
|
|
6086
6587
|
const optionsFor = (lease, inspectRequest) => {
|
|
6087
6588
|
const guard = {
|
|
6088
6589
|
...lifetime.guard(lease),
|
|
6089
|
-
...inspectRequest === void 0 ? {} : { inspectRequest }
|
|
6590
|
+
...inspectRequest === void 0 ? {} : { inspectRequest },
|
|
6591
|
+
onUnauthorized() {
|
|
6592
|
+
if (lease === void 0 || !lifetime.isCurrent(lease.revision) || source.readDisplaySnapshot() !== lease.snapshot || provenSnapshot !== lease.snapshot || snapshotProof !== lease.proof) return;
|
|
6593
|
+
rejectedAuth = lease.proof;
|
|
6594
|
+
lifetime.change();
|
|
6595
|
+
provenSnapshot = void 0;
|
|
6596
|
+
snapshotProof = void 0;
|
|
6597
|
+
lastValidatedProof = void 0;
|
|
6598
|
+
}
|
|
6090
6599
|
};
|
|
6091
6600
|
const { provider } = createAccountProvider(lease?.snapshot.models ?? [], guard, lease?.proof.baseURL ?? "https://api.individual.githubcopilot.com");
|
|
6092
6601
|
const profile = Object.freeze({
|
|
@@ -6303,6 +6812,7 @@ function activate(ctx, config) {
|
|
|
6303
6812
|
ctx.plugin(GitHubCopilotAuthorizationController);
|
|
6304
6813
|
ctx.plugin(GitHubCopilotDualModel);
|
|
6305
6814
|
ctx.plugin(SearchRoutingController);
|
|
6815
|
+
ctx.plugin(GitHubCopilotUsageController);
|
|
6306
6816
|
const resolveGitHubCopilotToken = createGitHubCopilotTokenResolver(ctx, async () => {
|
|
6307
6817
|
await ensureGitHubCopilotProviderProfile(ctx);
|
|
6308
6818
|
});
|