codex-copilot-dx 0.5.6 → 0.6.1

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,6 +1,10 @@
1
- import fs from "node:fs";
2
1
  import os from "node:os";
3
- import { githubTokenPath, validateGithubToken } from "./auth.mjs";
2
+ import { validateGithubToken } from "./auth.mjs";
3
+ import {
4
+ AUTH_PROFILE_CLAUDE,
5
+ AUTH_PROFILE_CODEX,
6
+ readAuthProfileCredentials,
7
+ } from "./auth-profile.mjs";
4
8
  import { buildHeaders, DEFAULT_API_BASE, FALLBACK_VSCODE_VERSION } from "./copilot.mjs";
5
9
  import { status } from "./status.mjs";
6
10
 
@@ -69,21 +73,41 @@ async function responseText(response) {
69
73
  }
70
74
  }
71
75
 
76
+ function readModelProfile(profile, home) {
77
+ const requested = String(profile || AUTH_PROFILE_CODEX).trim().toLowerCase();
78
+ if (requested !== AUTH_PROFILE_CODEX && requested !== AUTH_PROFILE_CLAUDE) {
79
+ throw new Error(`Unsupported models profile: ${profile}`);
80
+ }
81
+
82
+ let credentials;
83
+ try {
84
+ credentials = readAuthProfileCredentials(requested, { home });
85
+ } catch (error) {
86
+ const label = requested === AUTH_PROFILE_CLAUDE ? "Claude GitHub profile" : "GitHub token";
87
+ throw new Error(`Could not read the ${label}: ${error.message}`);
88
+ }
89
+ if (credentials.valid) return credentials;
90
+
91
+ if (requested === AUTH_PROFILE_CLAUDE) {
92
+ if (!credentials.configured) {
93
+ throw new Error("Claude GitHub profile is not configured. Run ccdx auth login claude.");
94
+ }
95
+ throw new Error(`Claude GitHub profile is invalid (${credentials.reason || "unknown"}). Run ccdx auth login claude --reauth.`);
96
+ }
97
+ if (!credentials.configured) throw new Error("GitHub token not found. Start ccdx once to log in.");
98
+ if (credentials.reason === "empty_token") throw new Error("GitHub token file is empty. Start ccdx again to log in.");
99
+ throw new Error(`GitHub token is unavailable (${credentials.reason || "unknown"}). Start ccdx again to log in.`);
100
+ }
101
+
72
102
  export async function fetchLiveCopilotModels({
73
103
  home = os.homedir(),
104
+ profile = AUTH_PROFILE_CODEX,
74
105
  fetchImpl = fetch,
75
106
  timeoutMs = DEFAULT_MODELS_TIMEOUT_MS,
76
107
  vscodeVersion = FALLBACK_VSCODE_VERSION,
77
108
  } = {}) {
78
- const tokenPath = githubTokenPath(home);
79
- let githubToken;
80
- try {
81
- githubToken = fs.readFileSync(tokenPath, "utf8").trim();
82
- } catch (error) {
83
- if (error?.code === "ENOENT") throw new Error("GitHub token not found. Start ccdx once to log in.");
84
- throw new Error(`Could not read the GitHub token: ${error.message}`);
85
- }
86
- if (!githubToken) throw new Error("GitHub token file is empty. Start ccdx again to log in.");
109
+ const credentials = readModelProfile(profile, home);
110
+ const githubToken = credentials.token;
87
111
 
88
112
  const controller = new AbortController();
89
113
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -123,7 +147,7 @@ export async function fetchLiveCopilotModels({
123
147
  const catalog = selectableCopilotModels(payload);
124
148
  let upstreamHost = "GitHub Copilot";
125
149
  try { upstreamHost = new URL(apiBase).hostname; } catch {}
126
- return { ...catalog, upstreamHost: safeText(upstreamHost, "GitHub Copilot") };
150
+ return { ...catalog, profile: credentials.profile, upstreamHost: safeText(upstreamHost, "GitHub Copilot") };
127
151
  } catch (error) {
128
152
  if (controller.signal.aborted && !/timed out/.test(error?.message || "")) {
129
153
  throw new Error(`Live model lookup timed out after ${timeoutMs}ms`);
@@ -136,12 +160,13 @@ export async function fetchLiveCopilotModels({
136
160
 
137
161
  export function formatLiveCopilotModels(catalog, { commandName = "ccdx" } = {}) {
138
162
  const models = Array.isArray(catalog?.models) ? catalog.models : [];
163
+ const profile = catalog?.profile === AUTH_PROFILE_CLAUDE ? AUTH_PROFILE_CLAUDE : AUTH_PROFILE_CODEX;
139
164
  const responses = models.filter((model) => model.endpoints.includes("responses")).length;
140
165
  const chat = models.filter((model) => model.endpoints.includes("chat")).length;
141
166
  const claude = models.filter((model) => model.id.toLowerCase().startsWith("claude-")
142
167
  || model.vendor.toLowerCase() === "anthropic").length;
143
168
  const lines = [
144
- `${commandName} models`,
169
+ `${commandName} models${profile === AUTH_PROFILE_CLAUDE ? " --profile claude" : ""}`,
145
170
  status("ok", `Live catalog from ${safeText(catalog?.upstreamHost, "GitHub Copilot")}: ${models.length} selectable of ${Number(catalog?.advertised) || 0} advertised`),
146
171
  status("info", `Responses: ${responses}; Chat: ${chat}; Claude/Anthropic: ${claude}`),
147
172
  ];
@@ -7,6 +7,38 @@ function unexpectedArgs(args) {
7
7
  throw new Error(`Unexpected argument${args.length === 1 ? "" : "s"}: ${args.join(" ")}`);
8
8
  }
9
9
 
10
+ function parseOptions(args, schema) {
11
+ const parsed = {};
12
+ const seen = new Set();
13
+ for (let index = 0; index < args.length; index += 1) {
14
+ const option = args[index];
15
+ const kind = schema.get(option);
16
+ if (!kind || seen.has(option)) unexpectedArgs([option]);
17
+ seen.add(option);
18
+ if (kind === "flag") {
19
+ parsed[option] = true;
20
+ continue;
21
+ }
22
+
23
+ const value = args[index + 1];
24
+ if (value === undefined || !String(value).trim() || String(value).startsWith("-")) {
25
+ throw new Error(`Missing value for ${option}`);
26
+ }
27
+ parsed[option] = value;
28
+ index += 1;
29
+ }
30
+ return parsed;
31
+ }
32
+
33
+ function checkedProfile(value, { allowAll = false } = {}) {
34
+ const profile = value || "codex";
35
+ const allowed = allowAll ? new Set(["codex", "claude", "all"]) : new Set(["codex", "claude"]);
36
+ if (!allowed.has(profile)) {
37
+ throw new Error(`Profile must be ${allowAll ? "codex, claude, or all" : "codex or claude"}: ${profile}`);
38
+ }
39
+ return profile;
40
+ }
41
+
10
42
  export function parseCliArgs(args = []) {
11
43
  const [command, ...rest] = args;
12
44
  if (!command) return { command: "start", configureClaudeDesktop: false, showRequestId: false, online: false, compat: false };
@@ -26,9 +58,62 @@ export function parseCliArgs(args = []) {
26
58
  if (rest.length) unexpectedArgs(rest);
27
59
  return { command: "usage", configureClaudeDesktop: false, showRequestId: false, online: false, compat: false };
28
60
  }
61
+ if (command === "pms") {
62
+ const [action, ...pmsArgs] = rest;
63
+ if (!action) throw new Error("Missing pms action: expected setup");
64
+ if (action !== "setup") throw new Error(`Unknown pms action: ${action}`);
65
+ if (pmsArgs.length) unexpectedArgs(pmsArgs);
66
+ return { command: "pms", action: "setup", configureClaudeDesktop: false, showRequestId: false, online: false, compat: false };
67
+ }
29
68
  if (command === "models") {
30
- if (rest.length) unexpectedArgs(rest);
31
- return { command: "models", configureClaudeDesktop: false, showRequestId: false, online: false, compat: false };
69
+ const options = parseOptions(rest, new Map([["--profile", "value"]]));
70
+ return {
71
+ command: "models",
72
+ configureClaudeDesktop: false,
73
+ showRequestId: false,
74
+ online: false,
75
+ compat: false,
76
+ profile: checkedProfile(options["--profile"]),
77
+ };
78
+ }
79
+ if (command === "auth") {
80
+ const [action, ...authArgs] = rest;
81
+ if (!action) throw new Error("Missing auth action: expected status or login");
82
+ if (action === "status") {
83
+ const options = parseOptions(authArgs, new Map([["--online", "flag"]]));
84
+ return {
85
+ command: "auth",
86
+ action: "status",
87
+ profile: "",
88
+ configureClaudeDesktop: false,
89
+ showRequestId: false,
90
+ online: Boolean(options["--online"]),
91
+ compat: false,
92
+ expectedLogin: "",
93
+ reauth: false,
94
+ };
95
+ }
96
+ if (action === "login") {
97
+ const [profile, ...loginArgs] = authArgs;
98
+ if (!profile) throw new Error("Missing auth login profile: expected claude");
99
+ if (profile !== "claude") throw new Error(`Auth login profile must be claude: ${profile}`);
100
+ const options = parseOptions(loginArgs, new Map([
101
+ ["--github-login", "value"],
102
+ ["--reauth", "flag"],
103
+ ]));
104
+ return {
105
+ command: "auth",
106
+ action: "login",
107
+ profile: "claude",
108
+ configureClaudeDesktop: false,
109
+ showRequestId: false,
110
+ online: false,
111
+ compat: false,
112
+ expectedLogin: options["--github-login"] || "",
113
+ reauth: Boolean(options["--reauth"]),
114
+ };
115
+ }
116
+ throw new Error(`Unknown auth action: ${action}`);
32
117
  }
33
118
  if (command === "auto-review-model") {
34
119
  if (rest.length) unexpectedArgs(rest);
@@ -50,18 +135,18 @@ export function parseCliArgs(args = []) {
50
135
  };
51
136
  }
52
137
  if (command === "doctor" || command === "--doctor") {
53
- const supported = new Set(["--online", "--compat"]);
54
- const invalid = rest.filter((arg) => !supported.has(arg));
55
- if (invalid.length) unexpectedArgs(invalid);
56
- for (const option of supported) {
57
- if (rest.filter((arg) => arg === option).length > 1) unexpectedArgs([option]);
58
- }
138
+ const options = parseOptions(rest, new Map([
139
+ ["--online", "flag"],
140
+ ["--compat", "flag"],
141
+ ["--profile", "value"],
142
+ ]));
59
143
  return {
60
144
  command: "doctor",
61
145
  configureClaudeDesktop: false,
62
146
  showRequestId: false,
63
- online: rest.includes("--online"),
64
- compat: rest.includes("--compat"),
147
+ online: Boolean(options["--online"]),
148
+ compat: Boolean(options["--compat"]),
149
+ profile: checkedProfile(options["--profile"], { allowAll: true }),
65
150
  };
66
151
  }
67
152
  if (START_OPTIONS.has(command)) {
@@ -127,9 +212,12 @@ export function cliHelp(commandName = "ccdx") {
127
212
  const alias = name === "ccdx" ? "codex-copilot-dx" : "ccdx";
128
213
  return `Usage:
129
214
  ${name} [--configure-claude-desktop] [--show-request-id]
130
- ${name} doctor [--online] [--compat]
215
+ ${name} auth status [--online]
216
+ ${name} auth login claude [--github-login <login>] [--reauth]
217
+ ${name} doctor [--online] [--compat] [--profile codex|claude|all]
131
218
  ${name} status
132
- ${name} models
219
+ ${name} models [--profile codex|claude]
220
+ ${name} pms setup
133
221
  ${name} usage
134
222
  ${name} auto-review-model
135
223
  ${name} update [npm|github]
@@ -51,6 +51,25 @@ function cacheHitRate(cache = {}) {
51
51
  return `${((hits / (hits + misses)) * 100).toFixed(1)}%`;
52
52
  }
53
53
 
54
+ function profileSummary(label, profile = {}) {
55
+ const client = profile.client || {};
56
+ const models = profile.models || {};
57
+ const mode = ["legacy", "inherited", "isolated"].includes(profile.mode)
58
+ ? profile.mode
59
+ : "unknown";
60
+ const source = typeof models.source === "string" && models.source ? models.source : "unknown";
61
+ const token = client.token_cached
62
+ ? `token cached (${duration(client.token_expires_in_ms)} remaining)`
63
+ : "token not cached";
64
+ return `${label} ${mode}: ${token}, ${count(models.models)} total/${count(models.claude_models)} Claude models (${source})`;
65
+ }
66
+
67
+ function routingTarget(value) {
68
+ if (value === "codex") return "Codex";
69
+ if (value === "claude") return "Claude";
70
+ return "unknown";
71
+ }
72
+
54
73
  export async function readAdapterStatus({
55
74
  host = "127.0.0.1",
56
75
  port = 2026,
@@ -107,7 +126,7 @@ export function formatAdapterStatus({ baseUrl, data }, { commandName = "ccdx", c
107
126
  ? status("ok", `Adapter ${data.version} is running at ${baseUrl} (PID ${data.pid}, uptime ${duration(data.uptime_ms)})`)
108
127
  : status("warn", `Adapter ${data.version} is running at ${baseUrl}, but this CLI is ${cliVersion}; stop the running adapter before switching versions`);
109
128
 
110
- return [
129
+ const lines = [
111
130
  `${commandName} status`,
112
131
  adapterLine,
113
132
  status("info", `Requests: ${count(requests.completed)}/${count(requests.total)} completed, ${count(requests.active)} active, ${count(requests.status_4xx)} 4xx, ${count(requests.status_5xx)} 5xx, ${count(requests.aborted)} aborted`),
@@ -117,6 +136,13 @@ export function formatAdapterStatus({ baseUrl, data }, { commandName = "ccdx", c
117
136
  status("info", `History: ${mebibytes(history.bytes)} / ${mebibytes(limits.response_history_max_bytes)}, ${count(history.entries)} entries, ${count(history.evicted)} evicted`),
118
137
  status("info", `Image cache: ${count(images.cache_entries)} entries, ${cacheHitRate(images)} hits, ${mebibytes(images.cache_bytes)} / ${mebibytes(images.cache_max_bytes)}`),
119
138
  status("info", `Models: ${count(models.models)} total, ${count(models.claude_models)} Claude; Copilot token ${copilot.token_cached ? `cached (${duration(copilot.token_expires_in_ms)} remaining)` : "not cached"}`),
120
- status("info", `Limits: request body ${mebibytes(limits.max_body_bytes)}, decoded body ${mebibytes(limits.max_decoded_body_bytes)}`),
121
- ].join("\n");
139
+ ];
140
+ if (data.profiles?.codex || data.profiles?.claude) {
141
+ lines.push(status("info", `Profiles: ${profileSummary("Codex", data.profiles?.codex)}; ${profileSummary("Claude", data.profiles?.claude)}`));
142
+ }
143
+ if (data.routing) {
144
+ lines.push(status("info", `Routing: /v1/responses -> ${routingTarget(data.routing.responses)}; /v1/messages -> ${routingTarget(data.routing.messages)}`));
145
+ }
146
+ lines.push(status("info", `Limits: request body ${mebibytes(limits.max_body_bytes)}, decoded body ${mebibytes(limits.max_decoded_body_bytes)}`));
147
+ return lines.join("\n");
122
148
  }
@@ -0,0 +1,226 @@
1
+ import { createCopilotTokenSession } from "./copilot-token-session.mjs";
2
+ import { prepareResponsesPayload, summarizeReqBody } from "./image-optimization.mjs";
3
+ import { debugLog } from "./log.mjs";
4
+ import { enforceResponsesPayloadByteBudget } from "./responses-byte-budget.mjs";
5
+ import { status } from "./status.mjs";
6
+ import { withChatStreamUsage } from "./stream-contract.mjs";
7
+ import { markUpstreamStarted } from "./stream-performance.mjs";
8
+
9
+ function abortError(signal) {
10
+ if (signal?.reason instanceof Error) return signal.reason;
11
+ const error = new Error("The operation was aborted");
12
+ error.name = "AbortError";
13
+ error.code = "ABORT_ERR";
14
+ return error;
15
+ }
16
+
17
+ function waitForSingleflight(flight, signal) {
18
+ if (signal?.aborted) return Promise.reject(abortError(signal));
19
+ flight.waiters += 1;
20
+ return new Promise((resolve, reject) => {
21
+ let finished = false;
22
+ const finish = (handler, value) => {
23
+ if (finished) return;
24
+ finished = true;
25
+ signal?.removeEventListener("abort", onAbort);
26
+ flight.waiters -= 1;
27
+ if (!flight.settled && flight.waiters === 0) flight.controller.abort();
28
+ handler(value);
29
+ };
30
+ const onAbort = () => finish(reject, abortError(signal));
31
+ signal?.addEventListener("abort", onAbort, { once: true });
32
+ if (signal?.aborted) onAbort();
33
+ flight.promise.then(
34
+ (value) => finish(resolve, value),
35
+ (error) => finish(reject, error),
36
+ );
37
+ });
38
+ }
39
+
40
+ export function createCopilotClientRuntime({
41
+ profile = "codex",
42
+ defaultApiBase,
43
+ parseApiBase,
44
+ fetchCopilotUpstream,
45
+ buildHeaders,
46
+ getVSCodeVersion,
47
+ computeInitiator,
48
+ computeVision,
49
+ responsesEndpointPath,
50
+ ...sessionOptions
51
+ } = {}) {
52
+ const tokenSession = createCopilotTokenSession({
53
+ ...sessionOptions,
54
+ profile,
55
+ defaultApiBase,
56
+ parseApiBase,
57
+ fetchCopilotUpstream,
58
+ });
59
+ let modelEndpointCache = new Map();
60
+ const modelListFlights = new Map();
61
+
62
+ function cacheModelEndpoints(models) {
63
+ const data = Array.isArray(models) ? models : models?.data;
64
+ if (!Array.isArray(data)) return false;
65
+ const next = new Map();
66
+ for (const model of data) {
67
+ const id = String(model?.id || "").trim();
68
+ if (id && Array.isArray(model?.supported_endpoints)) next.set(id, [...model.supported_endpoints]);
69
+ }
70
+ if (!next.size) return false;
71
+ modelEndpointCache = next;
72
+ return true;
73
+ }
74
+
75
+ function getCachedModelEndpoints(modelId) {
76
+ return modelEndpointCache.get(String(modelId || "").trim()) || null;
77
+ }
78
+
79
+ function resetModelEndpointCacheForTests() {
80
+ modelEndpointCache.clear();
81
+ }
82
+
83
+ async function chatCompletions(chatReq, {
84
+ signal,
85
+ fetchImpl,
86
+ retryOptions,
87
+ bodyText,
88
+ } = {}) {
89
+ const token = await tokenSession.getToken({ signal });
90
+ const upstreamReq = chatReq.stream === true ? withChatStreamUsage(chatReq) : chatReq;
91
+ const messages = upstreamReq.messages || [];
92
+ const serializedBody = typeof bodyText === "string" && upstreamReq === chatReq
93
+ ? bodyText
94
+ : JSON.stringify(upstreamReq);
95
+ const headers = buildHeaders({
96
+ token,
97
+ version: getVSCodeVersion(),
98
+ initiator: computeInitiator(messages),
99
+ vision: computeVision(messages),
100
+ });
101
+ headers["Content-Length"] = String(Buffer.byteLength(serializedBody));
102
+ if (upstreamReq.stream === true) markUpstreamStarted();
103
+ return fetchCopilotUpstream(`${tokenSession.getApiBase()}/chat/completions`, {
104
+ method: "POST",
105
+ headers,
106
+ body: serializedBody,
107
+ signal,
108
+ }, { fetchImpl, ...retryOptions });
109
+ }
110
+
111
+ async function fetchModels({ signal, fetchImpl, retryOptions }) {
112
+ const token = await tokenSession.getToken({ signal });
113
+ const headers = buildHeaders({ token, version: getVSCodeVersion(), initiator: "user", vision: false });
114
+ const response = await fetchCopilotUpstream(`${tokenSession.getApiBase()}/models`, {
115
+ headers,
116
+ signal,
117
+ }, { fetchImpl, ...retryOptions });
118
+ const body = await response.text();
119
+ if (response.ok) {
120
+ try { cacheModelEndpoints(JSON.parse(body)); } catch {}
121
+ }
122
+ return { status: response.status, body };
123
+ }
124
+
125
+ function listModels({ signal, fetchImpl = fetch, retryOptions } = {}) {
126
+ if (signal?.aborted) return Promise.reject(abortError(signal));
127
+ let flight = modelListFlights.get(fetchImpl);
128
+ if (flight?.controller.signal.aborted) {
129
+ modelListFlights.delete(fetchImpl);
130
+ flight = null;
131
+ }
132
+ if (!flight) {
133
+ const controller = new AbortController();
134
+ flight = { controller, waiters: 0, settled: false, promise: null };
135
+ flight.promise = fetchModels({ signal: controller.signal, fetchImpl, retryOptions })
136
+ .finally(() => {
137
+ flight.settled = true;
138
+ if (modelListFlights.get(fetchImpl) === flight) modelListFlights.delete(fetchImpl);
139
+ });
140
+ modelListFlights.set(fetchImpl, flight);
141
+ }
142
+ return waitForSingleflight(flight, signal);
143
+ }
144
+
145
+ function resetModelListSingleflightForTests() {
146
+ for (const flight of modelListFlights.values()) flight.controller.abort();
147
+ modelListFlights.clear();
148
+ }
149
+
150
+ async function responses(reqBody, {
151
+ signal,
152
+ fetchImpl,
153
+ retryOptions,
154
+ currentInputStart = 0,
155
+ payloadPrepared = false,
156
+ payloadOptions = {},
157
+ } = {}) {
158
+ const token = await tokenSession.getToken({ signal });
159
+ const preparedPayload = await prepareResponsesPayload(reqBody, {
160
+ ...payloadOptions,
161
+ currentInputStart,
162
+ profiles: payloadPrepared ? [] : payloadOptions.profiles,
163
+ skipInitialOptimization: payloadPrepared || payloadOptions.skipInitialOptimization,
164
+ signal,
165
+ });
166
+ const finalizedPayload = enforceResponsesPayloadByteBudget(reqBody, preparedPayload);
167
+ const { bodyText, bodyBytes, stage, adapted } = finalizedPayload;
168
+ const summary = finalizedPayload.stage === preparedPayload.stage
169
+ ? preparedPayload.summary
170
+ : summarizeReqBody(reqBody);
171
+ console.log(status("info", `responses payload bytes=${bodyBytes} input_items=${summary.items} images=${summary.images}`));
172
+ if (adapted) debugLog(`responses payload adapted stage=${stage} bytes=${bodyBytes}/${preparedPayload.targetBytes}`);
173
+ const headers = buildHeaders({ token, version: getVSCodeVersion(), initiator: "user", vision: false });
174
+ headers["Content-Type"] = "application/json; charset=utf-8";
175
+ headers["Content-Length"] = String(bodyBytes);
176
+ headers.Accept = reqBody.stream ? "text/event-stream" : "application/json";
177
+ try {
178
+ if (reqBody.stream === true) markUpstreamStarted();
179
+ return await fetchCopilotUpstream(`${tokenSession.getApiBase()}${responsesEndpointPath()}`, {
180
+ method: "POST",
181
+ headers,
182
+ body: bodyText,
183
+ signal,
184
+ }, { fetchImpl, ...retryOptions });
185
+ } catch (error) {
186
+ const cause = error?.cause;
187
+ const causeText = cause ? ` (${[cause.code, cause.message].filter(Boolean).join(": ")})` : "";
188
+ throw new Error(`Copilot responses fetch failed: ${error.message}${causeText}`);
189
+ }
190
+ }
191
+
192
+ function responsesCompact(reqBody, options = {}) {
193
+ return responses(reqBody, options);
194
+ }
195
+
196
+ function runtimeStatus(now = Date.now()) {
197
+ return {
198
+ ...tokenSession.runtimeStatus(now),
199
+ model_endpoint_cache_entries: modelEndpointCache.size,
200
+ model_list_flights: modelListFlights.size,
201
+ };
202
+ }
203
+
204
+ function resetForTests() {
205
+ tokenSession.resetForTests();
206
+ resetModelEndpointCacheForTests();
207
+ resetModelListSingleflightForTests();
208
+ }
209
+
210
+ return Object.freeze({
211
+ profile,
212
+ cacheModelEndpoints,
213
+ getCachedModelEndpoints,
214
+ resetModelEndpointCacheForTests,
215
+ getApiBase: tokenSession.getApiBase,
216
+ runtimeStatus,
217
+ getCopilotToken: tokenSession.getToken,
218
+ resetTokenForTests: tokenSession.resetForTests,
219
+ chatCompletions,
220
+ listModels,
221
+ resetModelListSingleflightForTests,
222
+ responses,
223
+ responsesCompact,
224
+ resetForTests,
225
+ });
226
+ }