@nick3/copilot-api 1.5.2 → 1.5.5
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 +5 -3
- package/dist/account-AacnHem5.js +362 -0
- package/dist/account-AacnHem5.js.map +1 -0
- package/dist/{accounts-manager-DhwGrhHF.js → accounts-manager-BevCBoaF.js} +96 -7
- package/dist/accounts-manager-BevCBoaF.js.map +1 -0
- package/dist/admin/assets/index-519a65q_.js +66 -0
- package/dist/admin/assets/index-ChMaMig2.css +1 -0
- package/dist/admin/index.html +2 -2
- package/dist/{auth-BtUQnU2m.js → auth-B7x3wjry.js} +6 -12
- package/dist/auth-B7x3wjry.js.map +1 -0
- package/dist/{check-usage-DiomC9Dl.js → check-usage-B1cbDEOI.js} +4 -5
- package/dist/{check-usage-DiomC9Dl.js.map → check-usage-B1cbDEOI.js.map} +1 -1
- package/dist/{debug-BzR5ZQUk.js → debug-BJfZVBB7.js} +2 -2
- package/dist/{debug-BzR5ZQUk.js.map → debug-BJfZVBB7.js.map} +1 -1
- package/dist/{get-copilot-token-DN1P6qll.js → get-copilot-token-cha9rQwA.js} +2 -2
- package/dist/{get-copilot-token-DN1P6qll.js.map → get-copilot-token-cha9rQwA.js.map} +1 -1
- package/dist/main.js +4 -4
- package/dist/{paths-Cvzy-eLX.js → paths-DGlr310R.js} +2 -2
- package/dist/{paths-Cvzy-eLX.js.map → paths-DGlr310R.js.map} +1 -1
- package/dist/{utils-53JWve7i.js → poll-access-token-DFooFWhY.js} +339 -46
- package/dist/poll-access-token-DFooFWhY.js.map +1 -0
- package/dist/{server-D9M_wFyO.js → server-CuXJhEMC.js} +495 -140
- package/dist/server-CuXJhEMC.js.map +1 -0
- package/dist/{start-D3yEfZnW.js → start-D6O1XcfI.js} +26 -15
- package/dist/start-D6O1XcfI.js.map +1 -0
- package/package.json +1 -1
- package/dist/account-CipKmikF.js +0 -17
- package/dist/account-CipKmikF.js.map +0 -1
- package/dist/accounts-manager-DhwGrhHF.js.map +0 -1
- package/dist/accounts-registry-CQYvRe65.js +0 -180
- package/dist/accounts-registry-CQYvRe65.js.map +0 -1
- package/dist/admin/assets/index-B-G_GrPI.js +0 -57
- package/dist/admin/assets/index-CsAeel_7.css +0 -1
- package/dist/auth-BtUQnU2m.js.map +0 -1
- package/dist/poll-access-token-BrRUFB1F.js +0 -52
- package/dist/poll-access-token-BrRUFB1F.js.map +0 -1
- package/dist/server-D9M_wFyO.js.map +0 -1
- package/dist/start-D3yEfZnW.js.map +0 -1
- package/dist/utils-53JWve7i.js.map +0 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
English | [中文](./README_CN.md)
|
|
4
4
|
|
|
5
5
|
> [!WARNING]
|
|
6
|
-
> This is a reverse-engineered proxy of GitHub Copilot API. It is not supported by GitHub, and may break unexpectedly. Use at your own risk.
|
|
6
|
+
> This is a reverse-engineered proxy of GitHub Copilot API. It is not supported by GitHub, and may break unexpectedly. Use at your own risk. In the current version, if not using opencode OAuth, the device ID and machine ID will be sent to GitHub Copilot. It is not recommended to use a large number of accounts on a single device; if necessary, it is advised to run them in Docker containers.
|
|
7
7
|
|
|
8
8
|
> [!WARNING]
|
|
9
9
|
> **GitHub Security Notice:**
|
|
@@ -795,7 +795,9 @@ Here is an example `.claude/settings.json` file:
|
|
|
795
795
|
"DISABLE_NON_ESSENTIAL_MODEL_CALLS": "1",
|
|
796
796
|
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
|
|
797
797
|
"CLAUDE_CODE_ATTRIBUTION_HEADER": "0",
|
|
798
|
-
"CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION": "false"
|
|
798
|
+
"CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION": "false",
|
|
799
|
+
"CLAUDE_CODE_DISABLE_TERMINAL_TITLE": "true",
|
|
800
|
+
"CLAUDE_PLUGIN_ENABLE_QUESTION_RULES": "true"
|
|
799
801
|
},
|
|
800
802
|
"permissions": {
|
|
801
803
|
"deny": [
|
|
@@ -805,7 +807,7 @@ Here is an example `.claude/settings.json` file:
|
|
|
805
807
|
}
|
|
806
808
|
```
|
|
807
809
|
|
|
808
|
-
- Replace `ANTHROPIC_MODEL`, `ANTHROPIC_DEFAULT_OPUS_MODEL`, `ANTHROPIC_DEFAULT_SONNET_MODEL`, and `ANTHROPIC_DEFAULT_HAIKU_MODEL` according to your needs.
|
|
810
|
+
- Replace `ANTHROPIC_MODEL`, `ANTHROPIC_DEFAULT_OPUS_MODEL`, `ANTHROPIC_DEFAULT_SONNET_MODEL`, and `ANTHROPIC_DEFAULT_HAIKU_MODEL` according to your needs. After configuration, please install the claude code plugin [Plugin Integrations](#plugin-integrations). If configuring the claude model, it is recommended to set all model configurations the same, so as to remain consistent with github-copilot claude agent behavior.
|
|
809
811
|
- Setting CLAUDE_CODE_ATTRIBUTION_HEADER to 0 can prevent Claude code from adding billing and version information in system prompts, thereby avoiding prompt cache invalidation.
|
|
810
812
|
- Turning off CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION can prevent quota from being consumed unnecessarily.
|
|
811
813
|
- If you want to disable Claude Code WebSearch, deny `WebSearch` in permissions or set `useResponsesApiWebSearch` to `false` in `config.json`. When enabled, `/v1/responses` can forward `web_search` tools upstream, but actual support still depends on the selected model and Copilot behavior.
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { C as normalizeDomain } from "./poll-access-token-DFooFWhY.js";
|
|
2
|
+
import { n as accountTokenPath, t as PATHS } from "./paths-DGlr310R.js";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
6
|
+
|
|
7
|
+
//#region src/lib/account-client-identity.ts
|
|
8
|
+
const DEFAULT_IDENTITY_OAUTH_APP = "default";
|
|
9
|
+
const DEFAULT_IDENTITY_ENTERPRISE_DOMAIN = "public";
|
|
10
|
+
const getCurrentIdentityEnvironment = () => {
|
|
11
|
+
const rawOauthApp = process.env.COPILOT_API_OAUTH_APP?.trim().toLowerCase();
|
|
12
|
+
const rawEnterpriseDomain = process.env.COPILOT_API_ENTERPRISE_URL?.trim();
|
|
13
|
+
return {
|
|
14
|
+
oauthApp: rawOauthApp || DEFAULT_IDENTITY_OAUTH_APP,
|
|
15
|
+
enterpriseDomain: rawEnterpriseDomain ? normalizeDomain(rawEnterpriseDomain).toLowerCase() : DEFAULT_IDENTITY_ENTERPRISE_DOMAIN
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
const buildIdentityKey = ({ login, oauthApp, enterpriseDomain }) => `${enterpriseDomain}:${oauthApp}:${login}`;
|
|
19
|
+
const createAccountDeviceId = () => randomUUID().toLowerCase();
|
|
20
|
+
const createAccountMachineId = () => createHash("sha256").update(randomUUID(), "utf8").digest("hex");
|
|
21
|
+
const createAccountSessionId = () => randomUUID() + Date.now().toString();
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/lib/accounts-registry.ts
|
|
25
|
+
/**
|
|
26
|
+
* Validate account ID (GitHub login).
|
|
27
|
+
* Rules:
|
|
28
|
+
* - Only alphanumeric characters or single hyphens
|
|
29
|
+
* - 1-39 chars
|
|
30
|
+
* - Cannot begin or end with a hyphen
|
|
31
|
+
* - No consecutive hyphens
|
|
32
|
+
*/
|
|
33
|
+
function validateAccountId(id) {
|
|
34
|
+
if (id.length === 0 || id.length > 39) return false;
|
|
35
|
+
if (!/^[a-z0-9-]+$/i.test(id)) return false;
|
|
36
|
+
if (id.startsWith("-") || id.endsWith("-")) return false;
|
|
37
|
+
if (id.includes("--")) return false;
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
const accountMetaSchema = z.object({
|
|
41
|
+
id: z.string().refine(validateAccountId, { message: "Invalid account id. Expected a GitHub login (1-39 chars, alphanumeric or single hyphens, no leading/trailing hyphen, no consecutive hyphens)." }),
|
|
42
|
+
accountType: z.enum([
|
|
43
|
+
"individual",
|
|
44
|
+
"business",
|
|
45
|
+
"enterprise"
|
|
46
|
+
]),
|
|
47
|
+
addedAt: z.number()
|
|
48
|
+
});
|
|
49
|
+
const accountClientIdentitySchema = z.object({
|
|
50
|
+
login: z.string().refine(validateAccountId, { message: "Invalid client identity login. Expected a GitHub login (1-39 chars, alphanumeric or single hyphens, no leading/trailing hyphen, no consecutive hyphens)." }),
|
|
51
|
+
oauthApp: z.string().min(1),
|
|
52
|
+
enterpriseDomain: z.string().min(1),
|
|
53
|
+
deviceId: z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u, "Invalid device ID format. Expected a lowercase UUID."),
|
|
54
|
+
machineId: z.string().regex(/^[0-9a-f]{64}$/u, "Invalid machine ID format. Expected 64 lowercase hexadecimal characters."),
|
|
55
|
+
createdAt: z.number()
|
|
56
|
+
});
|
|
57
|
+
const accountRegistryV1Schema = z.object({
|
|
58
|
+
version: z.literal(1),
|
|
59
|
+
accounts: z.array(accountMetaSchema)
|
|
60
|
+
});
|
|
61
|
+
const accountRegistryV2Schema = z.object({
|
|
62
|
+
version: z.literal(2),
|
|
63
|
+
accounts: z.array(accountMetaSchema),
|
|
64
|
+
clientIdentities: z.record(z.string(), accountClientIdentitySchema)
|
|
65
|
+
});
|
|
66
|
+
const identityLocks = /* @__PURE__ */ new Map();
|
|
67
|
+
let registryLock = Promise.resolve();
|
|
68
|
+
const runWithRegistryLock = async (operation) => {
|
|
69
|
+
const previousLock = registryLock;
|
|
70
|
+
let releaseLock;
|
|
71
|
+
registryLock = new Promise((resolve) => {
|
|
72
|
+
releaseLock = resolve;
|
|
73
|
+
});
|
|
74
|
+
await previousLock;
|
|
75
|
+
try {
|
|
76
|
+
return await operation();
|
|
77
|
+
} finally {
|
|
78
|
+
releaseLock();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Create an empty registry with the current schema version.
|
|
83
|
+
*/
|
|
84
|
+
function createEmptyRegistry() {
|
|
85
|
+
return {
|
|
86
|
+
version: 2,
|
|
87
|
+
accounts: [],
|
|
88
|
+
clientIdentities: {}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const createClientIdentity = ({ login, oauthApp, enterpriseDomain }) => ({
|
|
92
|
+
login,
|
|
93
|
+
oauthApp,
|
|
94
|
+
enterpriseDomain,
|
|
95
|
+
deviceId: createAccountDeviceId(),
|
|
96
|
+
machineId: createAccountMachineId(),
|
|
97
|
+
createdAt: Date.now()
|
|
98
|
+
});
|
|
99
|
+
const ensureRegistryIdentity = (registry, { login, oauthApp, enterpriseDomain }) => {
|
|
100
|
+
const identityKey = buildIdentityKey({
|
|
101
|
+
login,
|
|
102
|
+
oauthApp,
|
|
103
|
+
enterpriseDomain
|
|
104
|
+
});
|
|
105
|
+
const existing = registry.clientIdentities[identityKey];
|
|
106
|
+
if (existing) return existing;
|
|
107
|
+
const created = createClientIdentity({
|
|
108
|
+
login,
|
|
109
|
+
oauthApp,
|
|
110
|
+
enterpriseDomain
|
|
111
|
+
});
|
|
112
|
+
registry.clientIdentities[identityKey] = created;
|
|
113
|
+
return created;
|
|
114
|
+
};
|
|
115
|
+
const ensureClientIdentitiesForAccounts = (registry) => {
|
|
116
|
+
const { oauthApp, enterpriseDomain } = getCurrentIdentityEnvironment();
|
|
117
|
+
const countBefore = Object.keys(registry.clientIdentities).length;
|
|
118
|
+
for (const account of registry.accounts) ensureRegistryIdentity(registry, {
|
|
119
|
+
login: account.id,
|
|
120
|
+
oauthApp,
|
|
121
|
+
enterpriseDomain
|
|
122
|
+
});
|
|
123
|
+
return Object.keys(registry.clientIdentities).length !== countBefore;
|
|
124
|
+
};
|
|
125
|
+
const assertNoDuplicateAccounts = (registry) => {
|
|
126
|
+
const seen = /* @__PURE__ */ new Set();
|
|
127
|
+
for (const account of registry.accounts) {
|
|
128
|
+
if (seen.has(account.id)) throw new Error(`Invalid accounts registry at ${PATHS.ACCOUNTS_REGISTRY_PATH}: duplicate account id "${account.id}"`);
|
|
129
|
+
seen.add(account.id);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
const loadRegistrySnapshot = async () => {
|
|
133
|
+
try {
|
|
134
|
+
const content = await fs.readFile(PATHS.ACCOUNTS_REGISTRY_PATH, "utf8");
|
|
135
|
+
if (!content.trim()) return {
|
|
136
|
+
registry: createEmptyRegistry(),
|
|
137
|
+
shouldPersist: false
|
|
138
|
+
};
|
|
139
|
+
let parsed;
|
|
140
|
+
try {
|
|
141
|
+
parsed = JSON.parse(content);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
throw new Error(`Invalid accounts registry JSON at ${PATHS.ACCOUNTS_REGISTRY_PATH}: ${error instanceof Error ? error.message : String(error)}`);
|
|
144
|
+
}
|
|
145
|
+
const result = typeof parsed === "object" && parsed !== null && "version" in parsed && parsed.version === 2 ? accountRegistryV2Schema.safeParse(parsed) : accountRegistryV1Schema.safeParse(parsed);
|
|
146
|
+
if (!result.success) {
|
|
147
|
+
const issues = result.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ");
|
|
148
|
+
throw new Error(`Invalid accounts registry at ${PATHS.ACCOUNTS_REGISTRY_PATH}: ${issues}`);
|
|
149
|
+
}
|
|
150
|
+
const parsedRegistry = result.data;
|
|
151
|
+
const registry = parsedRegistry.version === 2 ? parsedRegistry : {
|
|
152
|
+
version: 2,
|
|
153
|
+
accounts: parsedRegistry.accounts,
|
|
154
|
+
clientIdentities: {}
|
|
155
|
+
};
|
|
156
|
+
assertNoDuplicateAccounts(registry);
|
|
157
|
+
const identitiesBackfilled = ensureClientIdentitiesForAccounts(registry);
|
|
158
|
+
return {
|
|
159
|
+
registry,
|
|
160
|
+
shouldPersist: parsedRegistry.version !== 2 || identitiesBackfilled
|
|
161
|
+
};
|
|
162
|
+
} catch (error) {
|
|
163
|
+
if (error.code === "ENOENT") return {
|
|
164
|
+
registry: createEmptyRegistry(),
|
|
165
|
+
shouldPersist: false
|
|
166
|
+
};
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
const saveRegistryUnlocked = async (registry) => {
|
|
171
|
+
const content = JSON.stringify(registry, null, 2);
|
|
172
|
+
await fs.writeFile(PATHS.ACCOUNTS_REGISTRY_PATH, content, { mode: 384 });
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Load the accounts registry from disk.
|
|
176
|
+
* Returns an empty registry if the file doesn't exist.
|
|
177
|
+
*/
|
|
178
|
+
async function loadRegistry() {
|
|
179
|
+
return runWithRegistryLock(async () => {
|
|
180
|
+
const { registry, shouldPersist } = await loadRegistrySnapshot();
|
|
181
|
+
if (shouldPersist) await saveRegistryUnlocked(registry);
|
|
182
|
+
return registry;
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Save the accounts registry to disk with secure permissions.
|
|
187
|
+
*/
|
|
188
|
+
async function saveRegistry(registry) {
|
|
189
|
+
await runWithRegistryLock(async () => {
|
|
190
|
+
await saveRegistryUnlocked(registry);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async function getAccountClientIdentityByLoginAndApp(login, oauthApp) {
|
|
194
|
+
const registry = await loadRegistry();
|
|
195
|
+
const candidates = Object.values(registry.clientIdentities).filter((identity) => identity !== void 0 && identity.login === login && identity.oauthApp === oauthApp);
|
|
196
|
+
const preferredCandidates = candidates.filter((identity) => identity.enterpriseDomain !== DEFAULT_IDENTITY_ENTERPRISE_DOMAIN);
|
|
197
|
+
return (preferredCandidates.length > 0 ? preferredCandidates : candidates).reduce((latest, current) => {
|
|
198
|
+
if (!latest || current.createdAt > latest.createdAt) return current;
|
|
199
|
+
return latest;
|
|
200
|
+
}, null);
|
|
201
|
+
}
|
|
202
|
+
async function ensureAccountClientIdentity({ login, oauthApp, enterpriseDomain }) {
|
|
203
|
+
if (!validateAccountId(login)) throw new Error(`Invalid account ID: ${login}`);
|
|
204
|
+
const normalizedOauthApp = oauthApp.trim();
|
|
205
|
+
if (!normalizedOauthApp) throw new Error("OAuth app namespace must not be empty");
|
|
206
|
+
const normalizedEnterpriseDomain = enterpriseDomain.trim();
|
|
207
|
+
if (!normalizedEnterpriseDomain) throw new Error("Enterprise domain namespace must not be empty");
|
|
208
|
+
const identityKey = buildIdentityKey({
|
|
209
|
+
login,
|
|
210
|
+
oauthApp: normalizedOauthApp,
|
|
211
|
+
enterpriseDomain: normalizedEnterpriseDomain
|
|
212
|
+
});
|
|
213
|
+
const existingLock = identityLocks.get(identityKey);
|
|
214
|
+
if (existingLock) return existingLock;
|
|
215
|
+
const identityPromise = runWithRegistryLock(async () => {
|
|
216
|
+
const { registry, shouldPersist } = await loadRegistrySnapshot();
|
|
217
|
+
const existing = registry.clientIdentities[identityKey];
|
|
218
|
+
if (existing) {
|
|
219
|
+
if (shouldPersist) await saveRegistryUnlocked(registry);
|
|
220
|
+
return existing;
|
|
221
|
+
}
|
|
222
|
+
const created = createClientIdentity({
|
|
223
|
+
login,
|
|
224
|
+
oauthApp: normalizedOauthApp,
|
|
225
|
+
enterpriseDomain: normalizedEnterpriseDomain
|
|
226
|
+
});
|
|
227
|
+
registry.clientIdentities[identityKey] = created;
|
|
228
|
+
await saveRegistryUnlocked(registry);
|
|
229
|
+
return created;
|
|
230
|
+
});
|
|
231
|
+
identityLocks.set(identityKey, identityPromise);
|
|
232
|
+
try {
|
|
233
|
+
return await identityPromise;
|
|
234
|
+
} finally {
|
|
235
|
+
if (identityLocks.get(identityKey) === identityPromise) identityLocks.delete(identityKey);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Add an account to the registry.
|
|
240
|
+
* The account is appended to the end of the list (lowest priority).
|
|
241
|
+
*/
|
|
242
|
+
async function addAccountToRegistry(meta) {
|
|
243
|
+
if (!validateAccountId(meta.id)) throw new Error(`Invalid account ID: ${meta.id}`);
|
|
244
|
+
await runWithRegistryLock(async () => {
|
|
245
|
+
const { registry } = await loadRegistrySnapshot();
|
|
246
|
+
if (registry.accounts.some((a) => a.id === meta.id)) throw new Error(`Account already exists: ${meta.id}`);
|
|
247
|
+
registry.accounts.push(meta);
|
|
248
|
+
const { oauthApp, enterpriseDomain } = getCurrentIdentityEnvironment();
|
|
249
|
+
ensureRegistryIdentity(registry, {
|
|
250
|
+
login: meta.id,
|
|
251
|
+
oauthApp,
|
|
252
|
+
enterpriseDomain
|
|
253
|
+
});
|
|
254
|
+
await saveRegistryUnlocked(registry);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Remove an account from the registry by ID or index (1-based).
|
|
259
|
+
* Returns the removed account metadata.
|
|
260
|
+
*/
|
|
261
|
+
async function removeAccountFromRegistry(idOrIndex) {
|
|
262
|
+
return runWithRegistryLock(async () => {
|
|
263
|
+
const { registry } = await loadRegistrySnapshot();
|
|
264
|
+
let index;
|
|
265
|
+
if (typeof idOrIndex === "number") {
|
|
266
|
+
index = idOrIndex - 1;
|
|
267
|
+
if (index < 0 || index >= registry.accounts.length) throw new Error(`Invalid account index: ${idOrIndex}`);
|
|
268
|
+
} else {
|
|
269
|
+
index = registry.accounts.findIndex((a) => a.id === idOrIndex);
|
|
270
|
+
if (index === -1) throw new Error(`Account not found: ${idOrIndex}`);
|
|
271
|
+
}
|
|
272
|
+
const [removed] = registry.accounts.splice(index, 1);
|
|
273
|
+
await saveRegistryUnlocked(registry);
|
|
274
|
+
return removed;
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* List all accounts from the registry.
|
|
279
|
+
*/
|
|
280
|
+
async function listAccountsFromRegistry() {
|
|
281
|
+
return (await loadRegistry()).accounts;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Load the GitHub token for a specific account.
|
|
285
|
+
* Returns null if the token file doesn't exist.
|
|
286
|
+
*/
|
|
287
|
+
async function loadAccountToken(id) {
|
|
288
|
+
if (!validateAccountId(id)) throw new Error(`Invalid account ID: ${id}`);
|
|
289
|
+
try {
|
|
290
|
+
const tokenPath = accountTokenPath(id);
|
|
291
|
+
return (await fs.readFile(tokenPath, "utf8")).trim() || null;
|
|
292
|
+
} catch (error) {
|
|
293
|
+
if (error.code === "ENOENT") return null;
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Save the GitHub token for a specific account with secure permissions.
|
|
299
|
+
*/
|
|
300
|
+
async function saveAccountToken(id, token) {
|
|
301
|
+
if (!validateAccountId(id)) throw new Error(`Invalid account ID: ${id}`);
|
|
302
|
+
const tokenPath = accountTokenPath(id);
|
|
303
|
+
await fs.writeFile(tokenPath, token, { mode: 384 });
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Remove the GitHub token file for a specific account.
|
|
307
|
+
*/
|
|
308
|
+
async function removeAccountToken(id) {
|
|
309
|
+
if (!validateAccountId(id)) throw new Error(`Invalid account ID: ${id}`);
|
|
310
|
+
const tokenPath = accountTokenPath(id);
|
|
311
|
+
try {
|
|
312
|
+
await fs.unlink(tokenPath);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
if (error.code !== "ENOENT") throw error;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Check if the legacy github_token file exists.
|
|
319
|
+
*/
|
|
320
|
+
async function hasLegacyToken() {
|
|
321
|
+
try {
|
|
322
|
+
return (await fs.readFile(PATHS.GITHUB_TOKEN_PATH, "utf8")).trim().length > 0;
|
|
323
|
+
} catch {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Read the legacy github_token file.
|
|
329
|
+
* Returns null if the file doesn't exist or is empty.
|
|
330
|
+
*/
|
|
331
|
+
async function readLegacyToken() {
|
|
332
|
+
try {
|
|
333
|
+
return (await fs.readFile(PATHS.GITHUB_TOKEN_PATH, "utf8")).trim() || null;
|
|
334
|
+
} catch {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Check if the registry file exists and has accounts.
|
|
340
|
+
*/
|
|
341
|
+
async function hasRegistry() {
|
|
342
|
+
return (await loadRegistry()).accounts.length > 0;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
//#endregion
|
|
346
|
+
//#region src/lib/types/account.ts
|
|
347
|
+
const ACCOUNT_TYPE_VALUES = [
|
|
348
|
+
"individual",
|
|
349
|
+
"business",
|
|
350
|
+
"enterprise"
|
|
351
|
+
];
|
|
352
|
+
function isAccountType(value) {
|
|
353
|
+
return typeof value === "string" && ACCOUNT_TYPE_VALUES.includes(value);
|
|
354
|
+
}
|
|
355
|
+
function parseAccountType(value) {
|
|
356
|
+
if (!isAccountType(value)) throw new Error(`Invalid account type: ${String(value)}. Valid values: ${ACCOUNT_TYPE_VALUES.join(", ")}`);
|
|
357
|
+
return value;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
//#endregion
|
|
361
|
+
export { buildIdentityKey as _, getAccountClientIdentityByLoginAndApp as a, listAccountsFromRegistry as c, readLegacyToken as d, removeAccountFromRegistry as f, DEFAULT_IDENTITY_ENTERPRISE_DOMAIN as g, saveRegistry as h, ensureAccountClientIdentity as i, loadAccountToken as l, saveAccountToken as m, parseAccountType as n, hasLegacyToken as o, removeAccountToken as p, addAccountToRegistry as r, hasRegistry as s, isAccountType as t, loadRegistry as u, createAccountSessionId as v, getCurrentIdentityEnvironment as y };
|
|
362
|
+
//# sourceMappingURL=account-AacnHem5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-AacnHem5.js","names":["registryLock: Promise<void>","releaseLock!: () => void","parsed: unknown","registry: AccountRegistry","index: number","ACCOUNT_TYPE_VALUES: ReadonlyArray<AccountType>"],"sources":["../src/lib/account-client-identity.ts","../src/lib/accounts-registry.ts","../src/lib/types/account.ts"],"sourcesContent":["import { createHash, randomUUID } from \"node:crypto\"\n\nimport { normalizeDomain } from \"./api-config\"\n\nexport const DEFAULT_IDENTITY_OAUTH_APP = \"default\"\nexport const DEFAULT_IDENTITY_ENTERPRISE_DOMAIN = \"public\"\n\nexport interface AccountIdentityEnvironment {\n oauthApp: string\n enterpriseDomain: string\n}\n\nexport const getCurrentIdentityEnvironment = (): AccountIdentityEnvironment => {\n const rawOauthApp = process.env.COPILOT_API_OAUTH_APP?.trim().toLowerCase()\n const rawEnterpriseDomain = process.env.COPILOT_API_ENTERPRISE_URL?.trim()\n\n return {\n oauthApp: rawOauthApp || DEFAULT_IDENTITY_OAUTH_APP,\n enterpriseDomain:\n rawEnterpriseDomain ?\n normalizeDomain(rawEnterpriseDomain).toLowerCase()\n : DEFAULT_IDENTITY_ENTERPRISE_DOMAIN,\n }\n}\n\nexport const buildIdentityKey = ({\n login,\n oauthApp,\n enterpriseDomain,\n}: {\n login: string\n oauthApp: string\n enterpriseDomain: string\n}): string => `${enterpriseDomain}:${oauthApp}:${login}`\n\nexport const createAccountDeviceId = (): string => randomUUID().toLowerCase()\n\nexport const createAccountMachineId = (): string =>\n createHash(\"sha256\").update(randomUUID(), \"utf8\").digest(\"hex\")\n\nexport const createAccountSessionId = (): string =>\n randomUUID() + Date.now().toString()\n","import fs from \"node:fs/promises\"\nimport { z } from \"zod\"\n\nimport type {\n AccountClientIdentity,\n AccountMeta,\n AccountRegistry,\n} from \"~/lib/types/account\"\n\nimport {\n DEFAULT_IDENTITY_ENTERPRISE_DOMAIN,\n buildIdentityKey,\n createAccountDeviceId,\n createAccountMachineId,\n getCurrentIdentityEnvironment,\n} from \"~/lib/account-client-identity\"\nimport { accountTokenPath, PATHS } from \"~/lib/paths\"\n\n/**\n * Validate account ID (GitHub login).\n * Rules:\n * - Only alphanumeric characters or single hyphens\n * - 1-39 chars\n * - Cannot begin or end with a hyphen\n * - No consecutive hyphens\n */\nexport function validateAccountId(id: string): boolean {\n if (id.length === 0 || id.length > 39) return false\n if (!/^[a-z0-9-]+$/i.test(id)) return false\n if (id.startsWith(\"-\") || id.endsWith(\"-\")) return false\n if (id.includes(\"--\")) return false\n return true\n}\n\nconst accountMetaSchema = z.object({\n id: z.string().refine(validateAccountId, {\n message:\n \"Invalid account id. Expected a GitHub login (1-39 chars, alphanumeric or single hyphens, no leading/trailing hyphen, no consecutive hyphens).\",\n }),\n accountType: z.enum([\"individual\", \"business\", \"enterprise\"]),\n addedAt: z.number(),\n})\n\nconst accountClientIdentitySchema = z.object({\n login: z.string().refine(validateAccountId, {\n message:\n \"Invalid client identity login. Expected a GitHub login (1-39 chars, alphanumeric or single hyphens, no leading/trailing hyphen, no consecutive hyphens).\",\n }),\n oauthApp: z.string().min(1),\n enterpriseDomain: z.string().min(1),\n deviceId: z\n .string()\n .regex(\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u,\n \"Invalid device ID format. Expected a lowercase UUID.\",\n ),\n machineId: z\n .string()\n .regex(\n /^[0-9a-f]{64}$/u,\n \"Invalid machine ID format. Expected 64 lowercase hexadecimal characters.\",\n ),\n createdAt: z.number(),\n})\n\nconst accountRegistryV1Schema = z.object({\n version: z.literal(1),\n accounts: z.array(accountMetaSchema),\n})\n\nconst accountRegistryV2Schema = z.object({\n version: z.literal(2),\n accounts: z.array(accountMetaSchema),\n clientIdentities: z.record(z.string(), accountClientIdentitySchema),\n})\n\nconst identityLocks = new Map<string, Promise<AccountClientIdentity>>()\nlet registryLock: Promise<void> = Promise.resolve()\n\nconst runWithRegistryLock = async <T>(\n operation: () => Promise<T>,\n): Promise<T> => {\n const previousLock = registryLock\n let releaseLock!: () => void\n registryLock = new Promise<void>((resolve) => {\n releaseLock = resolve\n })\n\n await previousLock\n\n try {\n return await operation()\n } finally {\n releaseLock()\n }\n}\n\n/**\n * Create an empty registry with the current schema version.\n */\nfunction createEmptyRegistry(): AccountRegistry {\n return {\n version: 2,\n accounts: [],\n clientIdentities: {},\n }\n}\n\nconst createClientIdentity = ({\n login,\n oauthApp,\n enterpriseDomain,\n}: {\n login: string\n oauthApp: string\n enterpriseDomain: string\n}): AccountClientIdentity => ({\n login,\n oauthApp,\n enterpriseDomain,\n deviceId: createAccountDeviceId(),\n machineId: createAccountMachineId(),\n createdAt: Date.now(),\n})\n\nconst ensureRegistryIdentity = (\n registry: AccountRegistry,\n {\n login,\n oauthApp,\n enterpriseDomain,\n }: {\n login: string\n oauthApp: string\n enterpriseDomain: string\n },\n): AccountClientIdentity => {\n const identityKey = buildIdentityKey({ login, oauthApp, enterpriseDomain })\n const existing = registry.clientIdentities[identityKey]\n if (existing) {\n return existing\n }\n\n const created = createClientIdentity({\n login,\n oauthApp,\n enterpriseDomain,\n })\n registry.clientIdentities[identityKey] = created\n return created\n}\n\nconst ensureClientIdentitiesForAccounts = (\n registry: AccountRegistry,\n): boolean => {\n const { oauthApp, enterpriseDomain } = getCurrentIdentityEnvironment()\n const countBefore = Object.keys(registry.clientIdentities).length\n\n for (const account of registry.accounts) {\n ensureRegistryIdentity(registry, {\n login: account.id,\n oauthApp,\n enterpriseDomain,\n })\n }\n\n return Object.keys(registry.clientIdentities).length !== countBefore\n}\n\nconst assertNoDuplicateAccounts = (registry: {\n accounts: Array<AccountMeta>\n}) => {\n const seen = new Set<string>()\n for (const account of registry.accounts) {\n if (seen.has(account.id)) {\n throw new Error(\n `Invalid accounts registry at ${PATHS.ACCOUNTS_REGISTRY_PATH}: duplicate account id \"${account.id}\"`,\n )\n }\n seen.add(account.id)\n }\n}\n\nconst loadRegistrySnapshot = async (): Promise<{\n registry: AccountRegistry\n shouldPersist: boolean\n}> => {\n try {\n const content = await fs.readFile(PATHS.ACCOUNTS_REGISTRY_PATH, \"utf8\")\n if (!content.trim()) {\n return {\n registry: createEmptyRegistry(),\n shouldPersist: false,\n }\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(content) as unknown\n } catch (error) {\n throw new Error(\n `Invalid accounts registry JSON at ${PATHS.ACCOUNTS_REGISTRY_PATH}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n )\n }\n\n const isVersion2Record =\n typeof parsed === \"object\"\n && parsed !== null\n && \"version\" in parsed\n && parsed.version === 2\n const result =\n isVersion2Record ?\n accountRegistryV2Schema.safeParse(parsed)\n : accountRegistryV1Schema.safeParse(parsed)\n if (!result.success) {\n const issues = result.error.issues\n .map((issue) => `${issue.path.join(\".\")}: ${issue.message}`)\n .join(\"; \")\n\n throw new Error(\n `Invalid accounts registry at ${PATHS.ACCOUNTS_REGISTRY_PATH}: ${issues}`,\n )\n }\n\n const parsedRegistry = result.data\n const registry: AccountRegistry =\n parsedRegistry.version === 2 ?\n parsedRegistry\n : {\n version: 2,\n accounts: parsedRegistry.accounts,\n clientIdentities: {},\n }\n\n assertNoDuplicateAccounts(registry)\n\n const identitiesBackfilled = ensureClientIdentitiesForAccounts(registry)\n\n return {\n registry,\n shouldPersist: parsedRegistry.version !== 2 || identitiesBackfilled,\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return {\n registry: createEmptyRegistry(),\n shouldPersist: false,\n }\n }\n throw error\n }\n}\n\nconst saveRegistryUnlocked = async (\n registry: AccountRegistry,\n): Promise<void> => {\n const content = JSON.stringify(registry, null, 2)\n await fs.writeFile(PATHS.ACCOUNTS_REGISTRY_PATH, content, { mode: 0o600 })\n}\n\n/**\n * Load the accounts registry from disk.\n * Returns an empty registry if the file doesn't exist.\n */\nexport async function loadRegistry(): Promise<AccountRegistry> {\n return runWithRegistryLock(async () => {\n const { registry, shouldPersist } = await loadRegistrySnapshot()\n if (shouldPersist) {\n await saveRegistryUnlocked(registry)\n }\n return registry\n })\n}\n\n/**\n * Save the accounts registry to disk with secure permissions.\n */\nexport async function saveRegistry(registry: AccountRegistry): Promise<void> {\n await runWithRegistryLock(async () => {\n await saveRegistryUnlocked(registry)\n })\n}\n\nexport async function getAccountClientIdentity(\n identityKey: string,\n): Promise<AccountClientIdentity | null> {\n const registry = await loadRegistry()\n return registry.clientIdentities[identityKey] ?? null\n}\n\nexport async function getAccountClientIdentityByLoginAndApp(\n login: string,\n oauthApp: string,\n): Promise<AccountClientIdentity | null> {\n const registry = await loadRegistry()\n\n const candidates = Object.values(registry.clientIdentities).filter(\n (identity): identity is AccountClientIdentity =>\n identity !== undefined\n && identity.login === login\n && identity.oauthApp === oauthApp,\n )\n\n const preferredCandidates = candidates.filter(\n (identity) =>\n identity.enterpriseDomain !== DEFAULT_IDENTITY_ENTERPRISE_DOMAIN,\n )\n const selectionPool =\n preferredCandidates.length > 0 ? preferredCandidates : candidates\n\n return selectionPool.reduce<AccountClientIdentity | null>(\n (latest, current) => {\n if (!latest || current.createdAt > latest.createdAt) return current\n return latest\n },\n null,\n )\n}\n\nexport async function ensureAccountClientIdentity({\n login,\n oauthApp,\n enterpriseDomain,\n}: {\n login: string\n oauthApp: string\n enterpriseDomain: string\n}): Promise<AccountClientIdentity> {\n if (!validateAccountId(login)) {\n throw new Error(`Invalid account ID: ${login}`)\n }\n\n const normalizedOauthApp = oauthApp.trim()\n if (!normalizedOauthApp) {\n throw new Error(\"OAuth app namespace must not be empty\")\n }\n\n const normalizedEnterpriseDomain = enterpriseDomain.trim()\n if (!normalizedEnterpriseDomain) {\n throw new Error(\"Enterprise domain namespace must not be empty\")\n }\n\n const identityKey = buildIdentityKey({\n login,\n oauthApp: normalizedOauthApp,\n enterpriseDomain: normalizedEnterpriseDomain,\n })\n const existingLock = identityLocks.get(identityKey)\n if (existingLock) {\n return existingLock\n }\n\n const identityPromise = runWithRegistryLock(\n async (): Promise<AccountClientIdentity> => {\n const { registry, shouldPersist } = await loadRegistrySnapshot()\n const existing = registry.clientIdentities[identityKey]\n if (existing) {\n if (shouldPersist) {\n await saveRegistryUnlocked(registry)\n }\n return existing\n }\n\n const created = createClientIdentity({\n login,\n oauthApp: normalizedOauthApp,\n enterpriseDomain: normalizedEnterpriseDomain,\n })\n registry.clientIdentities[identityKey] = created\n await saveRegistryUnlocked(registry)\n return created\n },\n )\n\n identityLocks.set(identityKey, identityPromise)\n\n try {\n return await identityPromise\n } finally {\n if (identityLocks.get(identityKey) === identityPromise) {\n identityLocks.delete(identityKey)\n }\n }\n}\n\n/**\n * Add an account to the registry.\n * The account is appended to the end of the list (lowest priority).\n */\nexport async function addAccountToRegistry(meta: AccountMeta): Promise<void> {\n if (!validateAccountId(meta.id)) {\n throw new Error(`Invalid account ID: ${meta.id}`)\n }\n\n await runWithRegistryLock(async () => {\n const { registry } = await loadRegistrySnapshot()\n\n // Check for duplicate\n if (registry.accounts.some((a) => a.id === meta.id)) {\n throw new Error(`Account already exists: ${meta.id}`)\n }\n\n registry.accounts.push(meta)\n const { oauthApp, enterpriseDomain } = getCurrentIdentityEnvironment()\n ensureRegistryIdentity(registry, {\n login: meta.id,\n oauthApp,\n enterpriseDomain,\n })\n await saveRegistryUnlocked(registry)\n })\n}\n\n/**\n * Remove an account from the registry by ID or index (1-based).\n * Returns the removed account metadata.\n */\nexport async function removeAccountFromRegistry(\n idOrIndex: string | number,\n): Promise<AccountMeta> {\n return runWithRegistryLock(async () => {\n const { registry } = await loadRegistrySnapshot()\n let index: number\n\n if (typeof idOrIndex === \"number\") {\n // 1-based index\n index = idOrIndex - 1\n if (index < 0 || index >= registry.accounts.length) {\n throw new Error(`Invalid account index: ${idOrIndex}`)\n }\n } else {\n index = registry.accounts.findIndex((a) => a.id === idOrIndex)\n if (index === -1) {\n throw new Error(`Account not found: ${idOrIndex}`)\n }\n }\n\n const [removed] = registry.accounts.splice(index, 1)\n await saveRegistryUnlocked(registry)\n return removed\n })\n}\n\n/**\n * List all accounts from the registry.\n */\nexport async function listAccountsFromRegistry(): Promise<Array<AccountMeta>> {\n const registry = await loadRegistry()\n return registry.accounts\n}\n\n/**\n * Load the GitHub token for a specific account.\n * Returns null if the token file doesn't exist.\n */\nexport async function loadAccountToken(id: string): Promise<string | null> {\n if (!validateAccountId(id)) {\n throw new Error(`Invalid account ID: ${id}`)\n }\n\n try {\n const tokenPath = accountTokenPath(id)\n const token = await fs.readFile(tokenPath, \"utf8\")\n return token.trim() || null\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return null\n }\n throw error\n }\n}\n\n/**\n * Save the GitHub token for a specific account with secure permissions.\n */\nexport async function saveAccountToken(\n id: string,\n token: string,\n): Promise<void> {\n if (!validateAccountId(id)) {\n throw new Error(`Invalid account ID: ${id}`)\n }\n\n const tokenPath = accountTokenPath(id)\n await fs.writeFile(tokenPath, token, { mode: 0o600 })\n}\n\n/**\n * Remove the GitHub token file for a specific account.\n */\nexport async function removeAccountToken(id: string): Promise<void> {\n if (!validateAccountId(id)) {\n throw new Error(`Invalid account ID: ${id}`)\n }\n\n const tokenPath = accountTokenPath(id)\n try {\n await fs.unlink(tokenPath)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw error\n }\n // File doesn't exist, nothing to remove\n }\n}\n\n/**\n * Check if the legacy github_token file exists.\n */\nexport async function hasLegacyToken(): Promise<boolean> {\n try {\n const content = await fs.readFile(PATHS.GITHUB_TOKEN_PATH, \"utf8\")\n return content.trim().length > 0\n } catch {\n return false\n }\n}\n\n/**\n * Read the legacy github_token file.\n * Returns null if the file doesn't exist or is empty.\n */\nexport async function readLegacyToken(): Promise<string | null> {\n try {\n const content = await fs.readFile(PATHS.GITHUB_TOKEN_PATH, \"utf8\")\n return content.trim() || null\n } catch {\n return null\n }\n}\n\n/**\n * Check if the registry file exists and has accounts.\n */\nexport async function hasRegistry(): Promise<boolean> {\n const registry = await loadRegistry()\n return registry.accounts.length > 0\n}\n","import type { ModelsResponse } from \"~/services/copilot/get-models\"\n\n/**\n * Account type for GitHub Copilot subscription.\n */\nexport type AccountType = \"individual\" | \"business\" | \"enterprise\"\n\nexport const ACCOUNT_TYPE_VALUES: ReadonlyArray<AccountType> = [\n \"individual\",\n \"business\",\n \"enterprise\",\n]\n\nexport function isAccountType(value: unknown): value is AccountType {\n return (\n typeof value === \"string\"\n && (ACCOUNT_TYPE_VALUES as ReadonlyArray<string>).includes(value)\n )\n}\n\nexport function parseAccountType(value: unknown): AccountType {\n if (!isAccountType(value)) {\n throw new Error(\n `Invalid account type: ${String(value)}. Valid values: ${ACCOUNT_TYPE_VALUES.join(\n \", \",\n )}`,\n )\n }\n return value\n}\n\n/**\n * Metadata for a registered account, stored in the registry file.\n */\nexport interface AccountMeta {\n /** GitHub login (username) */\n id: string\n /** Account subscription type */\n accountType: AccountType\n /** Timestamp when the account was added */\n addedAt: number\n}\n\nexport interface AccountClientIdentity {\n /** Real GitHub login */\n login: string\n /** OAuth app namespace */\n oauthApp: string\n /** Enterprise domain namespace (\"public\" for github.com) */\n enterpriseDomain: string\n /** Account-scoped upstream device identifier */\n deviceId: string\n /** Account-scoped upstream machine identifier */\n machineId: string\n /** Creation timestamp for debugging/auditing */\n createdAt: number\n}\n\n/**\n * Registry file structure for storing account metadata.\n */\nexport interface AccountRegistry {\n /** Schema version for future migrations */\n version: 2\n /** Ordered list of accounts (order = priority) */\n accounts: Array<AccountMeta>\n /** Persistent client identities keyed by logical environment + login */\n clientIdentities: Partial<Record<string, AccountClientIdentity>>\n}\n\n/**\n * Runtime state for an account, including tokens and quota information.\n */\nexport interface AccountRuntime extends AccountMeta {\n /** Real GitHub login, used to resolve account-scoped identity */\n accountLogin?: string\n /** Persistent identity key used to load/store account-scoped identifiers */\n identityKey?: string\n /** GitHub personal access token */\n githubToken: string\n /** Copilot API token (obtained from GitHub) */\n copilotToken?: string\n /** Account-specific Copilot API base URL returned by GitHub */\n copilotApiUrl?: string\n /** VS Code version for API headers */\n vsCodeVersion?: string\n /** Account-scoped device identifier sent upstream */\n clientDeviceId?: string\n /** Account-scoped machine identifier sent upstream */\n clientMachineId?: string\n /** Account-scoped session identifier sent upstream */\n clientSessionId?: string\n /** Session refresh timer reference */\n sessionRefreshTimer?: ReturnType<typeof setTimeout>\n /** Cached available models for this account */\n models?: ModelsResponse\n /** Timestamp of last models fetch */\n lastModelsFetch?: number\n /** Whether models refresh is in progress */\n isRefreshingModels?: boolean\n /** Promise for an in-flight models refresh */\n modelsRefreshPromise?: Promise<void>\n /** Total premium interactions quota entitlement */\n premiumEntitlement?: number\n /** Remaining premium interactions quota */\n premiumRemaining?: number\n /** Reserved premium interaction units for in-flight requests */\n premiumReserved?: number\n /** Internal reservation map for idempotent release */\n premiumReservations?: Map<symbol, number>\n /** Whether this account has unlimited quota */\n unlimited?: boolean\n /** Whether this account allows overage billing (enterprise feature) */\n overagePermitted?: boolean\n /** Timestamp of last quota fetch */\n lastQuotaFetch?: number\n /** Token refresh timer reference */\n refreshTimer?: ReturnType<typeof setInterval>\n /** Whether this account has failed (e.g., 401 error) */\n failed?: boolean\n /** Failure reason if failed */\n failureReason?: string\n /** Whether quota refresh is in progress (prevents concurrent refreshes) */\n isRefreshingQuota?: boolean\n /** Promise for an in-flight quota refresh (allows concurrent callers to await the same refresh) */\n quotaRefreshPromise?: Promise<void>\n}\n\n/**\n * Context required for making API calls on behalf of an account.\n * This is a subset of AccountRuntime used by service functions.\n */\nexport interface AccountContext {\n /** Real GitHub login */\n accountLogin?: string\n /** GitHub personal access token */\n githubToken: string\n /** Copilot API token */\n copilotToken?: string\n /** Account-specific Copilot API base URL */\n copilotApiUrl?: string\n /** Account subscription type */\n accountType: AccountType\n /** VS Code version for API headers */\n vsCodeVersion?: string\n /** Account-scoped device identifier */\n clientDeviceId?: string\n /** Account-scoped machine identifier */\n clientMachineId?: string\n /** Account-scoped session identifier */\n clientSessionId?: string\n}\n"],"mappings":";;;;;;;AAIA,MAAa,6BAA6B;AAC1C,MAAa,qCAAqC;AAOlD,MAAa,sCAAkE;CAC7E,MAAM,cAAc,QAAQ,IAAI,uBAAuB,MAAM,CAAC,aAAa;CAC3E,MAAM,sBAAsB,QAAQ,IAAI,4BAA4B,MAAM;AAE1E,QAAO;EACL,UAAU,eAAe;EACzB,kBACE,sBACE,gBAAgB,oBAAoB,CAAC,aAAa,GAClD;EACL;;AAGH,MAAa,oBAAoB,EAC/B,OACA,UACA,uBAKY,GAAG,iBAAiB,GAAG,SAAS,GAAG;AAEjD,MAAa,8BAAsC,YAAY,CAAC,aAAa;AAE7E,MAAa,+BACX,WAAW,SAAS,CAAC,OAAO,YAAY,EAAE,OAAO,CAAC,OAAO,MAAM;AAEjE,MAAa,+BACX,YAAY,GAAG,KAAK,KAAK,CAAC,UAAU;;;;;;;;;;;;ACftC,SAAgB,kBAAkB,IAAqB;AACrD,KAAI,GAAG,WAAW,KAAK,GAAG,SAAS,GAAI,QAAO;AAC9C,KAAI,CAAC,gBAAgB,KAAK,GAAG,CAAE,QAAO;AACtC,KAAI,GAAG,WAAW,IAAI,IAAI,GAAG,SAAS,IAAI,CAAE,QAAO;AACnD,KAAI,GAAG,SAAS,KAAK,CAAE,QAAO;AAC9B,QAAO;;AAGT,MAAM,oBAAoB,EAAE,OAAO;CACjC,IAAI,EAAE,QAAQ,CAAC,OAAO,mBAAmB,EACvC,SACE,iJACH,CAAC;CACF,aAAa,EAAE,KAAK;EAAC;EAAc;EAAY;EAAa,CAAC;CAC7D,SAAS,EAAE,QAAQ;CACpB,CAAC;AAEF,MAAM,8BAA8B,EAAE,OAAO;CAC3C,OAAO,EAAE,QAAQ,CAAC,OAAO,mBAAmB,EAC1C,SACE,4JACH,CAAC;CACF,UAAU,EAAE,QAAQ,CAAC,IAAI,EAAE;CAC3B,kBAAkB,EAAE,QAAQ,CAAC,IAAI,EAAE;CACnC,UAAU,EACP,QAAQ,CACR,MACC,mEACA,uDACD;CACH,WAAW,EACR,QAAQ,CACR,MACC,mBACA,2EACD;CACH,WAAW,EAAE,QAAQ;CACtB,CAAC;AAEF,MAAM,0BAA0B,EAAE,OAAO;CACvC,SAAS,EAAE,QAAQ,EAAE;CACrB,UAAU,EAAE,MAAM,kBAAkB;CACrC,CAAC;AAEF,MAAM,0BAA0B,EAAE,OAAO;CACvC,SAAS,EAAE,QAAQ,EAAE;CACrB,UAAU,EAAE,MAAM,kBAAkB;CACpC,kBAAkB,EAAE,OAAO,EAAE,QAAQ,EAAE,4BAA4B;CACpE,CAAC;AAEF,MAAM,gCAAgB,IAAI,KAA6C;AACvE,IAAIA,eAA8B,QAAQ,SAAS;AAEnD,MAAM,sBAAsB,OAC1B,cACe;CACf,MAAM,eAAe;CACrB,IAAIC;AACJ,gBAAe,IAAI,SAAe,YAAY;AAC5C,gBAAc;GACd;AAEF,OAAM;AAEN,KAAI;AACF,SAAO,MAAM,WAAW;WAChB;AACR,eAAa;;;;;;AAOjB,SAAS,sBAAuC;AAC9C,QAAO;EACL,SAAS;EACT,UAAU,EAAE;EACZ,kBAAkB,EAAE;EACrB;;AAGH,MAAM,wBAAwB,EAC5B,OACA,UACA,wBAK4B;CAC5B;CACA;CACA;CACA,UAAU,uBAAuB;CACjC,WAAW,wBAAwB;CACnC,WAAW,KAAK,KAAK;CACtB;AAED,MAAM,0BACJ,UACA,EACE,OACA,UACA,uBAMwB;CAC1B,MAAM,cAAc,iBAAiB;EAAE;EAAO;EAAU;EAAkB,CAAC;CAC3E,MAAM,WAAW,SAAS,iBAAiB;AAC3C,KAAI,SACF,QAAO;CAGT,MAAM,UAAU,qBAAqB;EACnC;EACA;EACA;EACD,CAAC;AACF,UAAS,iBAAiB,eAAe;AACzC,QAAO;;AAGT,MAAM,qCACJ,aACY;CACZ,MAAM,EAAE,UAAU,qBAAqB,+BAA+B;CACtE,MAAM,cAAc,OAAO,KAAK,SAAS,iBAAiB,CAAC;AAE3D,MAAK,MAAM,WAAW,SAAS,SAC7B,wBAAuB,UAAU;EAC/B,OAAO,QAAQ;EACf;EACA;EACD,CAAC;AAGJ,QAAO,OAAO,KAAK,SAAS,iBAAiB,CAAC,WAAW;;AAG3D,MAAM,6BAA6B,aAE7B;CACJ,MAAM,uBAAO,IAAI,KAAa;AAC9B,MAAK,MAAM,WAAW,SAAS,UAAU;AACvC,MAAI,KAAK,IAAI,QAAQ,GAAG,CACtB,OAAM,IAAI,MACR,gCAAgC,MAAM,uBAAuB,0BAA0B,QAAQ,GAAG,GACnG;AAEH,OAAK,IAAI,QAAQ,GAAG;;;AAIxB,MAAM,uBAAuB,YAGvB;AACJ,KAAI;EACF,MAAM,UAAU,MAAM,GAAG,SAAS,MAAM,wBAAwB,OAAO;AACvE,MAAI,CAAC,QAAQ,MAAM,CACjB,QAAO;GACL,UAAU,qBAAqB;GAC/B,eAAe;GAChB;EAGH,IAAIC;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,QAAQ;WACrB,OAAO;AACd,SAAM,IAAI,MACR,qCAAqC,MAAM,uBAAuB,IAChE,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GAEzD;;EAQH,MAAM,SAJJ,OAAO,WAAW,YACf,WAAW,QACX,aAAa,UACb,OAAO,YAAY,IAGpB,wBAAwB,UAAU,OAAO,GACzC,wBAAwB,UAAU,OAAO;AAC7C,MAAI,CAAC,OAAO,SAAS;GACnB,MAAM,SAAS,OAAO,MAAM,OACzB,KAAK,UAAU,GAAG,MAAM,KAAK,KAAK,IAAI,CAAC,IAAI,MAAM,UAAU,CAC3D,KAAK,KAAK;AAEb,SAAM,IAAI,MACR,gCAAgC,MAAM,uBAAuB,IAAI,SAClE;;EAGH,MAAM,iBAAiB,OAAO;EAC9B,MAAMC,WACJ,eAAe,YAAY,IACzB,iBACA;GACE,SAAS;GACT,UAAU,eAAe;GACzB,kBAAkB,EAAE;GACrB;AAEL,4BAA0B,SAAS;EAEnC,MAAM,uBAAuB,kCAAkC,SAAS;AAExE,SAAO;GACL;GACA,eAAe,eAAe,YAAY,KAAK;GAChD;UACM,OAAO;AACd,MAAK,MAAgC,SAAS,SAC5C,QAAO;GACL,UAAU,qBAAqB;GAC/B,eAAe;GAChB;AAEH,QAAM;;;AAIV,MAAM,uBAAuB,OAC3B,aACkB;CAClB,MAAM,UAAU,KAAK,UAAU,UAAU,MAAM,EAAE;AACjD,OAAM,GAAG,UAAU,MAAM,wBAAwB,SAAS,EAAE,MAAM,KAAO,CAAC;;;;;;AAO5E,eAAsB,eAAyC;AAC7D,QAAO,oBAAoB,YAAY;EACrC,MAAM,EAAE,UAAU,kBAAkB,MAAM,sBAAsB;AAChE,MAAI,cACF,OAAM,qBAAqB,SAAS;AAEtC,SAAO;GACP;;;;;AAMJ,eAAsB,aAAa,UAA0C;AAC3E,OAAM,oBAAoB,YAAY;AACpC,QAAM,qBAAqB,SAAS;GACpC;;AAUJ,eAAsB,sCACpB,OACA,UACuC;CACvC,MAAM,WAAW,MAAM,cAAc;CAErC,MAAM,aAAa,OAAO,OAAO,SAAS,iBAAiB,CAAC,QACzD,aACC,aAAa,UACV,SAAS,UAAU,SACnB,SAAS,aAAa,SAC5B;CAED,MAAM,sBAAsB,WAAW,QACpC,aACC,SAAS,qBAAqB,mCACjC;AAID,SAFE,oBAAoB,SAAS,IAAI,sBAAsB,YAEpC,QAClB,QAAQ,YAAY;AACnB,MAAI,CAAC,UAAU,QAAQ,YAAY,OAAO,UAAW,QAAO;AAC5D,SAAO;IAET,KACD;;AAGH,eAAsB,4BAA4B,EAChD,OACA,UACA,oBAKiC;AACjC,KAAI,CAAC,kBAAkB,MAAM,CAC3B,OAAM,IAAI,MAAM,uBAAuB,QAAQ;CAGjD,MAAM,qBAAqB,SAAS,MAAM;AAC1C,KAAI,CAAC,mBACH,OAAM,IAAI,MAAM,wCAAwC;CAG1D,MAAM,6BAA6B,iBAAiB,MAAM;AAC1D,KAAI,CAAC,2BACH,OAAM,IAAI,MAAM,gDAAgD;CAGlE,MAAM,cAAc,iBAAiB;EACnC;EACA,UAAU;EACV,kBAAkB;EACnB,CAAC;CACF,MAAM,eAAe,cAAc,IAAI,YAAY;AACnD,KAAI,aACF,QAAO;CAGT,MAAM,kBAAkB,oBACtB,YAA4C;EAC1C,MAAM,EAAE,UAAU,kBAAkB,MAAM,sBAAsB;EAChE,MAAM,WAAW,SAAS,iBAAiB;AAC3C,MAAI,UAAU;AACZ,OAAI,cACF,OAAM,qBAAqB,SAAS;AAEtC,UAAO;;EAGT,MAAM,UAAU,qBAAqB;GACnC;GACA,UAAU;GACV,kBAAkB;GACnB,CAAC;AACF,WAAS,iBAAiB,eAAe;AACzC,QAAM,qBAAqB,SAAS;AACpC,SAAO;GAEV;AAED,eAAc,IAAI,aAAa,gBAAgB;AAE/C,KAAI;AACF,SAAO,MAAM;WACL;AACR,MAAI,cAAc,IAAI,YAAY,KAAK,gBACrC,eAAc,OAAO,YAAY;;;;;;;AASvC,eAAsB,qBAAqB,MAAkC;AAC3E,KAAI,CAAC,kBAAkB,KAAK,GAAG,CAC7B,OAAM,IAAI,MAAM,uBAAuB,KAAK,KAAK;AAGnD,OAAM,oBAAoB,YAAY;EACpC,MAAM,EAAE,aAAa,MAAM,sBAAsB;AAGjD,MAAI,SAAS,SAAS,MAAM,MAAM,EAAE,OAAO,KAAK,GAAG,CACjD,OAAM,IAAI,MAAM,2BAA2B,KAAK,KAAK;AAGvD,WAAS,SAAS,KAAK,KAAK;EAC5B,MAAM,EAAE,UAAU,qBAAqB,+BAA+B;AACtE,yBAAuB,UAAU;GAC/B,OAAO,KAAK;GACZ;GACA;GACD,CAAC;AACF,QAAM,qBAAqB,SAAS;GACpC;;;;;;AAOJ,eAAsB,0BACpB,WACsB;AACtB,QAAO,oBAAoB,YAAY;EACrC,MAAM,EAAE,aAAa,MAAM,sBAAsB;EACjD,IAAIC;AAEJ,MAAI,OAAO,cAAc,UAAU;AAEjC,WAAQ,YAAY;AACpB,OAAI,QAAQ,KAAK,SAAS,SAAS,SAAS,OAC1C,OAAM,IAAI,MAAM,0BAA0B,YAAY;SAEnD;AACL,WAAQ,SAAS,SAAS,WAAW,MAAM,EAAE,OAAO,UAAU;AAC9D,OAAI,UAAU,GACZ,OAAM,IAAI,MAAM,sBAAsB,YAAY;;EAItD,MAAM,CAAC,WAAW,SAAS,SAAS,OAAO,OAAO,EAAE;AACpD,QAAM,qBAAqB,SAAS;AACpC,SAAO;GACP;;;;;AAMJ,eAAsB,2BAAwD;AAE5E,SADiB,MAAM,cAAc,EACrB;;;;;;AAOlB,eAAsB,iBAAiB,IAAoC;AACzE,KAAI,CAAC,kBAAkB,GAAG,CACxB,OAAM,IAAI,MAAM,uBAAuB,KAAK;AAG9C,KAAI;EACF,MAAM,YAAY,iBAAiB,GAAG;AAEtC,UADc,MAAM,GAAG,SAAS,WAAW,OAAO,EACrC,MAAM,IAAI;UAChB,OAAO;AACd,MAAK,MAAgC,SAAS,SAC5C,QAAO;AAET,QAAM;;;;;;AAOV,eAAsB,iBACpB,IACA,OACe;AACf,KAAI,CAAC,kBAAkB,GAAG,CACxB,OAAM,IAAI,MAAM,uBAAuB,KAAK;CAG9C,MAAM,YAAY,iBAAiB,GAAG;AACtC,OAAM,GAAG,UAAU,WAAW,OAAO,EAAE,MAAM,KAAO,CAAC;;;;;AAMvD,eAAsB,mBAAmB,IAA2B;AAClE,KAAI,CAAC,kBAAkB,GAAG,CACxB,OAAM,IAAI,MAAM,uBAAuB,KAAK;CAG9C,MAAM,YAAY,iBAAiB,GAAG;AACtC,KAAI;AACF,QAAM,GAAG,OAAO,UAAU;UACnB,OAAO;AACd,MAAK,MAAgC,SAAS,SAC5C,OAAM;;;;;;AASZ,eAAsB,iBAAmC;AACvD,KAAI;AAEF,UADgB,MAAM,GAAG,SAAS,MAAM,mBAAmB,OAAO,EACnD,MAAM,CAAC,SAAS;SACzB;AACN,SAAO;;;;;;;AAQX,eAAsB,kBAA0C;AAC9D,KAAI;AAEF,UADgB,MAAM,GAAG,SAAS,MAAM,mBAAmB,OAAO,EACnD,MAAM,IAAI;SACnB;AACN,SAAO;;;;;;AAOX,eAAsB,cAAgC;AAEpD,SADiB,MAAM,cAAc,EACrB,SAAS,SAAS;;;;;ACnhBpC,MAAaC,sBAAkD;CAC7D;CACA;CACA;CACD;AAED,SAAgB,cAAc,OAAsC;AAClE,QACE,OAAO,UAAU,YACb,oBAA8C,SAAS,MAAM;;AAIrE,SAAgB,iBAAiB,OAA6B;AAC5D,KAAI,CAAC,cAAc,MAAM,CACvB,OAAM,IAAI,MACR,yBAAyB,OAAO,MAAM,CAAC,kBAAkB,oBAAoB,KAC3E,KACD,GACF;AAEH,QAAO"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { t as getCopilotToken } from "./get-copilot-token-
|
|
1
|
+
import { _ as HTTPError, g as getCopilotUsage, m as getGitHubUser, p as getModels } from "./poll-access-token-DFooFWhY.js";
|
|
2
|
+
import { _ as buildIdentityKey, c as listAccountsFromRegistry, d as readLegacyToken, i as ensureAccountClientIdentity, l as loadAccountToken, m as saveAccountToken, o as hasLegacyToken, r as addAccountToRegistry, s as hasRegistry, v as createAccountSessionId, y as getCurrentIdentityEnvironment } from "./account-AacnHem5.js";
|
|
3
|
+
import { t as PATHS } from "./paths-DGlr310R.js";
|
|
4
|
+
import { t as getCopilotToken } from "./get-copilot-token-cha9rQwA.js";
|
|
5
5
|
import consola from "consola";
|
|
6
6
|
import fs from "node:fs";
|
|
7
7
|
|
|
@@ -485,11 +485,15 @@ const isSameAuthSnapshot = (a, b) => {
|
|
|
485
485
|
return a.githubToken === b.githubToken && a.accountType === b.accountType;
|
|
486
486
|
};
|
|
487
487
|
const toAccountContextFromSnapshot = (account, snapshot, copilotToken) => ({
|
|
488
|
+
accountLogin: account.accountLogin,
|
|
488
489
|
githubToken: snapshot.githubToken,
|
|
489
490
|
copilotToken,
|
|
490
491
|
...account.copilotApiUrl !== void 0 ? { copilotApiUrl: account.copilotApiUrl } : {},
|
|
491
492
|
accountType: snapshot.accountType,
|
|
492
|
-
vsCodeVersion: account.vsCodeVersion
|
|
493
|
+
vsCodeVersion: account.vsCodeVersion,
|
|
494
|
+
clientDeviceId: account.clientDeviceId,
|
|
495
|
+
clientMachineId: account.clientMachineId,
|
|
496
|
+
clientSessionId: account.clientSessionId
|
|
493
497
|
});
|
|
494
498
|
const applyCopilotTokenIfCurrent = (account, snapshot, copilotToken) => {
|
|
495
499
|
if (!isAuthSnapshotCurrent(account, snapshot)) return false;
|
|
@@ -583,6 +587,10 @@ const RELOAD_DEBOUNCE_MS = 500;
|
|
|
583
587
|
const WATCHER_RESTART_INITIAL_DELAY_MS = 1e3;
|
|
584
588
|
/** Registry watcher restart max delay in milliseconds */
|
|
585
589
|
const WATCHER_RESTART_MAX_DELAY_MS = 60 * 1e3;
|
|
590
|
+
/** Session refresh base interval in milliseconds. */
|
|
591
|
+
const SESSION_REFRESH_BASE_MS = 3600 * 1e3;
|
|
592
|
+
/** Session refresh jitter window in milliseconds. */
|
|
593
|
+
const SESSION_REFRESH_JITTER_MS = 1200 * 1e3;
|
|
586
594
|
/** Manages multiple GitHub Copilot accounts at runtime. */
|
|
587
595
|
var AccountsManager = class {
|
|
588
596
|
accounts = /* @__PURE__ */ new Map();
|
|
@@ -617,6 +625,7 @@ var AccountsManager = class {
|
|
|
617
625
|
}
|
|
618
626
|
const runtime = {
|
|
619
627
|
...meta,
|
|
628
|
+
accountLogin: meta.id,
|
|
620
629
|
githubToken: token,
|
|
621
630
|
vsCodeVersion: this.vsCodeVersion
|
|
622
631
|
};
|
|
@@ -644,6 +653,43 @@ var AccountsManager = class {
|
|
|
644
653
|
computeTokenRefreshDelayMs(refreshInSeconds) {
|
|
645
654
|
return Math.max((refreshInSeconds - 60) * 1e3, 1e3);
|
|
646
655
|
}
|
|
656
|
+
computeSessionRefreshDelayMs() {
|
|
657
|
+
return SESSION_REFRESH_BASE_MS + Math.floor(Math.random() * SESSION_REFRESH_JITTER_MS);
|
|
658
|
+
}
|
|
659
|
+
resolveAccountLogin(account) {
|
|
660
|
+
return account.accountLogin ?? account.id;
|
|
661
|
+
}
|
|
662
|
+
commitAccountIdentity(account, { identityKey, login, deviceId, machineId }) {
|
|
663
|
+
account.accountLogin = login;
|
|
664
|
+
account.identityKey = identityKey;
|
|
665
|
+
account.clientDeviceId = deviceId;
|
|
666
|
+
account.clientMachineId = machineId;
|
|
667
|
+
}
|
|
668
|
+
async applyAccountIdentity(account) {
|
|
669
|
+
const login = this.resolveAccountLogin(account);
|
|
670
|
+
const { oauthApp, enterpriseDomain } = getCurrentIdentityEnvironment();
|
|
671
|
+
const identityKey = buildIdentityKey({
|
|
672
|
+
login,
|
|
673
|
+
oauthApp,
|
|
674
|
+
enterpriseDomain
|
|
675
|
+
});
|
|
676
|
+
const identity = await ensureAccountClientIdentity({
|
|
677
|
+
login,
|
|
678
|
+
oauthApp,
|
|
679
|
+
enterpriseDomain
|
|
680
|
+
});
|
|
681
|
+
this.commitAccountIdentity(account, {
|
|
682
|
+
identityKey,
|
|
683
|
+
login,
|
|
684
|
+
deviceId: identity.deviceId,
|
|
685
|
+
machineId: identity.machineId
|
|
686
|
+
});
|
|
687
|
+
if (!account.clientSessionId) {
|
|
688
|
+
account.clientSessionId = createAccountSessionId();
|
|
689
|
+
consola.debug(`Generated VSCode session ID for account ${account.id}: ${account.clientSessionId}`);
|
|
690
|
+
}
|
|
691
|
+
this.startSessionRefresh(account);
|
|
692
|
+
}
|
|
647
693
|
shouldContinueTokenRefresh(account, snapshot) {
|
|
648
694
|
return this.tokenRefreshEnabledAccounts.has(account) && isAuthSnapshotCurrent(account, snapshot);
|
|
649
695
|
}
|
|
@@ -672,6 +718,7 @@ var AccountsManager = class {
|
|
|
672
718
|
}
|
|
673
719
|
/** Initialize a single account. */
|
|
674
720
|
async initializeAccount(account) {
|
|
721
|
+
await this.applyAccountIdentity(account);
|
|
675
722
|
const snapshot = takeAuthSnapshot(account);
|
|
676
723
|
try {
|
|
677
724
|
const { token, refresh_in } = await getCopilotToken(toAccountContextFromSnapshot(account, snapshot));
|
|
@@ -729,6 +776,31 @@ var AccountsManager = class {
|
|
|
729
776
|
for (const account of this.accounts.values()) this.stopTokenRefresh(account);
|
|
730
777
|
if (this.temporaryAccount) this.stopTokenRefresh(this.temporaryAccount);
|
|
731
778
|
}
|
|
779
|
+
startSessionRefresh(account) {
|
|
780
|
+
this.stopSessionRefresh(account);
|
|
781
|
+
const delayMs = this.computeSessionRefreshDelayMs();
|
|
782
|
+
consola.debug(`Scheduling next VSCode session ID refresh for ${account.id} in ${Math.round(delayMs / 1e3)} seconds`);
|
|
783
|
+
account.sessionRefreshTimer = setTimeout(() => {
|
|
784
|
+
try {
|
|
785
|
+
account.clientSessionId = createAccountSessionId();
|
|
786
|
+
consola.debug(`Refreshed VSCode session ID for account ${account.id}: ${account.clientSessionId}`);
|
|
787
|
+
} catch (error) {
|
|
788
|
+
consola.error(`Failed to refresh VSCode session ID for ${account.id}, rescheduling...`, error);
|
|
789
|
+
} finally {
|
|
790
|
+
this.startSessionRefresh(account);
|
|
791
|
+
}
|
|
792
|
+
}, delayMs);
|
|
793
|
+
}
|
|
794
|
+
stopSessionRefresh(account) {
|
|
795
|
+
if (account.sessionRefreshTimer) {
|
|
796
|
+
clearTimeout(account.sessionRefreshTimer);
|
|
797
|
+
account.sessionRefreshTimer = void 0;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
stopAllSessionRefresh() {
|
|
801
|
+
for (const account of this.accounts.values()) this.stopSessionRefresh(account);
|
|
802
|
+
if (this.temporaryAccount) this.stopSessionRefresh(this.temporaryAccount);
|
|
803
|
+
}
|
|
732
804
|
scheduleModelsRefresh() {
|
|
733
805
|
this.stopModelsRefresh();
|
|
734
806
|
if (!this.modelsRefreshIntervalMs || this.modelsRefreshIntervalMs <= 0) return;
|
|
@@ -1115,8 +1187,17 @@ var AccountsManager = class {
|
|
|
1115
1187
|
* This account takes priority over registered accounts.
|
|
1116
1188
|
*/
|
|
1117
1189
|
async setTemporaryAccount(githubToken, accountType) {
|
|
1190
|
+
const user = await getGitHubUser({
|
|
1191
|
+
githubToken,
|
|
1192
|
+
accountType
|
|
1193
|
+
});
|
|
1194
|
+
if (this.temporaryAccount) {
|
|
1195
|
+
this.stopTokenRefresh(this.temporaryAccount);
|
|
1196
|
+
this.stopSessionRefresh(this.temporaryAccount);
|
|
1197
|
+
}
|
|
1118
1198
|
const runtime = {
|
|
1119
1199
|
id: "(temporary)",
|
|
1200
|
+
accountLogin: user.login,
|
|
1120
1201
|
accountType,
|
|
1121
1202
|
addedAt: Date.now(),
|
|
1122
1203
|
githubToken,
|
|
@@ -1174,11 +1255,15 @@ var AccountsManager = class {
|
|
|
1174
1255
|
*/
|
|
1175
1256
|
toAccountContext(account) {
|
|
1176
1257
|
return {
|
|
1258
|
+
accountLogin: account.accountLogin,
|
|
1177
1259
|
githubToken: account.githubToken,
|
|
1178
1260
|
copilotToken: account.copilotToken,
|
|
1179
1261
|
...account.copilotApiUrl !== void 0 ? { copilotApiUrl: account.copilotApiUrl } : {},
|
|
1180
1262
|
accountType: account.accountType,
|
|
1181
|
-
vsCodeVersion: account.vsCodeVersion
|
|
1263
|
+
vsCodeVersion: account.vsCodeVersion,
|
|
1264
|
+
clientDeviceId: account.clientDeviceId,
|
|
1265
|
+
clientMachineId: account.clientMachineId,
|
|
1266
|
+
clientSessionId: account.clientSessionId
|
|
1182
1267
|
};
|
|
1183
1268
|
}
|
|
1184
1269
|
/**
|
|
@@ -1255,6 +1340,7 @@ var AccountsManager = class {
|
|
|
1255
1340
|
const account = this.accounts.get(id);
|
|
1256
1341
|
if (!account) continue;
|
|
1257
1342
|
this.stopTokenRefresh(account);
|
|
1343
|
+
this.stopSessionRefresh(account);
|
|
1258
1344
|
this.accounts.delete(id);
|
|
1259
1345
|
removed.push(id);
|
|
1260
1346
|
}
|
|
@@ -1274,6 +1360,7 @@ var AccountsManager = class {
|
|
|
1274
1360
|
const addedAtChanged = account.addedAt !== meta.addedAt;
|
|
1275
1361
|
if (accountTypeChanged) account.accountType = meta.accountType;
|
|
1276
1362
|
if (addedAtChanged) account.addedAt = meta.addedAt;
|
|
1363
|
+
account.accountLogin = meta.id;
|
|
1277
1364
|
if (tokenChanged) account.githubToken = token;
|
|
1278
1365
|
if (!accountTypeChanged && !tokenChanged) continue;
|
|
1279
1366
|
try {
|
|
@@ -1306,6 +1393,7 @@ var AccountsManager = class {
|
|
|
1306
1393
|
}
|
|
1307
1394
|
const runtime = {
|
|
1308
1395
|
...meta,
|
|
1396
|
+
accountLogin: meta.id,
|
|
1309
1397
|
githubToken: token,
|
|
1310
1398
|
vsCodeVersion: this.vsCodeVersion
|
|
1311
1399
|
};
|
|
@@ -1344,6 +1432,7 @@ var AccountsManager = class {
|
|
|
1344
1432
|
shutdown() {
|
|
1345
1433
|
this.stopRegistryWatcher();
|
|
1346
1434
|
this.stopAllTokenRefresh();
|
|
1435
|
+
this.stopAllSessionRefresh();
|
|
1347
1436
|
this.stopModelsRefresh();
|
|
1348
1437
|
this.affinityCache.clear();
|
|
1349
1438
|
this.loadBalanceCursor = 0;
|
|
@@ -1357,4 +1446,4 @@ const accountsManager = new AccountsManager();
|
|
|
1357
1446
|
|
|
1358
1447
|
//#endregion
|
|
1359
1448
|
export { isMessagesApiEnabled as _, getClaudeTokenMultiplier as a, mergeConfigWithDefaults as b, getModelAliases as c, getProviderConfig as d, getReasoningEffortForModel as f, isMessageStartInputTokensFallbackEnabled as g, isForceAgentEnabled as h, getAnthropicApiKey as i, getModelAliasesInfo as l, isAccountAffinityEnabled as m, PROVIDER_TYPE_ANTHROPIC as n, getConfig as o, getSmallModel as p, getAliasTargetSet as r, getExtraPromptForModel as s, accountsManager as t, getModelRefreshIntervalMs as u, isResponsesApiContextManagementModel as v, shouldCompactUseSmallModel as x, isResponsesApiWebSearchEnabled as y };
|
|
1360
|
-
//# sourceMappingURL=accounts-manager-
|
|
1449
|
+
//# sourceMappingURL=accounts-manager-BevCBoaF.js.map
|