@jeffreycao/copilot-api 2.6.26 → 2.6.29

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.
@@ -1,14 +1,11 @@
1
1
  import { S as ensurePaths, g as mergeConfigWithDefaults } from "./config-store-CARrUljo.js";
2
2
  import { i as listEnabledProviders, p as startModelsDevCache } from "./config-B2OaAnpL.js";
3
- import { i as HTTPError, r as isGitHubCopilotEnabled } from "./github-copilot-provider-DlQZgaCT.js";
4
- import { D as initOpencodeVersion, F as getConfiguredApiKeys, I as getMissingApiKeysMessage, S as copilotModelsHeaders, Z as state, _ as GITHUB_TOKEN_ENV, b as copilotBaseUrl, n as logUser, s as setupCopilotToken, v as readGitHubToken, y as readGitHubTokenFromEnv } from "./token-DgVri1k7.js";
5
- import { a as runProviderSetup } from "./auth-BUdtezdW.js";
6
- import { n as getLatestModelForFamily } from "./models-CHdnmX1S.js";
3
+ import { r as isGitHubCopilotEnabled } from "./github-copilot-provider-DlQZgaCT.js";
4
+ import { $ as state, L as getConfiguredApiKeys, R as getMissingApiKeysMessage, b as readGitHubToken, k as initOpencodeVersion, x as readGitHubTokenFromEnv, y as GITHUB_TOKEN_ENV } from "./token-0pmwyQAu.js";
5
+ import { a as runProviderSetup } from "./auth-CmKD_Gmp.js";
6
+ import { i as getLatestModelForFamily, t as setupCopilotRuntime } from "./copilot-runtime-B4x9RzsD.js";
7
7
  import { defineCommand } from "citty";
8
8
  import consola from "consola";
9
- import { createHash, randomUUID } from "node:crypto";
10
- import path from "node:path";
11
- import { networkInterfaces } from "node:os";
12
9
  import { getProxyForUrl } from "proxy-from-env";
13
10
  import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
14
11
  import { execSync } from "node:child_process";
@@ -178,273 +175,6 @@ function generateEnvScript(envVars, commandToRun = "") {
178
175
  return commandBlock || commandToRun;
179
176
  }
180
177
  //#endregion
181
- //#region src/services/copilot/get-models.ts
182
- const getModels = async () => {
183
- consola.info(`Fetching models from ${copilotBaseUrl(state)}/models`);
184
- const response = await fetch(`${copilotBaseUrl(state)}/models`, { headers: copilotModelsHeaders(state) });
185
- if (!response.ok) {
186
- const errorText = await response.clone().text();
187
- consola.error("Failed to get models response body", errorText);
188
- throw new HTTPError("Failed to get models", response);
189
- }
190
- return await response.json();
191
- };
192
- //#endregion
193
- //#region src/services/copilot/models-cache.ts
194
- const MODELS_REFRESH_BASE_MS = 18e5;
195
- let modelsRefreshTimer = null;
196
- const stopModelsRefreshLoop = () => {
197
- if (modelsRefreshTimer) {
198
- clearTimeout(modelsRefreshTimer);
199
- modelsRefreshTimer = null;
200
- }
201
- };
202
- const refreshModels = async (fetcher) => {
203
- const prevIds = new Set(state.models?.data.map((m) => m.id) ?? []);
204
- const models = await fetcher();
205
- state.models = {
206
- ...models,
207
- data: models.data.filter((model) => model.policy?.state !== "disabled" && (model.model_picker_enabled || model.capabilities.type === "embeddings"))
208
- };
209
- const nextIds = state.models.data.map((m) => m.id);
210
- const added = nextIds.filter((id) => !prevIds.has(id));
211
- if (added.length > 0) consola.info(`Models refresh: ${added.length} new`);
212
- else consola.debug(`Models refresh: no changes (${nextIds.length} total)`);
213
- };
214
- const scheduleModelsRefresh = (fetcher, intervalMs) => {
215
- const delay = intervalMs + Math.floor(Math.random() * (intervalMs / 6));
216
- consola.debug(`Scheduling next models refresh in ${Math.round(delay / 1e3)} seconds`);
217
- stopModelsRefreshLoop();
218
- modelsRefreshTimer = setTimeout(async () => {
219
- try {
220
- await refreshModels(fetcher);
221
- } catch (error) {
222
- consola.warn("Failed to refresh models, keeping previous cache.", error);
223
- } finally {
224
- scheduleModelsRefresh(fetcher, intervalMs);
225
- }
226
- }, delay);
227
- };
228
- async function cacheModels(fetcher = getModels, intervalMs = MODELS_REFRESH_BASE_MS) {
229
- await refreshModels(fetcher);
230
- scheduleModelsRefresh(fetcher, intervalMs);
231
- }
232
- //#endregion
233
- //#region src/lib/deviceid.ts
234
- const WINDOWS_DEVICE_ID_KEY = String.raw`\SOFTWARE\Microsoft\DeveloperTools`;
235
- const WINDOWS_DEVICE_ID_NAME = "deviceid";
236
- const windows64Architectures = /* @__PURE__ */ new Set([
237
- "AMD64",
238
- "ARM64",
239
- "IA64"
240
- ]);
241
- const getPosixHomeDir = () => {
242
- if (!process.env.HOME) throw new Error("Home directory not found");
243
- return process.env.HOME;
244
- };
245
- const getDeviceIdFilePath = () => {
246
- let folder;
247
- switch (process.platform) {
248
- case "darwin":
249
- folder = path.posix.join(getPosixHomeDir(), "Library", "Application Support");
250
- break;
251
- case "linux":
252
- folder = process.env.XDG_CACHE_HOME ?? path.posix.join(getPosixHomeDir(), ".cache");
253
- break;
254
- default: throw new Error("Unsupported platform");
255
- }
256
- return path.posix.join(folder, "Microsoft", "DeveloperTools", "deviceid");
257
- };
258
- const isMissingFileError = (error) => {
259
- return error instanceof Error && "code" in error && error.code === "ENOENT";
260
- };
261
- const readStoredDeviceIdFile = async (filePath) => {
262
- const { readFile } = await import("node:fs/promises");
263
- try {
264
- return await readFile(filePath, "utf8");
265
- } catch (error) {
266
- if (isMissingFileError(error)) return;
267
- throw error;
268
- }
269
- };
270
- const writeStoredDeviceIdFile = async (filePath, deviceId) => {
271
- const { mkdir, writeFile } = await import("node:fs/promises");
272
- await mkdir(path.posix.dirname(filePath), { recursive: true });
273
- await writeFile(filePath, deviceId, "utf8");
274
- };
275
- const getWindowsRegistryArch = () => {
276
- const architecture = (process.env.PROCESSOR_ARCHITEW6432 ?? process.env.PROCESSOR_ARCHITECTURE)?.toUpperCase();
277
- return architecture && windows64Architectures.has(architecture) ? "x64" : void 0;
278
- };
279
- const loadWinreg = async () => {
280
- const module = await import("winreg");
281
- return "default" in module ? module.default : module;
282
- };
283
- const isMissingRegistryError = (error) => {
284
- if (!error) return false;
285
- const errorCode = Number(error.code);
286
- return Number.isFinite(errorCode) && errorCode === 1;
287
- };
288
- const createWindowsRegistry = async () => {
289
- const Winreg = await loadWinreg();
290
- return {
291
- registry: new Winreg({
292
- hive: Winreg.HKCU,
293
- key: WINDOWS_DEVICE_ID_KEY,
294
- arch: getWindowsRegistryArch()
295
- }),
296
- regSz: Winreg.REG_SZ
297
- };
298
- };
299
- const readRegistryString = async (registry, name) => {
300
- return new Promise((resolve, reject) => {
301
- registry.get(name, (error, item) => {
302
- if (isMissingRegistryError(error)) {
303
- resolve(void 0);
304
- return;
305
- }
306
- if (error) {
307
- reject(error instanceof Error ? error : /* @__PURE__ */ new Error("Unknown registry error"));
308
- return;
309
- }
310
- resolve(item?.value);
311
- });
312
- });
313
- };
314
- const writeRegistryString = async ({ registry, regSz, name, value }) => {
315
- return new Promise((resolve, reject) => {
316
- registry.set(name, regSz, value, (error) => {
317
- if (error) {
318
- reject(error instanceof Error ? error : /* @__PURE__ */ new Error("Unknown registry error"));
319
- return;
320
- }
321
- resolve();
322
- });
323
- });
324
- };
325
- const getStoredVSCodeDeviceId = async () => {
326
- switch (process.platform) {
327
- case "win32": {
328
- const { registry } = await createWindowsRegistry();
329
- return readRegistryString(registry, WINDOWS_DEVICE_ID_NAME);
330
- }
331
- case "darwin":
332
- case "linux": return readStoredDeviceIdFile(getDeviceIdFilePath());
333
- default: throw new Error("Unsupported platform");
334
- }
335
- };
336
- const setStoredVSCodeDeviceId = async (deviceId) => {
337
- switch (process.platform) {
338
- case "win32": {
339
- const { registry, regSz } = await createWindowsRegistry();
340
- await writeRegistryString({
341
- registry,
342
- regSz,
343
- name: WINDOWS_DEVICE_ID_NAME,
344
- value: deviceId
345
- });
346
- return;
347
- }
348
- case "darwin":
349
- case "linux":
350
- await writeStoredDeviceIdFile(getDeviceIdFilePath(), deviceId);
351
- return;
352
- default: throw new Error("Unsupported platform");
353
- }
354
- };
355
- const createVSCodeDeviceId = () => randomUUID().toLowerCase();
356
- async function getVSCodeDeviceId() {
357
- let deviceId;
358
- try {
359
- deviceId = await getStoredVSCodeDeviceId();
360
- } catch (error) {
361
- consola.debug("Failed to read VSCode device id", error);
362
- }
363
- if (deviceId) return deviceId;
364
- const newDeviceId = createVSCodeDeviceId();
365
- try {
366
- await setStoredVSCodeDeviceId(newDeviceId);
367
- } catch (error) {
368
- consola.warn("Failed to persist VSCode device id, using ephemeral id", error);
369
- }
370
- return newDeviceId;
371
- }
372
- //#endregion
373
- //#region src/services/get-vscode-version.ts
374
- const FALLBACK = "1.139.1";
375
- async function getVSCodeVersion() {
376
- await Promise.resolve();
377
- return FALLBACK;
378
- }
379
- //#endregion
380
- //#region src/services/vscode-env.ts
381
- const cacheVSCodeVersion = async () => {
382
- const response = await getVSCodeVersion();
383
- state.vsCodeVersion = response;
384
- consola.info(`Using VSCode version: ${response}`);
385
- };
386
- const invalidMacAddresses = /* @__PURE__ */ new Set([
387
- "00:00:00:00:00:00",
388
- "ff:ff:ff:ff:ff:ff",
389
- "ac:de:48:00:11:22"
390
- ]);
391
- function validateMacAddress(candidate) {
392
- const tempCandidate = candidate.replaceAll("-", ":").toLowerCase();
393
- return !invalidMacAddresses.has(tempCandidate);
394
- }
395
- function getMac() {
396
- const ifaces = networkInterfaces();
397
- for (const name in ifaces) {
398
- const networkInterface = ifaces[name];
399
- if (networkInterface) {
400
- for (const { mac } of networkInterface) if (validateMacAddress(mac)) return mac;
401
- }
402
- }
403
- return null;
404
- }
405
- const cacheMacMachineId = () => {
406
- const macAddress = getMac() ?? randomUUID();
407
- state.macMachineId = createHash("sha256").update(macAddress, "utf8").digest("hex");
408
- consola.debug(`Using machine ID: ${state.macMachineId}`);
409
- };
410
- const cacheVsCodeDeviceId = async () => {
411
- state.vsCodeDeviceId = await getVSCodeDeviceId();
412
- consola.debug(`Using VSCode device ID: ${state.vsCodeDeviceId}`);
413
- };
414
- const SESSION_REFRESH_BASE_MS = 36e5;
415
- const SESSION_REFRESH_JITTER_MS = 12e5;
416
- let vsCodeSessionRefreshTimer = null;
417
- const generateSessionId = () => {
418
- state.vsCodeSessionId = randomUUID() + Date.now().toString();
419
- consola.debug(`Generated VSCode session ID: ${state.vsCodeSessionId}`);
420
- };
421
- const stopVsCodeSessionRefreshLoop = () => {
422
- if (vsCodeSessionRefreshTimer) {
423
- clearTimeout(vsCodeSessionRefreshTimer);
424
- vsCodeSessionRefreshTimer = null;
425
- }
426
- };
427
- const scheduleSessionIdRefresh = () => {
428
- const randomDelay = Math.floor(Math.random() * SESSION_REFRESH_JITTER_MS);
429
- const delay = SESSION_REFRESH_BASE_MS + randomDelay;
430
- consola.debug(`Scheduling next VSCode session ID refresh in ${Math.round(delay / 1e3)} seconds`);
431
- stopVsCodeSessionRefreshLoop();
432
- vsCodeSessionRefreshTimer = setTimeout(() => {
433
- try {
434
- generateSessionId();
435
- } catch (error) {
436
- consola.error("Failed to refresh session ID, rescheduling...", error);
437
- } finally {
438
- scheduleSessionIdRefresh();
439
- }
440
- }, delay);
441
- };
442
- const cacheVsCodeSessionId = () => {
443
- stopVsCodeSessionRefreshLoop();
444
- generateSessionId();
445
- scheduleSessionIdRefresh();
446
- };
447
- //#endregion
448
178
  //#region src/start.ts
449
179
  async function resolveGitHubToken(cliToken) {
450
180
  if (cliToken) return {
@@ -464,15 +194,9 @@ async function resolveGitHubToken(cliToken) {
464
194
  return null;
465
195
  }
466
196
  async function setupCopilotMode(githubToken, source, serverUrl, claudeCode) {
467
- state.githubToken = githubToken;
197
+ state.githubTokenSource = source;
468
198
  consola.info(source === "cli" ? "Using provided GitHub token" : source === "env" ? `Using GitHub token from the ${GITHUB_TOKEN_ENV} environment variable` : "Using GitHub token from local file");
469
- await logUser();
470
- await cacheVSCodeVersion();
471
- cacheMacMachineId();
472
- cacheVsCodeSessionId();
473
- await cacheVsCodeDeviceId();
474
- await setupCopilotToken();
475
- await cacheModels();
199
+ await setupCopilotRuntime(githubToken);
476
200
  if (claudeCode) runClaudeCode(serverUrl);
477
201
  }
478
202
  function runClaudeCode(serverUrl) {
@@ -549,7 +273,7 @@ async function runServer(options) {
549
273
  if (resolvedGitHubToken && isGitHubCopilotEnabled()) await setupCopilotMode(resolvedGitHubToken.token, resolvedGitHubToken.source, serverUrl, options.claudeCode);
550
274
  else await setupProviderMode(serverUrl, options.claudeCode);
551
275
  consola.box(`🌐 Dashboard Viewer: ${serverUrl}/usage-viewer?endpoint=${serverUrl}/usage`);
552
- const { createServer } = await import("./server-JC3AQpcl.js");
276
+ const { createServer } = await import("./server-VjfrS2Aw.js");
553
277
  const server = createServer({ networkExposed: binding.networkExposed });
554
278
  serve({
555
279
  fetch: server.fetch,
@@ -618,4 +342,4 @@ const start = defineCommand({
618
342
  //#endregion
619
343
  export { start };
620
344
 
621
- //# sourceMappingURL=start-bv97pvAG.js.map
345
+ //# sourceMappingURL=start-DX3Q5lol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-DX3Q5lol.js","names":["get","process"],"sources":["../src/lib/proxy.ts","../src/lib/server-host-shared.ts","../src/lib/server-host.ts","../src/lib/shell.ts","../src/start.ts"],"sourcesContent":["import consola from \"consola\"\nimport { getProxyForUrl } from \"proxy-from-env\"\nimport { Agent, ProxyAgent, setGlobalDispatcher, type Dispatcher } from \"undici\"\n\nlet proxyEnvDispatcher: Dispatcher | undefined\n\nexport function getProxyEnvDispatcher(): Dispatcher | undefined {\n return proxyEnvDispatcher\n}\n\nexport function initProxyFromEnv(): void {\n try {\n const direct = new Agent()\n const proxies = new Map<string, ProxyAgent>()\n\n // We only need a minimal dispatcher that implements `dispatch` at runtime.\n // Typing the object as `Dispatcher` forces TypeScript to require many\n // additional methods. Instead, keep a plain object and cast when passing\n // to `setGlobalDispatcher`.\n const dispatcher = {\n dispatch(\n options: Dispatcher.DispatchOptions,\n handler: Dispatcher.DispatchHandler,\n ) {\n try {\n const origin =\n typeof options.origin === \"string\" ?\n new URL(options.origin)\n : (options.origin as URL)\n const get = getProxyForUrl as unknown as (\n u: string,\n ) => string | undefined\n const raw = get(origin.toString())\n const proxyUrl = raw && raw.length > 0 ? raw : undefined\n if (!proxyUrl) {\n consola.debug(`HTTP proxy bypass: ${origin.hostname}`)\n return (direct as unknown as Dispatcher).dispatch(options, handler)\n }\n let agent = proxies.get(proxyUrl)\n if (!agent) {\n agent = new ProxyAgent(proxyUrl)\n proxies.set(proxyUrl, agent)\n }\n let label = proxyUrl\n try {\n const u = new URL(proxyUrl)\n label = `${u.protocol}//${u.host}`\n } catch {\n /* noop */\n }\n consola.debug(`HTTP proxy route: ${origin.hostname} via ${label}`)\n return (agent as unknown as Dispatcher).dispatch(options, handler)\n } catch {\n return (direct as unknown as Dispatcher).dispatch(options, handler)\n }\n },\n close() {\n return direct.close()\n },\n destroy() {\n return direct.destroy()\n },\n }\n\n proxyEnvDispatcher = dispatcher as unknown as Dispatcher\n\n if (typeof Bun !== \"undefined\") {\n consola.debug(\"WebSocket proxy configured from environment (per-URL)\")\n return\n }\n\n setGlobalDispatcher(proxyEnvDispatcher)\n consola.debug(\"HTTP proxy configured from environment (per-URL)\")\n } catch (err) {\n consola.debug(\"Proxy setup skipped:\", err)\n }\n}\n","// Pure hostname helpers shared by the gateway server (Node) and the desktop\n// app (including the renderer bundle). This module must stay free of\n// `node:*` imports because the renderer cannot use `node:net`; all\n// address-family checks live in `server-host.ts`.\nexport const INVALID_HOST_CHARACTERS = /[\\s/?#]/\n\n// Bind hosts that listen on every interface, in normalized form (no\n// brackets, so \"[::]\" is covered via `stripIpv6Brackets`).\nexport const NORMALIZED_WILDCARD_HOSTS = new Set([\"0.0.0.0\", \"::\"])\n\nexport function stripIpv6Brackets(hostname: string): string {\n if (hostname.startsWith(\"[\") && hostname.endsWith(\"]\")) {\n return hostname.slice(1, -1)\n }\n return hostname\n}\n\nexport function normalizeHostnameBase(hostname: string): string {\n const normalized = stripIpv6Brackets(hostname.trim())\n if (!normalized || INVALID_HOST_CHARACTERS.test(normalized)) {\n throw new Error(`Invalid server host: ${JSON.stringify(hostname)}`)\n }\n return normalized\n}\n\nexport function isWildcardHostname(hostname: string): boolean {\n return NORMALIZED_WILDCARD_HOSTS.has(stripIpv6Brackets(hostname.trim()))\n}\n","import { isIP } from \"node:net\"\n\nimport { isWildcardHostname, normalizeHostnameBase } from \"./server-host-shared\"\n\nexport const DEFAULT_SERVER_HOST = \"127.0.0.1\"\n\n// Listen failures that mean the hostname itself cannot be bound, as opposed\n// to the port being occupied (EADDRINUSE) or forbidden (EACCES). ENOTFOUND\n// covers unresolvable names such as typos, EADDRNOTAVAIL covers valid IPs\n// that are not assigned to this machine.\nconst INVALID_BIND_ERROR_CODES = new Set([\n \"ENOTFOUND\",\n \"EADDRNOTAVAIL\",\n \"EINVAL\",\n \"EAFNOSUPPORT\",\n \"ENXIO\",\n])\n\nexport function isInvalidBindErrorCode(code?: string): boolean {\n return !!code && INVALID_BIND_ERROR_CODES.has(code)\n}\n\nexport interface ServerBinding {\n hostname: string\n clientHostname: string\n networkExposed: boolean\n}\n\nfunction isLoopbackIpv4(hostname: string): boolean {\n return isIP(hostname) === 4 && hostname.split(\".\", 1)[0] === \"127\"\n}\n\nfunction isLoopbackMappedIpv4(hostname: string): boolean {\n try {\n const canonicalHostname = new URL(`http://[${hostname}]/`).hostname.slice(\n 1,\n -1,\n )\n const mappedIpv4 = canonicalHostname.match(\n /^::ffff:([\\da-f]{1,4}):[\\da-f]{1,4}$/,\n )\n if (!mappedIpv4) {\n return false\n }\n\n return (Number.parseInt(mappedIpv4[1], 16) & 0xff00) === 0x7f00\n } catch {\n return false\n }\n}\n\nexport function normalizeServerHostname(hostname: string): string {\n return normalizeHostnameBase(hostname)\n}\n\nexport function isLoopbackHostname(hostname: string): boolean {\n const normalized = normalizeServerHostname(hostname).toLowerCase()\n if (normalized === \"localhost\" || normalized === \"localhost.\") {\n return true\n }\n if (isLoopbackIpv4(normalized)) {\n return true\n }\n if (isIP(normalized) !== 6) {\n return false\n }\n\n if (normalized === \"::1\" || normalized === \"0:0:0:0:0:0:0:1\") {\n return true\n }\n\n return isLoopbackMappedIpv4(normalized)\n}\n\nexport function resolveServerBinding(\n hostname: string,\n hasApiKeys: boolean,\n): ServerBinding {\n const normalizedHostname = normalizeServerHostname(hostname)\n const networkExposed = !isLoopbackHostname(normalizedHostname)\n\n if (networkExposed && !hasApiKeys) {\n throw new Error(\n `Refusing to listen on non-loopback host ${JSON.stringify(normalizedHostname)} without gateway API keys. Run \\`npx @jeffreycao/copilot-api@latest auth keys --add <key>\\` first, or use \\`--host ${DEFAULT_SERVER_HOST}\\`.`,\n )\n }\n\n return {\n hostname: normalizedHostname,\n clientHostname: resolveClientHostname(normalizedHostname),\n networkExposed,\n }\n}\n\nexport function resolveClientHostname(hostname: string): string {\n const normalizedHostname = normalizeServerHostname(hostname)\n // Wildcard binds listen on every interface, which clients cannot dial\n // directly (0.0.0.0 is not a routable destination).\n return isWildcardHostname(normalizedHostname) ? DEFAULT_SERVER_HOST : (\n normalizedHostname.toLowerCase()\n )\n}\n\nexport function resolveClientHostnameOrDefault(\n hostname: string | null | undefined,\n): string {\n const normalizedHostname = hostname?.trim()\n return normalizedHostname ?\n resolveClientHostname(normalizedHostname)\n : DEFAULT_SERVER_HOST\n}\n\nexport function formatServerUrl(hostname: string, port: number): string {\n const urlHostname =\n isIP(hostname) === 6 ? `[${hostname.replace(\"%\", \"%25\")}]` : hostname\n return `http://${urlHostname}:${port}`\n}\n","import { execSync } from \"node:child_process\"\nimport process from \"node:process\"\n\ntype ShellName = \"bash\" | \"zsh\" | \"fish\" | \"powershell\" | \"cmd\" | \"sh\"\ntype EnvVars = Record<string, string | undefined>\n\nfunction getShell(): ShellName {\n const { platform, ppid, env } = process\n\n if (platform === \"win32\") {\n try {\n const command = `wmic process get ParentProcessId,Name | findstr \"${ppid}\"`\n const parentProcess = execSync(command, { stdio: \"pipe\" }).toString()\n\n if (parentProcess.toLowerCase().includes(\"powershell.exe\")) {\n return \"powershell\"\n }\n } catch {\n return \"cmd\"\n }\n\n return \"cmd\"\n } else {\n const shellPath = env.SHELL\n if (shellPath) {\n if (shellPath.endsWith(\"zsh\")) return \"zsh\"\n if (shellPath.endsWith(\"fish\")) return \"fish\"\n if (shellPath.endsWith(\"bash\")) return \"bash\"\n }\n\n return \"sh\"\n }\n}\n\n/**\n * Generates a copy-pasteable script to set multiple environment variables\n * and run a subsequent command.\n * @param {EnvVars} envVars - An object of environment variables to set.\n * @param {string} commandToRun - The command to run after setting the variables.\n * @returns {string} The formatted script string.\n */\nexport function generateEnvScript(\n envVars: EnvVars,\n commandToRun: string = \"\",\n): string {\n const shell = getShell()\n const filteredEnvVars = Object.entries(envVars).filter(\n ([, value]) => value !== undefined,\n ) as Array<[string, string]>\n\n let commandBlock: string\n\n switch (shell) {\n case \"powershell\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `$env:${key} = ${value}`)\n .join(\"; \")\n break\n }\n case \"cmd\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `set ${key}=${value}`)\n .join(\" & \")\n break\n }\n case \"fish\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `set -gx ${key} ${value}`)\n .join(\"; \")\n break\n }\n default: {\n // bash, zsh, sh\n const assignments = filteredEnvVars\n .map(([key, value]) => `${key}=${value}`)\n .join(\" \")\n commandBlock = filteredEnvVars.length > 0 ? `export ${assignments}` : \"\"\n break\n }\n }\n\n if (commandBlock && commandToRun) {\n const separator = shell === \"cmd\" ? \" & \" : \" && \"\n return `${commandBlock}${separator}${commandToRun}`\n }\n\n return commandBlock || commandToRun\n}\n","#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport clipboard from \"clipboardy\"\nimport consola from \"consola\"\nimport { serve, type ServerHandler } from \"srvx\"\nimport invariant from \"tiny-invariant\"\n\nimport { runProviderSetup } from \"./auth\"\nimport { isGitHubCopilotEnabled } from \"./lib/github-copilot-provider\"\nimport { listEnabledProviders, mergeConfigWithDefaults } from \"./lib/config\"\nimport { setupCopilotRuntime } from \"~/lib/copilot-runtime\"\nimport {\n GITHUB_TOKEN_ENV,\n readGitHubToken,\n readGitHubTokenFromEnv,\n} from \"./lib/credential-store\"\nimport { getLatestModelForFamily } from \"./lib/models\"\nimport { startModelsDevCache } from \"./lib/models-dev-cache\"\nimport { initOpencodeVersion } from \"./lib/opencode\"\nimport { ensurePaths } from \"./lib/paths\"\nimport { initProxyFromEnv } from \"./lib/proxy\"\nimport {\n getConfiguredApiKeys,\n getMissingApiKeysMessage,\n} from \"./lib/request-auth\"\nimport {\n DEFAULT_SERVER_HOST,\n formatServerUrl,\n resolveServerBinding,\n} from \"./lib/server-host\"\nimport { generateEnvScript } from \"./lib/shell\"\nimport { state } from \"./lib/state\"\n\ninterface RunServerOptions {\n host: string\n port: number\n verbose: boolean\n githubToken?: string\n claudeCode: boolean\n showToken: boolean\n proxyEnv: boolean\n}\n\ntype GitHubTokenSource = \"cli\" | \"env\" | \"file\"\n\n// The environment is preferred over the token file so the token never has to\n// travel through the process list; --github-token stays first for callers that\n// opt in explicitly.\nasync function resolveGitHubToken(\n cliToken: string | undefined,\n): Promise<{ token: string; source: GitHubTokenSource } | null> {\n if (cliToken) return { token: cliToken, source: \"cli\" }\n\n const envToken = readGitHubTokenFromEnv()\n if (envToken) return { token: envToken, source: \"env\" }\n\n const fileToken = await readGitHubToken()\n if (fileToken) return { token: fileToken, source: \"file\" }\n\n return null\n}\n\nasync function setupCopilotMode(\n githubToken: string,\n source: GitHubTokenSource,\n serverUrl: string,\n claudeCode: boolean,\n): Promise<void> {\n state.githubTokenSource = source\n consola.info(\n source === \"cli\" ? \"Using provided GitHub token\"\n : source === \"env\" ?\n `Using GitHub token from the ${GITHUB_TOKEN_ENV} environment variable`\n : \"Using GitHub token from local file\",\n )\n\n await setupCopilotRuntime(githubToken)\n\n if (claudeCode) {\n runClaudeCode(serverUrl)\n }\n}\n\nfunction runClaudeCode(serverUrl: string): void {\n consola.log(\n \"\\n💡 Tip: The --claude-code flag simply generates a clipboard command for launching Claude Code. \\n\"\n + \"All models remain fully accessible without this flag, just configure the model ID directly in your settings.json file.\",\n )\n\n invariant(state.models, \"Models should be loaded by now\")\n\n // Default to the latest available model for each Claude Code size tier so\n // opus maps to opus, sonnet maps to sonnet, and haiku maps to haiku.\n const opusModel = getLatestModelForFamily(\"opus\")?.id\n const sonnetModel = getLatestModelForFamily(\"sonnet\")?.id\n const haikuModel = getLatestModelForFamily(\"haiku\")?.id\n\n consola.info(\n \"Selected default Claude Code models:\\n\"\n + `- Opus: ${opusModel ?? \"(none available)\"}\\n`\n + `- Sonnet: ${sonnetModel ?? \"(none available)\"}\\n`\n + `- Haiku: ${haikuModel ?? \"(none available)\"}`,\n )\n\n const command = generateEnvScript(\n {\n ANTHROPIC_BASE_URL: serverUrl,\n ANTHROPIC_AUTH_TOKEN: \"dummy\",\n ANTHROPIC_MODEL: sonnetModel ?? opusModel,\n ANTHROPIC_DEFAULT_OPUS_MODEL: opusModel,\n ANTHROPIC_DEFAULT_SONNET_MODEL: sonnetModel,\n ANTHROPIC_DEFAULT_HAIKU_MODEL: haikuModel,\n CLAUDE_CODE_USE_VERTEX: \"0\",\n CLAUDE_CODE_USE_BEDROCK: \"0\",\n DISABLE_NON_ESSENTIAL_MODEL_CALLS: \"1\",\n CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: \"1\",\n CLAUDE_CODE_ATTRIBUTION_HEADER: \"0\",\n CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: \"false\",\n CLAUDE_CODE_DISABLE_TERMINAL_TITLE: \"true\",\n CLAUDE_CODE_ENABLE_AWAY_SUMMARY: \"0\",\n CLAUDE_CODE_TOTAL_TOKENS_REMINDER: \"off\",\n MCP_CONNECT_TIMEOUT_MS: \"20000\",\n },\n \"claude\",\n )\n\n try {\n clipboard.writeSync(command)\n consola.success(\"Copied Claude Code command to clipboard!\")\n } catch {\n consola.warn(\n \"Failed to copy to clipboard. Here is the Claude Code command:\",\n )\n consola.log(command)\n }\n}\n\nasync function setupProviderMode(\n serverUrl: string,\n claudeCode: boolean,\n): Promise<void> {\n const enabledProviders = listEnabledProviders()\n\n if (enabledProviders.length > 0) {\n consola.info(`Using enabled providers: ${enabledProviders.join(\", \")}`)\n return\n }\n\n if (!isGitHubCopilotEnabled()) {\n throw new Error(\n \"No enabled providers found. Enable GitHub Copilot with `copilot-api provider enable github-copilot`, or enable another configured provider.\",\n )\n }\n\n consola.info(\"No enabled providers found. Setting one up...\")\n await runProviderSetup()\n\n if (state.githubToken && isGitHubCopilotEnabled()) {\n // The setup flow persisted the token with the credential store.\n await setupCopilotMode(state.githubToken, \"file\", serverUrl, claudeCode)\n return\n }\n\n const providersAfterSetup = listEnabledProviders()\n if (providersAfterSetup.length === 0) {\n throw new Error(\n \"Failed to configure any provider. Run `copilot-api auth login` to set one up.\",\n )\n }\n consola.info(`Configured providers: ${providersAfterSetup.join(\", \")}`)\n}\n\nexport async function runServer(options: RunServerOptions): Promise<void> {\n const tlsModule = await import(\"./lib/tls\")\n tlsModule.enableSystemCACompat()\n\n consola.options.throttle = 0\n\n mergeConfigWithDefaults()\n\n const configuredApiKeys = getConfiguredApiKeys()\n const binding = resolveServerBinding(\n options.host,\n configuredApiKeys.length > 0,\n )\n\n const missingApiKeysMessage = getMissingApiKeysMessage()\n if (missingApiKeysMessage) {\n consola.info(missingApiKeysMessage)\n }\n\n await initOpencodeVersion()\n\n if (options.proxyEnv) {\n initProxyFromEnv()\n }\n\n state.verbose = options.verbose\n if (options.verbose) {\n consola.level = 5\n consola.info(\"Verbose logging enabled\")\n }\n\n state.showToken = options.showToken\n\n await ensurePaths()\n await startModelsDevCache()\n\n const serverUrl = formatServerUrl(binding.clientHostname, options.port)\n\n const resolvedGitHubToken =\n isGitHubCopilotEnabled() ?\n await resolveGitHubToken(options.githubToken)\n : null\n if (resolvedGitHubToken && isGitHubCopilotEnabled()) {\n await setupCopilotMode(\n resolvedGitHubToken.token,\n resolvedGitHubToken.source,\n serverUrl,\n options.claudeCode,\n )\n } else {\n await setupProviderMode(serverUrl, options.claudeCode)\n }\n\n consola.box(\n `🌐 Dashboard Viewer: ${serverUrl}/usage-viewer?endpoint=${serverUrl}/usage`,\n )\n\n const { createServer } = await import(\"./server\")\n const server = createServer({ networkExposed: binding.networkExposed })\n\n serve({\n fetch: server.fetch as ServerHandler,\n hostname: binding.hostname,\n port: options.port,\n bun: {\n idleTimeout: 0,\n },\n })\n}\n\nexport const start = defineCommand({\n meta: {\n name: \"start\",\n description: \"Start the Copilot API server\",\n },\n args: {\n host: {\n type: \"string\",\n default: process.env.HOST?.trim() || DEFAULT_SERVER_HOST,\n description: \"Host to listen on\",\n },\n port: {\n alias: \"p\",\n type: \"string\",\n default: \"4141\",\n description: \"Port to listen on\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n \"github-token\": {\n alias: \"g\",\n type: \"string\",\n description:\n \"Provide GitHub token directly (must be generated using the `auth` subcommand)\",\n },\n \"claude-code\": {\n alias: \"c\",\n type: \"boolean\",\n default: false,\n description:\n \"Generate a command to launch Claude Code with Copilot API config\",\n },\n \"show-token\": {\n type: \"boolean\",\n default: false,\n description: \"Show GitHub and Copilot tokens on fetch and refresh\",\n },\n \"proxy-env\": {\n type: \"boolean\",\n default: false,\n description: \"Initialize proxy from environment variables\",\n },\n },\n run({ args }) {\n return runServer({\n host: args.host,\n port: Number.parseInt(args.port, 10),\n verbose: args.verbose,\n githubToken: args[\"github-token\"],\n claudeCode: args[\"claude-code\"],\n showToken: args[\"show-token\"],\n proxyEnv: args[\"proxy-env\"],\n })\n },\n})\n"],"mappings":"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"}
@@ -762,8 +762,9 @@ function buildCodexRequestHeaders(requestHeaders) {
762
762
  applyOpencodeCodexHeaders(headers);
763
763
  return headers;
764
764
  }
765
+ const codexResponsesDependencies = { isResponsesApiWebSocketEnabled: () => isResponsesApiWebSocketEnabled() };
765
766
  function resolveCodexResponsesTransport(transport) {
766
- return transport ?? (isResponsesApiWebSocketEnabled() ? "websocket" : "http");
767
+ return transport ?? (codexResponsesDependencies.isResponsesApiWebSocketEnabled() ? "websocket" : "http");
767
768
  }
768
769
  function buildCodexResponsesWebSocketHeaders(requestHeaders, options = {}) {
769
770
  const headers = buildCodexResponsesHeaders(requestHeaders, options);
@@ -1836,6 +1837,8 @@ async function writeCodexCredentials(credentials, options = {}) {
1836
1837
  const accountIdSelector = normalizeCodexAccountSelector(normalizedCredentials.accountId);
1837
1838
  const existingIndex = store.accounts.findIndex((account) => account.accountId === normalizedCredentials.accountId);
1838
1839
  if (existingIndex < 0 && options.insertIfMissing === false) return;
1840
+ const existingRefreshToken = store.accounts[existingIndex]?.refreshToken;
1841
+ if (options.expectedRefreshToken !== void 0 && existingRefreshToken !== options.expectedRefreshToken && existingRefreshToken !== normalizedCredentials.refreshToken) return;
1839
1842
  if (existingIndex < 0 && store.accounts.some((account) => normalizeCodexAccountSelector(account.accountId) === accountIdSelector)) throw new Error(`Codex account id '${normalizedCredentials.accountId}' is already in use`);
1840
1843
  if (existingIndex < 0 && store.accounts.length >= 3) throw new Error(`Codex supports at most 3 accounts`);
1841
1844
  if (store.accounts.some((account, index) => index !== existingIndex && account.alias && normalizeCodexAccountSelector(account.alias) === accountIdSelector)) throw new Error(`Codex account id '${normalizedCredentials.accountId}' conflicts with another account alias`);
@@ -1996,13 +1999,15 @@ const defaultPollAccessTokenDependencies = {
1996
1999
  sleep
1997
2000
  };
1998
2001
  const toSleepDuration = (intervalSeconds) => (intervalSeconds + 1) * 1e3;
1999
- async function pollAccessToken(deviceCode, dependencies = defaultPollAccessTokenDependencies) {
2002
+ async function pollAccessToken(deviceCode, dependencies = defaultPollAccessTokenDependencies, options = {}) {
2003
+ const { signal } = options;
2000
2004
  const { clientId, headers } = getOauthAppConfig();
2001
2005
  const { accessTokenUrl } = getOauthUrls();
2002
2006
  const expiresAt = dependencies.now() + deviceCode.expires_in * 1e3;
2003
2007
  let intervalSeconds = deviceCode.interval;
2004
2008
  consola.debug(`Polling access token with interval of ${toSleepDuration(intervalSeconds)}ms`);
2005
2009
  while (true) {
2010
+ signal?.throwIfAborted();
2006
2011
  if (dependencies.now() >= expiresAt) throw new Error("GitHub device code expired before authorization completed. Please log in again.");
2007
2012
  const json = await requestAccessToken(accessTokenUrl, {
2008
2013
  method: "POST",
@@ -2011,7 +2016,8 @@ async function pollAccessToken(deviceCode, dependencies = defaultPollAccessToken
2011
2016
  client_id: clientId,
2012
2017
  device_code: deviceCode.device_code,
2013
2018
  grant_type: "urn:ietf:params:oauth:grant-type:device_code"
2014
- })
2019
+ }),
2020
+ signal
2015
2021
  });
2016
2022
  if (json) {
2017
2023
  consola.debug("Polling access token response received");
@@ -2022,13 +2028,31 @@ async function pollAccessToken(deviceCode, dependencies = defaultPollAccessToken
2022
2028
  consola.debug(`GitHub asked to slow down, polling interval is now ${intervalSeconds}s`);
2023
2029
  } else if (json.error && json.error !== "authorization_pending") throw new Error(`GitHub device authorization failed: ${json.error_description ?? json.error}`);
2024
2030
  }
2025
- await dependencies.sleep(toSleepDuration(intervalSeconds));
2031
+ await sleepUnlessAborted(dependencies.sleep, toSleepDuration(intervalSeconds), signal);
2032
+ }
2033
+ }
2034
+ async function sleepUnlessAborted(sleepFor, ms, signal) {
2035
+ if (!signal) {
2036
+ await sleepFor(ms);
2037
+ return;
2038
+ }
2039
+ signal.throwIfAborted();
2040
+ let onAbort;
2041
+ const aborted = new Promise((_resolve, reject) => {
2042
+ onAbort = () => reject(signal.reason);
2043
+ signal.addEventListener("abort", onAbort, { once: true });
2044
+ });
2045
+ try {
2046
+ await Promise.race([aborted, sleepFor(ms)]);
2047
+ } finally {
2048
+ if (onAbort) signal.removeEventListener("abort", onAbort);
2026
2049
  }
2027
2050
  }
2028
2051
  /**
2029
2052
  * Resolves to `undefined` when the poll should simply be retried. The device
2030
2053
  * code stays valid until it expires, so a dropped connection (for example a
2031
2054
  * proxy closing the socket) or a non-2xx response must not abort the login.
2055
+ * A request aborted through `init.signal` rejects instead.
2032
2056
  */
2033
2057
  async function requestAccessToken(url, init) {
2034
2058
  try {
@@ -2039,6 +2063,7 @@ async function requestAccessToken(url, init) {
2039
2063
  }
2040
2064
  return await response.json();
2041
2065
  } catch (error) {
2066
+ if (init.signal?.aborted) throw error;
2042
2067
  consola.warn(`Failed to poll access token, will retry: ${describeError(error)}`);
2043
2068
  return;
2044
2069
  }
@@ -2054,8 +2079,9 @@ function describeError(error) {
2054
2079
  //#region src/lib/token.ts
2055
2080
  let copilotRefreshLoopController = null;
2056
2081
  let codexRefreshLoopController = null;
2057
- let codexRefreshInFlight = null;
2058
- let codexCredentialsPendingPersistence = null;
2082
+ let codexRuntimeGeneration = 0;
2083
+ const codexRefreshInFlight = /* @__PURE__ */ new Map();
2084
+ const codexCredentialsPendingPersistence = /* @__PURE__ */ new Map();
2059
2085
  const defaultCopilotTokenDependencies = {
2060
2086
  getCopilotToken,
2061
2087
  getCopilotUsage
@@ -2070,6 +2096,14 @@ const stopCodexRefreshLoop = () => {
2070
2096
  codexRefreshLoopController.abort();
2071
2097
  codexRefreshLoopController = null;
2072
2098
  };
2099
+ const invalidateCodexRuntime = () => {
2100
+ codexRuntimeGeneration++;
2101
+ stopCodexRefreshLoop();
2102
+ state.codexAccessToken = void 0;
2103
+ state.codexRefreshToken = void 0;
2104
+ state.codexExpiresAt = void 0;
2105
+ state.codexAccountId = void 0;
2106
+ };
2073
2107
  function applyCodexCredentials(credentials) {
2074
2108
  state.codexAccessToken = credentials.accessToken;
2075
2109
  state.codexRefreshToken = credentials.refreshToken;
@@ -2150,17 +2184,21 @@ async function removeCodexAccount(selector) {
2150
2184
  });
2151
2185
  }
2152
2186
  async function persistCodexCredentials(credentials, options = {}) {
2187
+ const generation = options.runtimeGeneration ?? codexRuntimeGeneration;
2153
2188
  const persist = async () => {
2154
2189
  await writeCodexCredentials(credentials, {
2155
2190
  alias: options.alias,
2156
- insertIfMissing: options.insertIfMissing
2191
+ insertIfMissing: options.insertIfMissing,
2192
+ expectedRefreshToken: options.expectedRefreshToken
2157
2193
  });
2158
2194
  if (options.syncProvider !== false) syncCodexProviderConfig({
2159
2195
  accountId: options.activateAccount ? credentials.accountId : void 0,
2160
2196
  enabled: options.enableProvider ? true : void 0
2161
2197
  });
2162
- applyCodexCredentials(credentials);
2163
- codexCredentialsPendingPersistence = null;
2198
+ if (generation === codexRuntimeGeneration && (options.syncProvider !== false || getConfiguredCodexAccountId() === credentials.accountId)) applyCodexCredentials(credentials);
2199
+ if (options.syncProvider !== false) {
2200
+ for (const [key, pending] of codexCredentialsPendingPersistence) if (pending.accountId === credentials.accountId) codexCredentialsPendingPersistence.delete(key);
2201
+ }
2164
2202
  };
2165
2203
  if (options.syncProvider === false) {
2166
2204
  await persist();
@@ -2168,14 +2206,19 @@ async function persistCodexCredentials(credentials, options = {}) {
2168
2206
  }
2169
2207
  await withCodexAccountMutationLock(persist);
2170
2208
  }
2171
- const defaultCodexRefreshDependencies = {
2172
- getCurrentCredentials: getLoadedCodexCredentials,
2173
- persistCodexCredentials: (credentials) => persistCodexCredentials(credentials, {
2174
- insertIfMissing: false,
2175
- syncProvider: false
2176
- }),
2177
- refreshCodexCredentials
2178
- };
2209
+ function createCodexRefreshDependencies(credentials) {
2210
+ const generation = codexRuntimeGeneration;
2211
+ return {
2212
+ getCurrentCredentials: getLoadedCodexCredentials,
2213
+ persistCodexCredentials: (refreshed) => persistCodexCredentials(refreshed, {
2214
+ insertIfMissing: false,
2215
+ syncProvider: false,
2216
+ expectedRefreshToken: credentials.refreshToken,
2217
+ runtimeGeneration: generation
2218
+ }),
2219
+ refreshCodexCredentials
2220
+ };
2221
+ }
2179
2222
  /**
2180
2223
  * Refreshes and persists Codex credentials through one module level
2181
2224
  * single-flight guard.
@@ -2186,29 +2229,31 @@ const defaultCodexRefreshDependencies = {
2186
2229
  * Codex request, so a burst of requests (or the background loop waking at the
2187
2230
  * same moment) must share a single attempt instead of burning the token.
2188
2231
  */
2189
- function refreshCodexCredentialsOnce(credentials, dependencies = defaultCodexRefreshDependencies) {
2190
- const inFlight = codexRefreshInFlight;
2232
+ function refreshCodexCredentialsOnce(credentials, dependencies) {
2233
+ const current = dependencies ? dependencies.getCurrentCredentials() : getLoadedCodexCredentials();
2234
+ const rotated = current && current.accountId === credentials.accountId && current.refreshToken !== credentials.refreshToken ? current : null;
2235
+ const base = rotated ?? credentials;
2236
+ const refreshDependencies = dependencies ?? createCodexRefreshDependencies(base);
2237
+ const refreshKey = JSON.stringify([base.accountId, base.refreshToken]);
2238
+ const inFlight = codexRefreshInFlight.get(refreshKey);
2191
2239
  if (inFlight) return inFlight;
2192
2240
  const attempt = (async () => {
2193
- const pendingPersistence = codexCredentialsPendingPersistence;
2241
+ const pendingPersistence = codexCredentialsPendingPersistence.get(refreshKey);
2194
2242
  if (pendingPersistence) {
2195
- await dependencies.persistCodexCredentials(pendingPersistence);
2196
- if (codexCredentialsPendingPersistence === pendingPersistence) codexCredentialsPendingPersistence = null;
2243
+ await refreshDependencies.persistCodexCredentials(pendingPersistence);
2244
+ if (codexCredentialsPendingPersistence.get(refreshKey) === pendingPersistence) codexCredentialsPendingPersistence.delete(refreshKey);
2197
2245
  return pendingPersistence;
2198
2246
  }
2199
- const current = dependencies.getCurrentCredentials();
2200
- const rotated = current && current.refreshToken !== credentials.refreshToken ? current : null;
2201
2247
  if (rotated && !isCodexCredentialsExpired(rotated)) return rotated;
2202
- const base = rotated ?? credentials;
2203
- const refreshed = await dependencies.refreshCodexCredentials(base);
2204
- codexCredentialsPendingPersistence = refreshed;
2205
- await dependencies.persistCodexCredentials(refreshed);
2206
- if (codexCredentialsPendingPersistence === refreshed) codexCredentialsPendingPersistence = null;
2248
+ const refreshed = await refreshDependencies.refreshCodexCredentials(base);
2249
+ codexCredentialsPendingPersistence.set(refreshKey, refreshed);
2250
+ await refreshDependencies.persistCodexCredentials(refreshed);
2251
+ if (codexCredentialsPendingPersistence.get(refreshKey) === refreshed) codexCredentialsPendingPersistence.delete(refreshKey);
2207
2252
  return refreshed;
2208
2253
  })();
2209
- codexRefreshInFlight = attempt;
2254
+ codexRefreshInFlight.set(refreshKey, attempt);
2210
2255
  const clearAttempt = () => {
2211
- if (codexRefreshInFlight === attempt) codexRefreshInFlight = null;
2256
+ if (codexRefreshInFlight.get(refreshKey) === attempt) codexRefreshInFlight.delete(refreshKey);
2212
2257
  };
2213
2258
  attempt.then(clearAttempt, clearAttempt);
2214
2259
  return attempt;
@@ -2241,6 +2286,17 @@ const setupCopilotToken = async (dependencies = defaultCopilotTokenDependencies)
2241
2286
  });
2242
2287
  };
2243
2288
  const setupCodexToken = async () => {
2289
+ while (getRawProviderConfig("codex")?.enabled !== false) {
2290
+ const generation = codexRuntimeGeneration;
2291
+ try {
2292
+ await setupCodexTokenForGeneration(generation);
2293
+ } catch (error) {
2294
+ if (generation === codexRuntimeGeneration) throw error;
2295
+ }
2296
+ if (generation === codexRuntimeGeneration) return;
2297
+ }
2298
+ };
2299
+ const setupCodexTokenForGeneration = async (generation) => {
2244
2300
  const configuredAccountId = getConfiguredCodexAccountId();
2245
2301
  const loadedCredentials = getLoadedCodexCredentials();
2246
2302
  if (configuredAccountId && loadedCredentials?.accountId === configuredAccountId && !isCodexCredentialsExpired(loadedCredentials) && codexRefreshLoopController) return;
@@ -2248,6 +2304,7 @@ const setupCodexToken = async () => {
2248
2304
  let selectedAccountId = configuredAccountId;
2249
2305
  if (!selectedAccountId) {
2250
2306
  store = await readCodexCredentialStore();
2307
+ if (generation !== codexRuntimeGeneration) return;
2251
2308
  if (!store || store.accounts.length === 0) throw new Error(`Codex credentials not found. Run \`copilot-api auth login --provider codex\` first.`);
2252
2309
  if (store.accounts.length > 1) throw new Error("Multiple Codex accounts found but no account is selected. Run `copilot-api auth codex --use <alias-or-accountId>` first.");
2253
2310
  selectedAccountId = store.accounts[0].accountId;
@@ -2261,6 +2318,7 @@ const setupCodexToken = async () => {
2261
2318
  if (loadedCredentials && !selectedLoadedCredentials) stopCodexRefreshLoop();
2262
2319
  if (!selectedLoadedCredentials) {
2263
2320
  store ??= await readCodexCredentialStore();
2321
+ if (generation !== codexRuntimeGeneration) return;
2264
2322
  if (!store || store.accounts.length === 0) throw new Error(`Codex credentials not found. Run \`copilot-api auth login --provider codex\` first.`);
2265
2323
  }
2266
2324
  const credentials = selectedLoadedCredentials ?? store?.accounts.find((account) => account.accountId === selectedAccountId);
@@ -2270,6 +2328,7 @@ const setupCodexToken = async () => {
2270
2328
  consola.debug("Refreshing expired Codex credentials");
2271
2329
  nextCredentials = await refreshCodexCredentialsOnce(credentials);
2272
2330
  }
2331
+ if (generation !== codexRuntimeGeneration) return;
2273
2332
  applyCodexCredentials(nextCredentials);
2274
2333
  stopCodexRefreshLoop();
2275
2334
  const controller = new AbortController();
@@ -2300,6 +2359,7 @@ const runCopilotRefreshLoop = async (refreshIn, signal, dependencies) => {
2300
2359
  consola.debug("Refreshing Copilot token");
2301
2360
  try {
2302
2361
  const response = await dependencies.getCopilotToken();
2362
+ if (signal.aborted) return;
2303
2363
  applyCopilotTokenResponse(response);
2304
2364
  refreshAtMs = getRefreshDeadlineMs(response.refresh_in);
2305
2365
  retryDelayMs = RETRY_REFRESH_DELAY_MS;
@@ -2378,6 +2438,6 @@ async function logUser(dependencies = defaultCopilotTokenDependencies) {
2378
2438
  state.tokenBasedBilling = copilotUser.token_based_billing;
2379
2439
  }
2380
2440
  //#endregion
2381
- export { stripInternalRequestHeaders as $, compactSummaryPromptStart as A, forwardCodexResponses as B, copilotWebSocketHeaders as C, initOpencodeVersion as D, prepareMessageProxyHeaders as E, getConfiguredApiKeys as F, createResponsesHttpEventStream as G, requestContext as H, getMissingApiKeysMessage as I, getResponsesStreamTerminalDisposition as J, createResponsesSafeStream as K, loginCodex as L, compactTextOnlyGuard as M, createAuthMiddleware as N, compactAutoContinuePromptStarts as O, getConfiguredAdminApiKeys as P, FULL_MODEL_CATALOG_HEADER as Q, CODEX_API_BASE_URL as R, copilotModelsHeaders as S, prepareInteractionHeaders as T, resolveTraceId as U, generateTraceId as V, fetchUpstreamWithLifecycle as W, createWebSocketUrl as X, createPooledWebSocketStream as Y, state as Z, GITHUB_TOKEN_ENV as _, selectCodexAccount as a, copilotBaseUrl as b, setupGitHubToken as c, getUUID as d, isAsyncIterable as f, getCopilotUsage as g, parseUserIdMetadata as h, removeCodexAccount as i, compactSystemPromptStarts as j, compactMessageSections as k, generateRequestIdFromPayload as l, isResponsesStream as m, logUser as n, setupCodexToken as o, isNullish as p, encodePoolKeyPart as q, persistCodexCredentials as r, setupCopilotToken as s, getCodexAccounts as t, getRootSessionId as u, readGitHubToken as v, prepareForCompact as w, copilotHeaders as x, readGitHubTokenFromEnv as y, buildCodexRequestHeaders as z };
2441
+ export { state as $, compactAutoContinuePromptStarts as A, CODEX_API_BASE_URL as B, copilotHeaders as C, prepareInteractionHeaders as D, prepareForCompact as E, createAuthMiddleware as F, resolveTraceId as G, forwardCodexResponses as H, getConfiguredAdminApiKeys as I, createResponsesSafeStream as J, fetchUpstreamWithLifecycle as K, getConfiguredApiKeys as L, compactSummaryPromptStart as M, compactSystemPromptStarts as N, prepareMessageProxyHeaders as O, compactTextOnlyGuard as P, createWebSocketUrl as Q, getMissingApiKeysMessage as R, copilotBaseUrl as S, copilotWebSocketHeaders as T, generateTraceId as U, buildCodexRequestHeaders as V, requestContext as W, getResponsesStreamTerminalDisposition as X, encodePoolKeyPart as Y, createPooledWebSocketStream as Z, parseUserIdMetadata as _, removeCodexAccount as a, readGitHubToken as b, setupCopilotToken as c, generateRequestIdFromPayload as d, FULL_MODEL_CATALOG_HEADER as et, getRootSessionId as f, isResponsesStream as g, isNullish as h, persistCodexCredentials as i, compactMessageSections as j, initOpencodeVersion as k, setupGitHubToken as l, isAsyncIterable as m, invalidateCodexRuntime as n, selectCodexAccount as o, getUUID as p, createResponsesHttpEventStream as q, logUser as r, setupCodexToken as s, getCodexAccounts as t, stripInternalRequestHeaders as tt, stopCopilotRefreshLoop as u, getCopilotUsage as v, copilotModelsHeaders as w, readGitHubTokenFromEnv as x, GITHUB_TOKEN_ENV as y, loginCodex as z };
2382
2442
 
2383
- //# sourceMappingURL=token-DgVri1k7.js.map
2443
+ //# sourceMappingURL=token-0pmwyQAu.js.map