@jeffreycao/copilot-api 2.6.28 → 2.6.30

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-CZr63uyO.js";
5
- import { a as runProviderSetup } from "./auth-CgMFSux_.js";
6
- import { n as getLatestModelForFamily } from "./models-C_0HbW3K.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-sTarDtg3.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-DPRCrNJa.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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"}
@@ -1837,6 +1837,8 @@ async function writeCodexCredentials(credentials, options = {}) {
1837
1837
  const accountIdSelector = normalizeCodexAccountSelector(normalizedCredentials.accountId);
1838
1838
  const existingIndex = store.accounts.findIndex((account) => account.accountId === normalizedCredentials.accountId);
1839
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;
1840
1842
  if (existingIndex < 0 && store.accounts.some((account) => normalizeCodexAccountSelector(account.accountId) === accountIdSelector)) throw new Error(`Codex account id '${normalizedCredentials.accountId}' is already in use`);
1841
1843
  if (existingIndex < 0 && store.accounts.length >= 3) throw new Error(`Codex supports at most 3 accounts`);
1842
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`);
@@ -2077,8 +2079,9 @@ function describeError(error) {
2077
2079
  //#region src/lib/token.ts
2078
2080
  let copilotRefreshLoopController = null;
2079
2081
  let codexRefreshLoopController = null;
2080
- let codexRefreshInFlight = null;
2081
- let codexCredentialsPendingPersistence = null;
2082
+ let codexRuntimeGeneration = 0;
2083
+ const codexRefreshInFlight = /* @__PURE__ */ new Map();
2084
+ const codexCredentialsPendingPersistence = /* @__PURE__ */ new Map();
2082
2085
  const defaultCopilotTokenDependencies = {
2083
2086
  getCopilotToken,
2084
2087
  getCopilotUsage
@@ -2093,6 +2096,14 @@ const stopCodexRefreshLoop = () => {
2093
2096
  codexRefreshLoopController.abort();
2094
2097
  codexRefreshLoopController = null;
2095
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
+ };
2096
2107
  function applyCodexCredentials(credentials) {
2097
2108
  state.codexAccessToken = credentials.accessToken;
2098
2109
  state.codexRefreshToken = credentials.refreshToken;
@@ -2173,17 +2184,21 @@ async function removeCodexAccount(selector) {
2173
2184
  });
2174
2185
  }
2175
2186
  async function persistCodexCredentials(credentials, options = {}) {
2187
+ const generation = options.runtimeGeneration ?? codexRuntimeGeneration;
2176
2188
  const persist = async () => {
2177
2189
  await writeCodexCredentials(credentials, {
2178
2190
  alias: options.alias,
2179
- insertIfMissing: options.insertIfMissing
2191
+ insertIfMissing: options.insertIfMissing,
2192
+ expectedRefreshToken: options.expectedRefreshToken
2180
2193
  });
2181
2194
  if (options.syncProvider !== false) syncCodexProviderConfig({
2182
2195
  accountId: options.activateAccount ? credentials.accountId : void 0,
2183
2196
  enabled: options.enableProvider ? true : void 0
2184
2197
  });
2185
- applyCodexCredentials(credentials);
2186
- 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
+ }
2187
2202
  };
2188
2203
  if (options.syncProvider === false) {
2189
2204
  await persist();
@@ -2191,14 +2206,19 @@ async function persistCodexCredentials(credentials, options = {}) {
2191
2206
  }
2192
2207
  await withCodexAccountMutationLock(persist);
2193
2208
  }
2194
- const defaultCodexRefreshDependencies = {
2195
- getCurrentCredentials: getLoadedCodexCredentials,
2196
- persistCodexCredentials: (credentials) => persistCodexCredentials(credentials, {
2197
- insertIfMissing: false,
2198
- syncProvider: false
2199
- }),
2200
- refreshCodexCredentials
2201
- };
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
+ }
2202
2222
  /**
2203
2223
  * Refreshes and persists Codex credentials through one module level
2204
2224
  * single-flight guard.
@@ -2209,29 +2229,31 @@ const defaultCodexRefreshDependencies = {
2209
2229
  * Codex request, so a burst of requests (or the background loop waking at the
2210
2230
  * same moment) must share a single attempt instead of burning the token.
2211
2231
  */
2212
- function refreshCodexCredentialsOnce(credentials, dependencies = defaultCodexRefreshDependencies) {
2213
- 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);
2214
2239
  if (inFlight) return inFlight;
2215
2240
  const attempt = (async () => {
2216
- const pendingPersistence = codexCredentialsPendingPersistence;
2241
+ const pendingPersistence = codexCredentialsPendingPersistence.get(refreshKey);
2217
2242
  if (pendingPersistence) {
2218
- await dependencies.persistCodexCredentials(pendingPersistence);
2219
- if (codexCredentialsPendingPersistence === pendingPersistence) codexCredentialsPendingPersistence = null;
2243
+ await refreshDependencies.persistCodexCredentials(pendingPersistence);
2244
+ if (codexCredentialsPendingPersistence.get(refreshKey) === pendingPersistence) codexCredentialsPendingPersistence.delete(refreshKey);
2220
2245
  return pendingPersistence;
2221
2246
  }
2222
- const current = dependencies.getCurrentCredentials();
2223
- const rotated = current && current.refreshToken !== credentials.refreshToken ? current : null;
2224
2247
  if (rotated && !isCodexCredentialsExpired(rotated)) return rotated;
2225
- const base = rotated ?? credentials;
2226
- const refreshed = await dependencies.refreshCodexCredentials(base);
2227
- codexCredentialsPendingPersistence = refreshed;
2228
- await dependencies.persistCodexCredentials(refreshed);
2229
- 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);
2230
2252
  return refreshed;
2231
2253
  })();
2232
- codexRefreshInFlight = attempt;
2254
+ codexRefreshInFlight.set(refreshKey, attempt);
2233
2255
  const clearAttempt = () => {
2234
- if (codexRefreshInFlight === attempt) codexRefreshInFlight = null;
2256
+ if (codexRefreshInFlight.get(refreshKey) === attempt) codexRefreshInFlight.delete(refreshKey);
2235
2257
  };
2236
2258
  attempt.then(clearAttempt, clearAttempt);
2237
2259
  return attempt;
@@ -2264,6 +2286,17 @@ const setupCopilotToken = async (dependencies = defaultCopilotTokenDependencies)
2264
2286
  });
2265
2287
  };
2266
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) => {
2267
2300
  const configuredAccountId = getConfiguredCodexAccountId();
2268
2301
  const loadedCredentials = getLoadedCodexCredentials();
2269
2302
  if (configuredAccountId && loadedCredentials?.accountId === configuredAccountId && !isCodexCredentialsExpired(loadedCredentials) && codexRefreshLoopController) return;
@@ -2271,6 +2304,7 @@ const setupCodexToken = async () => {
2271
2304
  let selectedAccountId = configuredAccountId;
2272
2305
  if (!selectedAccountId) {
2273
2306
  store = await readCodexCredentialStore();
2307
+ if (generation !== codexRuntimeGeneration) return;
2274
2308
  if (!store || store.accounts.length === 0) throw new Error(`Codex credentials not found. Run \`copilot-api auth login --provider codex\` first.`);
2275
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.");
2276
2310
  selectedAccountId = store.accounts[0].accountId;
@@ -2284,6 +2318,7 @@ const setupCodexToken = async () => {
2284
2318
  if (loadedCredentials && !selectedLoadedCredentials) stopCodexRefreshLoop();
2285
2319
  if (!selectedLoadedCredentials) {
2286
2320
  store ??= await readCodexCredentialStore();
2321
+ if (generation !== codexRuntimeGeneration) return;
2287
2322
  if (!store || store.accounts.length === 0) throw new Error(`Codex credentials not found. Run \`copilot-api auth login --provider codex\` first.`);
2288
2323
  }
2289
2324
  const credentials = selectedLoadedCredentials ?? store?.accounts.find((account) => account.accountId === selectedAccountId);
@@ -2293,6 +2328,7 @@ const setupCodexToken = async () => {
2293
2328
  consola.debug("Refreshing expired Codex credentials");
2294
2329
  nextCredentials = await refreshCodexCredentialsOnce(credentials);
2295
2330
  }
2331
+ if (generation !== codexRuntimeGeneration) return;
2296
2332
  applyCodexCredentials(nextCredentials);
2297
2333
  stopCodexRefreshLoop();
2298
2334
  const controller = new AbortController();
@@ -2323,6 +2359,7 @@ const runCopilotRefreshLoop = async (refreshIn, signal, dependencies) => {
2323
2359
  consola.debug("Refreshing Copilot token");
2324
2360
  try {
2325
2361
  const response = await dependencies.getCopilotToken();
2362
+ if (signal.aborted) return;
2326
2363
  applyCopilotTokenResponse(response);
2327
2364
  refreshAtMs = getRefreshDeadlineMs(response.refresh_in);
2328
2365
  retryDelayMs = RETRY_REFRESH_DELAY_MS;
@@ -2401,6 +2438,6 @@ async function logUser(dependencies = defaultCopilotTokenDependencies) {
2401
2438
  state.tokenBasedBilling = copilotUser.token_based_billing;
2402
2439
  }
2403
2440
  //#endregion
2404
- 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 };
2405
2442
 
2406
- //# sourceMappingURL=token-CZr63uyO.js.map
2443
+ //# sourceMappingURL=token-0pmwyQAu.js.map