@nick3/copilot-api 1.10.8 → 1.10.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.
Files changed (55) hide show
  1. package/README.md +141 -46
  2. package/README.zh-CN.md +141 -46
  3. package/dist/{account-COtMmvzU.js → account-DpW8RaT6.js} +3 -3
  4. package/dist/{account-COtMmvzU.js.map → account-DpW8RaT6.js.map} +1 -1
  5. package/dist/admin/AGENTS.md +19 -0
  6. package/dist/auth-nO-eHeO_.js +327 -0
  7. package/dist/auth-nO-eHeO_.js.map +1 -0
  8. package/dist/{check-usage-DdevqHE5.js → check-usage-ZifYvA3w.js} +4 -42
  9. package/dist/check-usage-ZifYvA3w.js.map +1 -0
  10. package/dist/config-CmhIPHn_.js +578 -0
  11. package/dist/config-CmhIPHn_.js.map +1 -0
  12. package/dist/{debug-BMo6ltbp.js → debug-DvpksqEL.js} +18 -7
  13. package/dist/debug-DvpksqEL.js.map +1 -0
  14. package/dist/main.js +5 -10
  15. package/dist/main.js.map +1 -1
  16. package/dist/mcp-http-BhELuvog.js +2 -0
  17. package/dist/mcp-http-DI4Vz01p.js +82 -0
  18. package/dist/mcp-http-DI4Vz01p.js.map +1 -0
  19. package/dist/mcp-http-config-DMdUDz1D.js +39 -0
  20. package/dist/mcp-http-config-DMdUDz1D.js.map +1 -0
  21. package/dist/mcp-pLTPS0tO.js +79 -0
  22. package/dist/mcp-pLTPS0tO.js.map +1 -0
  23. package/dist/{tool-search-BrN7M0Dd.js → mcp-server-DEqHrXFq.js} +25 -2
  24. package/dist/mcp-server-DEqHrXFq.js.map +1 -0
  25. package/dist/{paths-CclKwouX.js → paths-Bpsb62LK.js} +3 -1
  26. package/dist/paths-Bpsb62LK.js.map +1 -0
  27. package/dist/{poll-access-token-BAgM2-7k.js → poll-access-token-GzVkiTH8.js} +71 -4
  28. package/dist/poll-access-token-GzVkiTH8.js.map +1 -0
  29. package/dist/{request-outbound-BJjWS_jF.js → request-outbound-BkEA8Wgb.js} +1 -1
  30. package/dist/{request-outbound-Pu1kp2x8.js → request-outbound-DZTxxtcx.js} +3 -3
  31. package/dist/{request-outbound-Pu1kp2x8.js.map → request-outbound-DZTxxtcx.js.map} +1 -1
  32. package/dist/{proxy-_U-hgwIn.js → responses-bridge-registry-BJ5Sbh6-.js} +116 -577
  33. package/dist/responses-bridge-registry-BJ5Sbh6-.js.map +1 -0
  34. package/dist/{server-DulP9mYd.js → server-DJ3_UGc4.js} +339 -168
  35. package/dist/server-DJ3_UGc4.js.map +1 -0
  36. package/dist/start-DaB0AcjZ.js +526 -0
  37. package/dist/start-DaB0AcjZ.js.map +1 -0
  38. package/dist/token-DrFDLVxa.js +365 -0
  39. package/dist/token-DrFDLVxa.js.map +1 -0
  40. package/package.json +1 -1
  41. package/dist/auth-B0y-2njL.js +0 -226
  42. package/dist/auth-B0y-2njL.js.map +0 -1
  43. package/dist/check-usage-DdevqHE5.js.map +0 -1
  44. package/dist/debug-BMo6ltbp.js.map +0 -1
  45. package/dist/get-copilot-token-8Rm-rVsp.js +0 -17
  46. package/dist/get-copilot-token-8Rm-rVsp.js.map +0 -1
  47. package/dist/mcp-9Hgepkc5.js +0 -37
  48. package/dist/mcp-9Hgepkc5.js.map +0 -1
  49. package/dist/paths-CclKwouX.js.map +0 -1
  50. package/dist/poll-access-token-BAgM2-7k.js.map +0 -1
  51. package/dist/proxy-_U-hgwIn.js.map +0 -1
  52. package/dist/server-DulP9mYd.js.map +0 -1
  53. package/dist/start-CtEoE2gt.js +0 -274
  54. package/dist/start-CtEoE2gt.js.map +0 -1
  55. package/dist/tool-search-BrN7M0Dd.js.map +0 -1
@@ -1,538 +1,112 @@
1
- import { M as consumeOutboundHeadersSnapshot, N as requestContext, _ as HTTPError, g as getCopilotUsage, k as accountFromState, m as getGitHubUser, x as copilotModelsHeaders, y as copilotBaseUrl } from "./poll-access-token-BAgM2-7k.js";
2
- import { b as getCurrentIdentityEnvironment, c as isAccountEnabled, f as readLegacyToken, h as saveAccountToken, i as ensureAccountClientIdentity, l as listAccountsFromRegistry, o as hasLegacyToken, r as addAccountToRegistry, s as hasRegistry, u as loadAccountToken, v as buildIdentityKey, y as createAccountSessionId } from "./account-COtMmvzU.js";
3
- import { t as PATHS } from "./paths-CclKwouX.js";
4
- import { t as getCopilotToken } from "./get-copilot-token-8Rm-rVsp.js";
5
- import { c as getAdminDbUserVersion, i as getRequestOutboundStore, o as getAdminDb, s as getAdminDbPath } from "./request-outbound-Pu1kp2x8.js";
1
+ import { A as accountFromState, N as consumeOutboundHeadersSnapshot, P as requestContext, S as copilotBaseUrl, _ as getCopilotToken, b as HTTPError, g as getCopilotUsage, m as getGitHubUser, w as copilotModelsHeaders } from "./poll-access-token-GzVkiTH8.js";
2
+ import { b as getCurrentIdentityEnvironment, c as isAccountEnabled, f as readLegacyToken, h as saveAccountToken, i as ensureAccountClientIdentity, l as listAccountsFromRegistry, o as hasLegacyToken, r as addAccountToRegistry, s as hasRegistry, u as loadAccountToken, v as buildIdentityKey, y as createAccountSessionId } from "./account-DpW8RaT6.js";
3
+ import { t as PATHS } from "./paths-Bpsb62LK.js";
4
+ import { E as resolveModelAlias, a as getConfig, f as getQuotaRefreshConfig, h as getSessionAffinityRetentionMs } from "./config-CmhIPHn_.js";
5
+ import { c as getAdminDbUserVersion, i as getRequestOutboundStore, o as getAdminDb, s as getAdminDbPath } from "./request-outbound-DZTxxtcx.js";
6
6
  import consola, { consola as consola$1 } from "consola";
7
7
  import fs from "node:fs/promises";
8
+ import { timingSafeEqual } from "node:crypto";
8
9
  import fs$1 from "node:fs";
9
- import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
10
- import { getProxyForUrl } from "proxy-from-env";
11
- //#region src/lib/config.ts
12
- const PROVIDER_TYPE_ANTHROPIC = "anthropic";
13
- const gpt5ExplorationPrompt = `## Exploration and reading files
14
- - **Think first.** Before any tool call, decide ALL files/resources you will need.
15
- - **Batch everything.** If you need multiple files (even from different places), read them together.
16
- - **multi_tool_use.parallel** Use multi_tool_use.parallel to parallelize tool calls and only this.
17
- - **Only make sequential calls if you truly cannot know the next file without seeing a result first.**
18
- - **Workflow:** (a) plan all needed reads (b) issue one parallel batch → (c) analyze results → (d) repeat if new, unpredictable reads arise.`;
19
- const DEFAULT_QUOTA_REFRESH_CONFIG = {
20
- enabled: true,
21
- intervalMinutes: 360,
22
- startupDelaySeconds: 60,
23
- staggerMinSeconds: 2,
24
- staggerMaxSeconds: 5
25
- };
26
- const MIN_QUOTA_REFRESH_INTERVAL_MINUTES = 30;
27
- const gpt5CommentaryPrompt = `# Working with the user
28
-
29
- You interact with the user through a terminal. You have 2 ways of communicating with the users:
30
- - Share intermediary updates in \`commentary\` channel.
31
- - After you have completed all your work, send a message to the \`final\` channel.
32
-
33
- ## Intermediary updates
34
-
35
- - Intermediary updates go to the \`commentary\` channel.
36
- - User updates are short updates while you are working, they are NOT final answers.
37
- - You use 1-2 sentence user updates to communicate progress and new information to the user as you are doing work.
38
- - Do not begin responses with conversational interjections or meta commentary. Avoid openers such as acknowledgements (“Done —”, “Got it”, “Great question, ) or framing phrases.
39
- - You provide user updates frequently, every 20s.
40
- - Before exploring or doing substantial work, you start with a user update acknowledging the request and explaining your first step. You should include your understanding of the user request and explain what you will do. Avoid commenting on the request or using starters such as "Got it -" or "Understood -" etc.
41
- - When exploring, e.g. searching, reading files, you provide user updates as you go, every 20s, explaining what context you are gathering and what you've learned. Vary your sentence structure when providing these updates to avoid sounding repetitive - in particular, don't start each sentence the same way.
42
- - After you have sufficient context, and the work is substantial, you provide a longer plan (this is the only user update that may be longer than 2 sentences and can contain formatting).
43
- - Before performing file edits of any kind, you provide updates explaining what edits you are making.
44
- - As you are thinking, you very frequently provide updates even if not taking any actions, informing the user of your progress. You interrupt your thinking and send multiple updates in a row if thinking for more than 100 words.
45
- - Tone of your updates MUST match your personality.`;
46
- const defaultConfig = {
47
- auth: { apiKeys: [] },
48
- providers: {},
49
- extraPrompts: {
50
- "gpt-5-mini": gpt5ExplorationPrompt,
51
- "gpt-5.3-codex": gpt5CommentaryPrompt,
52
- "gpt-5.4-mini": gpt5CommentaryPrompt,
53
- "gpt-5.4": gpt5CommentaryPrompt,
54
- "gpt-5.5": gpt5CommentaryPrompt
55
- },
56
- smallModel: "gpt-5-mini",
57
- accountAffinity: true,
58
- responsesApiContextManagementModels: [],
59
- modelReasoningEfforts: {
60
- "gpt-5-mini": "low",
61
- "gpt-5.3-codex": "xhigh",
62
- "gpt-5.4-mini": "xhigh",
63
- "gpt-5.4": "xhigh",
64
- "gpt-5.5": "xhigh"
65
- },
66
- allowOriginalModelNamesForAliases: false,
67
- forceAgent: false,
68
- compactUseSmallModel: true,
69
- messageStartInputTokensFallback: false,
70
- modelRefreshIntervalHours: 24,
71
- sessionAffinityRetentionDays: 7,
72
- useMessagesApi: true,
73
- useResponsesApiWebSocket: true,
74
- useResponsesApiWebSearch: true,
75
- logLevel: "info",
76
- devMode: {
77
- enabled: false,
78
- capture4xx: false,
79
- capture5xx: false,
80
- captureOther: false
81
- },
82
- quotaRefresh: DEFAULT_QUOTA_REFRESH_CONFIG
83
- };
84
- let cachedConfig = null;
85
- function isPlainObject(value) {
86
- return typeof value === "object" && value !== null && !Array.isArray(value);
87
- }
88
- function normalizeAuthApiKeys(value) {
89
- if (!Array.isArray(value)) return [];
90
- return [...new Set(value.filter((item) => typeof item === "string").map((item) => item.trim()).filter((item) => item.length > 0))];
91
- }
92
- function normalizeNonNegativeNumber(value) {
93
- if (typeof value !== "number") return void 0;
94
- if (!Number.isFinite(value)) return void 0;
95
- if (value < 0) return void 0;
96
- return value;
97
- }
98
- function normalizeQuotaRefreshIntervalMinutes(value) {
99
- const minutes = normalizeNonNegativeNumber(value) ?? DEFAULT_QUOTA_REFRESH_CONFIG.intervalMinutes;
100
- if (minutes > 0 && minutes < MIN_QUOTA_REFRESH_INTERVAL_MINUTES) return MIN_QUOTA_REFRESH_INTERVAL_MINUTES;
101
- return minutes;
102
- }
103
- function normalizeQuotaRefreshConfig(value) {
104
- const raw = isPlainObject(value) ? value : {};
105
- const staggerMinSeconds = normalizeNonNegativeNumber(raw.staggerMinSeconds) ?? DEFAULT_QUOTA_REFRESH_CONFIG.staggerMinSeconds;
106
- const rawStaggerMaxSeconds = normalizeNonNegativeNumber(raw.staggerMaxSeconds) ?? DEFAULT_QUOTA_REFRESH_CONFIG.staggerMaxSeconds;
107
- return {
108
- enabled: typeof raw.enabled === "boolean" ? raw.enabled : DEFAULT_QUOTA_REFRESH_CONFIG.enabled,
109
- intervalMinutes: normalizeQuotaRefreshIntervalMinutes(raw.intervalMinutes),
110
- startupDelaySeconds: normalizeNonNegativeNumber(raw.startupDelaySeconds) ?? DEFAULT_QUOTA_REFRESH_CONFIG.startupDelaySeconds,
111
- staggerMinSeconds,
112
- staggerMaxSeconds: Math.max(staggerMinSeconds, rawStaggerMaxSeconds)
113
- };
114
- }
115
- const LOG_LEVELS = new Set([
116
- "error",
117
- "warn",
118
- "info",
119
- "debug"
120
- ]);
121
- function normalizeLogLevel(value) {
122
- if (typeof value !== "string") return void 0;
123
- return LOG_LEVELS.has(value) ? value : void 0;
124
- }
125
- function ensureConfigFile() {
126
- try {
127
- fs$1.accessSync(PATHS.CONFIG_PATH, fs$1.constants.R_OK);
128
- return;
129
- } catch {}
130
- try {
131
- fs$1.mkdirSync(PATHS.APP_DIR, { recursive: true });
132
- fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(defaultConfig, null, 2)}\n`, "utf8");
133
- try {
134
- fs$1.chmodSync(PATHS.CONFIG_PATH, 384);
135
- } catch {}
136
- } catch {}
137
- }
138
- function readConfigFromDisk() {
139
- ensureConfigFile();
140
- try {
141
- const raw = fs$1.readFileSync(PATHS.CONFIG_PATH, "utf8");
142
- if (!raw.trim()) {
143
- fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(defaultConfig, null, 2)}\n`, "utf8");
144
- return defaultConfig;
10
+ //#region src/lib/request-auth.ts
11
+ const LEGACY_API_KEY_ENV_VAR = "COPILOT_API_KEY";
12
+ let warnedLegacyEnvFallback = false;
13
+ let warnedLegacyConfigFallback = false;
14
+ function normalizeApiKeys(apiKeys) {
15
+ if (!Array.isArray(apiKeys)) {
16
+ if (apiKeys !== void 0) consola.warn("Invalid auth.apiKeys config. Expected an array of strings.");
17
+ return [];
18
+ }
19
+ const normalizedKeys = apiKeys.filter((key) => typeof key === "string").map((key) => key.trim()).filter((key) => key.length > 0);
20
+ if (normalizedKeys.length !== apiKeys.length) consola.warn("Invalid auth.apiKeys entries found. Only non-empty strings are allowed.");
21
+ return [...new Set(normalizedKeys)];
22
+ }
23
+ function getConfiguredApiKeys() {
24
+ const config = getConfig();
25
+ const configuredApiKeys = normalizeApiKeys(config.auth?.apiKeys);
26
+ if (configuredApiKeys.length > 0) return configuredApiKeys;
27
+ const envApiKey = process.env[LEGACY_API_KEY_ENV_VAR]?.trim();
28
+ if (envApiKey) {
29
+ if (!warnedLegacyEnvFallback) {
30
+ warnedLegacyEnvFallback = true;
31
+ consola.warn(`Using legacy ${LEGACY_API_KEY_ENV_VAR}. Please migrate to config.auth.apiKeys.`);
32
+ }
33
+ return [envApiKey];
34
+ }
35
+ const legacyConfigApiKey = config.apiKey?.trim();
36
+ if (legacyConfigApiKey) {
37
+ if (!warnedLegacyConfigFallback) {
38
+ warnedLegacyConfigFallback = true;
39
+ consola.warn("Using deprecated config.apiKey. Please migrate to config.auth.apiKeys.");
40
+ }
41
+ return [legacyConfigApiKey];
42
+ }
43
+ return configuredApiKeys;
44
+ }
45
+ function extractHeadersApiKey(headers) {
46
+ const xApiKey = headers.get("x-api-key")?.trim();
47
+ if (xApiKey) return xApiKey;
48
+ const authorization = headers.get("authorization");
49
+ if (!authorization) return null;
50
+ const [scheme, ...rest] = authorization.trim().split(/\s+/);
51
+ if (scheme.toLowerCase() !== "bearer") return null;
52
+ return rest.join(" ").trim() || null;
53
+ }
54
+ function isAuthorizedHeaders(headers, getApiKeys = getConfiguredApiKeys) {
55
+ const apiKeys = getApiKeys();
56
+ if (apiKeys.length === 0) return true;
57
+ const requestApiKey = extractHeadersApiKey(headers);
58
+ return requestApiKey ? apiKeys.some((apiKey) => timingSafeKeyCompare(requestApiKey, apiKey)) : false;
59
+ }
60
+ function createUnauthorizedRawResponse() {
61
+ return new Response(JSON.stringify({ error: {
62
+ message: "Unauthorized. Provide Authorization: Bearer <key> or x-api-key.",
63
+ type: "unauthorized"
64
+ } }), {
65
+ status: 401,
66
+ headers: {
67
+ "content-type": "application/json",
68
+ "WWW-Authenticate": "Bearer realm=\"copilot-api\""
145
69
  }
146
- return JSON.parse(raw);
147
- } catch (error) {
148
- consola.error("Failed to read config file, using default config", error);
149
- return defaultConfig;
150
- }
151
- }
152
- function mergeDefaultConfig(config) {
153
- const extraPrompts = config.extraPrompts ?? {};
154
- const defaultExtraPrompts = defaultConfig.extraPrompts ?? {};
155
- const modelReasoningEfforts = config.modelReasoningEfforts ?? {};
156
- const defaultModelReasoningEfforts = defaultConfig.modelReasoningEfforts ?? {};
157
- const hasForceAgent = typeof config.forceAgent === "boolean";
158
- const defaultForceAgent = defaultConfig.forceAgent ?? false;
159
- const missingExtraPromptModels = Object.keys(defaultExtraPrompts).filter((model) => !Object.hasOwn(extraPrompts, model));
160
- const missingReasoningEffortModels = Object.keys(defaultModelReasoningEfforts).filter((model) => !Object.hasOwn(modelReasoningEfforts, model));
161
- const hasExtraPromptChanges = missingExtraPromptModels.length > 0;
162
- const hasReasoningEffortChanges = missingReasoningEffortModels.length > 0;
163
- if (!hasExtraPromptChanges && !hasReasoningEffortChanges && !!hasForceAgent) return {
164
- mergedConfig: config,
165
- changed: false
166
- };
167
- return {
168
- mergedConfig: {
169
- ...config,
170
- extraPrompts: {
171
- ...defaultExtraPrompts,
172
- ...extraPrompts
173
- },
174
- modelReasoningEfforts: {
175
- ...defaultModelReasoningEfforts,
176
- ...modelReasoningEfforts
177
- },
178
- forceAgent: hasForceAgent ? config.forceAgent : defaultForceAgent
179
- },
180
- changed: true
181
- };
182
- }
183
- function mergeDefaultAuth(config) {
184
- const authConfig = isPlainObject(config.auth) ? config.auth : void 0;
185
- const nextAuth = { apiKeys: normalizeAuthApiKeys(Array.isArray(authConfig?.apiKeys) ? authConfig.apiKeys : void 0) };
186
- if (authConfig && JSON.stringify(authConfig) === JSON.stringify(nextAuth)) return {
187
- mergedConfig: config,
188
- changed: false
189
- };
190
- return {
191
- mergedConfig: {
192
- ...config,
193
- auth: nextAuth
194
- },
195
- changed: true
196
- };
197
- }
198
- function mergeDefaultAccountAffinity(config) {
199
- const raw = config;
200
- const hasOld = typeof raw.freeModelLoadBalancing === "boolean";
201
- const hasNew = typeof config.accountAffinity === "boolean";
202
- if (hasOld) {
203
- const next = { ...config };
204
- if (!hasNew) next.accountAffinity = raw.freeModelLoadBalancing;
205
- delete next.freeModelLoadBalancing;
206
- return {
207
- mergedConfig: next,
208
- changed: true
209
- };
210
- }
211
- if (hasNew) return {
212
- mergedConfig: config,
213
- changed: false
214
- };
215
- return {
216
- mergedConfig: {
217
- ...config,
218
- accountAffinity: defaultConfig.accountAffinity ?? true
219
- },
220
- changed: true
221
- };
222
- }
223
- function mergeDefaultModelRefreshInterval(config) {
224
- if (normalizeNonNegativeNumber(config.modelRefreshIntervalHours) !== void 0) return {
225
- mergedConfig: config,
226
- changed: false
227
- };
228
- return {
229
- mergedConfig: {
230
- ...config,
231
- modelRefreshIntervalHours: defaultConfig.modelRefreshIntervalHours ?? 24
232
- },
233
- changed: true
234
- };
235
- }
236
- function mergeDefaultSessionAffinityRetention(config) {
237
- if (normalizeNonNegativeNumber(config.sessionAffinityRetentionDays) !== void 0) return {
238
- mergedConfig: config,
239
- changed: false
240
- };
241
- return {
242
- mergedConfig: {
243
- ...config,
244
- sessionAffinityRetentionDays: defaultConfig.sessionAffinityRetentionDays ?? 7
245
- },
246
- changed: true
247
- };
248
- }
249
- function mergeDefaultLogLevel(config) {
250
- if (normalizeLogLevel(config.logLevel) !== void 0) return {
251
- mergedConfig: config,
252
- changed: false
253
- };
254
- return {
255
- mergedConfig: {
256
- ...config,
257
- logLevel: defaultConfig.logLevel ?? "info"
258
- },
259
- changed: true
260
- };
261
- }
262
- function mergeDefaultDevMode(config) {
263
- const current = config.devMode;
264
- if (current && typeof current.enabled === "boolean" && typeof current.capture4xx === "boolean" && typeof current.capture5xx === "boolean" && typeof current.captureOther === "boolean") return {
265
- mergedConfig: config,
266
- changed: false
267
- };
268
- return {
269
- mergedConfig: {
270
- ...config,
271
- devMode: {
272
- enabled: current?.enabled === true,
273
- capture4xx: current?.capture4xx === true,
274
- capture5xx: current?.capture5xx === true,
275
- captureOther: current?.captureOther === true
276
- }
277
- },
278
- changed: true
279
- };
280
- }
281
- function mergeDefaultQuotaRefresh(config) {
282
- const quotaRefresh = normalizeQuotaRefreshConfig(config.quotaRefresh);
283
- if (JSON.stringify(config.quotaRefresh) === JSON.stringify(quotaRefresh)) return {
284
- mergedConfig: config,
285
- changed: false
286
- };
287
- return {
288
- mergedConfig: {
289
- ...config,
290
- quotaRefresh
291
- },
292
- changed: true
293
- };
294
- }
295
- function applyConfigMerges(config, mergeFns) {
296
- return mergeFns.reduce((acc, mergeFn) => {
297
- const result = mergeFn(acc.mergedConfig);
298
- return {
299
- mergedConfig: result.mergedConfig,
300
- changed: acc.changed || result.changed
301
- };
302
- }, {
303
- mergedConfig: config,
304
- changed: false
305
70
  });
306
71
  }
307
- function mergeConfigWithDefaults() {
308
- const { mergedConfig, changed } = applyConfigMerges(readConfigFromDisk(), [
309
- mergeDefaultAuth,
310
- mergeDefaultConfig,
311
- mergeDefaultAccountAffinity,
312
- mergeDefaultModelRefreshInterval,
313
- mergeDefaultSessionAffinityRetention,
314
- mergeDefaultLogLevel,
315
- mergeDefaultDevMode,
316
- mergeDefaultQuotaRefresh
317
- ]);
318
- if (changed) try {
319
- fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(mergedConfig, null, 2)}\n`, "utf8");
320
- } catch (writeError) {
321
- consola.warn("Failed to write merged config defaults", writeError);
322
- }
323
- cachedConfig = mergedConfig;
324
- return mergedConfig;
325
- }
326
- function getConfig() {
327
- cachedConfig ??= mergeDefaultConfig(readConfigFromDisk()).mergedConfig;
328
- return cachedConfig;
329
- }
330
- function normalizeAliasKey(value) {
331
- const trimmed = value.trim().toLowerCase();
332
- return trimmed.length > 0 ? trimmed : null;
333
- }
334
- function normalizeAliasTarget(value) {
335
- const trimmed = value.trim();
336
- return trimmed.length > 0 ? trimmed : null;
337
- }
338
- function normalizeAliasSpec(value) {
339
- if (typeof value === "string") {
340
- const normalizedTarget = normalizeAliasTarget(value);
341
- return normalizedTarget ? { target: normalizedTarget } : null;
342
- }
343
- if (!value || typeof value !== "object") return null;
344
- const targetValue = value.target;
345
- if (typeof targetValue !== "string") return null;
346
- const normalizedTarget = normalizeAliasTarget(targetValue);
347
- if (!normalizedTarget) return null;
348
- const allowOriginalValue = value.allowOriginal;
349
- return {
350
- target: normalizedTarget,
351
- allowOriginal: typeof allowOriginalValue === "boolean" ? allowOriginalValue : void 0
352
- };
353
- }
354
- function getModelAliasesInfo() {
355
- const raw = getConfig().modelAliases ?? {};
356
- const normalized = {};
357
- for (const [alias, rawSpec] of Object.entries(raw)) {
358
- const normalizedAlias = normalizeAliasKey(alias);
359
- const normalizedSpec = normalizeAliasSpec(rawSpec);
360
- if (!normalizedAlias || !normalizedSpec) continue;
361
- if (!Object.hasOwn(normalized, normalizedAlias)) normalized[normalizedAlias] = normalizedSpec;
362
- }
363
- return normalized;
364
- }
365
- function getModelAliases() {
366
- const info = getModelAliasesInfo();
367
- const normalized = {};
368
- for (const [alias, spec] of Object.entries(info)) normalized[alias] = spec.target;
369
- return normalized;
370
- }
371
- function resolveModelAlias(modelId) {
372
- const normalized = normalizeAliasKey(modelId);
373
- if (!normalized) return modelId;
374
- return getModelAliases()[normalized] ?? modelId;
375
- }
376
- function isOriginalModelNameAllowedForAliases() {
377
- return getConfig().allowOriginalModelNamesForAliases ?? false;
378
- }
379
- function getAliasTargetSet() {
380
- const aliases = getModelAliasesInfo();
381
- const allowOriginalDefault = isOriginalModelNameAllowedForAliases();
382
- const targetAllowMap = /* @__PURE__ */ new Map();
383
- for (const { target, allowOriginal } of Object.values(aliases)) {
384
- const normalizedTarget = target.toLowerCase();
385
- const effectiveAllow = allowOriginal ?? allowOriginalDefault;
386
- const currentAllow = targetAllowMap.get(normalizedTarget);
387
- if (currentAllow === true) continue;
388
- if (effectiveAllow) targetAllowMap.set(normalizedTarget, true);
389
- else if (currentAllow === void 0) targetAllowMap.set(normalizedTarget, false);
390
- }
391
- const blockedTargets = /* @__PURE__ */ new Set();
392
- for (const [target, allowed] of targetAllowMap.entries()) if (!allowed) blockedTargets.add(target);
393
- return blockedTargets;
394
- }
395
- function isOriginalModelNameAllowedForTarget(modelId) {
396
- const normalized = normalizeAliasKey(modelId);
397
- if (!normalized) return true;
398
- return !getAliasTargetSet().has(normalized);
399
- }
400
- function getPreferredAliasForTarget(modelId) {
401
- return getAliasKeysForTarget(modelId, getModelAliases())[0] ?? null;
402
- }
403
- function getAliasKeysForTarget(target, aliases) {
404
- const normalizedTarget = target.toLowerCase();
405
- return Object.entries(aliases).filter(([, model]) => model.toLowerCase() === normalizedTarget).map(([alias]) => alias).sort();
406
- }
407
- function getAliasFallbackValue(record, modelId, aliases) {
408
- if (!record) return void 0;
409
- const aliasKeys = getAliasKeysForTarget(modelId, aliases);
410
- if (aliasKeys.length === 0) return void 0;
411
- const recordByAlias = /* @__PURE__ */ new Map();
412
- for (const [key, value] of Object.entries(record)) {
413
- const normalized = normalizeAliasKey(key);
414
- if (normalized) recordByAlias.set(normalized, value);
415
- }
416
- for (const alias of aliasKeys) {
417
- const value = recordByAlias.get(alias);
418
- if (value !== void 0) return value;
419
- }
420
- }
421
- function getExtraPromptForModel(model) {
422
- const config = getConfig();
423
- const direct = config.extraPrompts?.[model];
424
- if (direct !== void 0) return direct;
425
- const aliases = getModelAliases();
426
- return getAliasFallbackValue(config.extraPrompts, model, aliases) ?? "";
427
- }
428
- function getSmallModel() {
429
- const model = getConfig().smallModel ?? "gpt-5-mini";
430
- if (isOriginalModelNameAllowedForTarget(model)) return model;
431
- return getPreferredAliasForTarget(model) ?? model;
432
- }
433
- function getLogLevel() {
434
- return normalizeLogLevel(getConfig().logLevel) ?? defaultConfig.logLevel ?? "info";
435
- }
436
- function isAccountAffinityEnabled() {
437
- return getConfig().accountAffinity ?? true;
438
- }
439
- function getModelRefreshIntervalHours() {
440
- return normalizeNonNegativeNumber(getConfig().modelRefreshIntervalHours) ?? defaultConfig.modelRefreshIntervalHours ?? 24;
441
- }
442
- function getModelRefreshIntervalMs() {
443
- const hours = getModelRefreshIntervalHours();
444
- if (!Number.isFinite(hours) || hours <= 0) return 0;
445
- return hours * 60 * 60 * 1e3;
446
- }
447
- function getQuotaRefreshConfig() {
448
- return normalizeQuotaRefreshConfig(getConfig().quotaRefresh);
449
- }
450
- function getSessionAffinityRetentionDays() {
451
- return normalizeNonNegativeNumber(getConfig().sessionAffinityRetentionDays) ?? defaultConfig.sessionAffinityRetentionDays ?? 7;
452
- }
453
- function getSessionAffinityRetentionMs() {
454
- const days = getSessionAffinityRetentionDays();
455
- if (!Number.isFinite(days) || days <= 0) return 0;
456
- return days * 24 * 60 * 60 * 1e3;
457
- }
458
- function isMessageStartInputTokensFallbackEnabled() {
459
- return getConfig().messageStartInputTokensFallback ?? false;
72
+ function createUnauthorizedResponse(c) {
73
+ c.header("WWW-Authenticate", "Bearer realm=\"copilot-api\"");
74
+ return c.json({ error: {
75
+ message: "Unauthorized. Provide Authorization: Bearer <key> or x-api-key.",
76
+ type: "unauthorized"
77
+ } }, 401);
460
78
  }
461
- function shouldCompactUseSmallModel() {
462
- return getConfig().compactUseSmallModel ?? true;
79
+ function normalizePathname(pathname) {
80
+ if (pathname.length > 1 && pathname.endsWith("/")) return pathname.slice(0, -1);
81
+ return pathname;
463
82
  }
464
- function getResponsesApiContextManagementModels() {
465
- return getConfig().responsesApiContextManagementModels ?? defaultConfig.responsesApiContextManagementModels ?? [];
83
+ function hasPrefixBoundary(pathname, prefix) {
84
+ return pathname === prefix || pathname.startsWith(`${prefix}/`);
466
85
  }
467
- function isResponsesApiContextManagementModel(model) {
468
- return getResponsesApiContextManagementModels().includes(model);
469
- }
470
- function getReasoningEffortForModel(model) {
471
- const config = getConfig();
472
- const direct = config.modelReasoningEfforts?.[model];
473
- if (direct !== void 0) return direct;
474
- const aliases = getModelAliases();
475
- return getAliasFallbackValue(config.modelReasoningEfforts, model, aliases) ?? "high";
476
- }
477
- function isForceAgentEnabled() {
478
- return getConfig().forceAgent ?? false;
479
- }
480
- function normalizeProviderBaseUrl(url) {
481
- return url.trim().replace(/\/+$/u, "");
482
- }
483
- function getDefaultProviderAuthType(providerType) {
484
- return providerType === "openai-compatible" ? "authorization" : "x-api-key";
485
- }
486
- function resolveProviderAuthType(providerName, authType, providerType) {
487
- if (authType === void 0) return getDefaultProviderAuthType(providerType);
488
- if (authType === "x-api-key") return "x-api-key";
489
- if (authType === "authorization") return authType;
490
- consola.warn(`Provider ${providerName} has invalid authType '${authType}', falling back to ${getDefaultProviderAuthType(providerType)}`);
491
- return getDefaultProviderAuthType(providerType);
492
- }
493
- function getProviderConfig(name) {
494
- const providerName = name.trim();
495
- if (!providerName) return null;
496
- const provider = getConfig().providers?.[providerName];
497
- if (!provider) return null;
498
- if (provider.enabled === false) return null;
499
- const type = provider.type ?? "anthropic";
500
- if (type !== "anthropic" && type !== "openai-compatible") {
501
- consola.warn(`Provider ${providerName} is ignored because type '${type}' is not supported`);
502
- return null;
503
- }
504
- const baseUrl = normalizeProviderBaseUrl(provider.baseUrl ?? "");
505
- const apiKey = (provider.apiKey ?? "").trim();
506
- const authType = resolveProviderAuthType(providerName, provider.authType, type);
507
- if (!baseUrl || !apiKey) {
508
- consola.warn(`Provider ${providerName} is enabled but missing baseUrl or apiKey`);
509
- return null;
510
- }
511
- return {
512
- name: providerName,
513
- type,
514
- baseUrl,
515
- apiKey,
516
- authType,
517
- models: provider.models,
518
- adjustInputTokens: provider.adjustInputTokens
86
+ function timingSafeKeyCompare(a, b) {
87
+ try {
88
+ const aBuf = Buffer.from(a);
89
+ const bBuf = Buffer.from(b);
90
+ if (aBuf.length !== bBuf.length) return false;
91
+ return timingSafeEqual(aBuf, bBuf);
92
+ } catch {
93
+ return false;
94
+ }
95
+ }
96
+ function createAuthMiddleware(options = {}) {
97
+ const getApiKeys = options.getApiKeys ?? getConfiguredApiKeys;
98
+ const allowUnauthenticatedPaths = new Set((options.allowUnauthenticatedPaths ?? ["/"]).map((path) => normalizePathname(path)));
99
+ const allowUnauthenticatedPathPrefixes = (options.allowUnauthenticatedPathPrefixes ?? []).map((path) => normalizePathname(path));
100
+ const allowOptionsBypass = options.allowOptionsBypass ?? true;
101
+ return async (c, next) => {
102
+ if (allowOptionsBypass && c.req.method === "OPTIONS") return next();
103
+ const pathname = normalizePathname(new URL(c.req.url, "http://local").pathname);
104
+ if (allowUnauthenticatedPaths.has(pathname)) return next();
105
+ if (allowUnauthenticatedPathPrefixes.some((prefix) => hasPrefixBoundary(pathname, prefix))) return next();
106
+ if (!isAuthorizedHeaders(c.req.raw.headers, getApiKeys)) return createUnauthorizedResponse(c);
107
+ return next();
519
108
  };
520
109
  }
521
- function isMessagesApiEnabled() {
522
- return getConfig().useMessagesApi ?? true;
523
- }
524
- function isResponsesApiWebSocketEnabled() {
525
- return getConfig().useResponsesApiWebSocket ?? true;
526
- }
527
- function getAnthropicApiKey() {
528
- return getConfig().anthropicApiKey ?? process.env.ANTHROPIC_API_KEY ?? void 0;
529
- }
530
- function isResponsesApiWebSearchEnabled() {
531
- return getConfig().useResponsesApiWebSearch ?? true;
532
- }
533
- function getClaudeTokenMultiplier() {
534
- return getConfig().claudeTokenMultiplier ?? 1.15;
535
- }
536
110
  //#endregion
537
111
  //#region src/lib/account-affinity.ts
538
112
  const DEFAULT_MAX_ENTRIES$1 = 1e4;
@@ -1629,7 +1203,7 @@ var RequestHistoryStore = class {
1629
1203
  } catch (error) {
1630
1204
  consola.debug("Failed to cleanup request_log retention", error);
1631
1205
  }
1632
- import("./request-outbound-BJjWS_jF.js").then((m) => m.getRequestOutboundStore().cleanupOrphans()).catch(() => {});
1206
+ import("./request-outbound-BkEA8Wgb.js").then((m) => m.getRequestOutboundStore().cleanupOrphans()).catch(() => {});
1633
1207
  }
1634
1208
  meta() {
1635
1209
  return {
@@ -3257,59 +2831,24 @@ function updateQuotaRefreshSchedulerFromConfig() {
3257
2831
  quotaRefreshScheduler.updateConfig(getQuotaRefreshConfig());
3258
2832
  }
3259
2833
  //#endregion
3260
- //#region src/lib/proxy.ts
3261
- let proxyEnvDispatcher;
3262
- function getProxyEnvDispatcher() {
3263
- return proxyEnvDispatcher;
3264
- }
3265
- function initProxyFromEnv() {
2834
+ //#region src/services/copilot/responses-bridge-registry.ts
2835
+ const bridgeClosers = /* @__PURE__ */ new Map();
2836
+ const registerResponsesBridge = (bridgeId, closer) => {
2837
+ bridgeClosers.set(bridgeId, closer);
2838
+ };
2839
+ const unregisterResponsesBridge = (bridgeId) => {
2840
+ bridgeClosers.delete(bridgeId);
2841
+ };
2842
+ const closeResponsesBridge = (bridgeId) => {
2843
+ const closer = bridgeClosers.get(bridgeId);
2844
+ if (!closer) return;
3266
2845
  try {
3267
- const direct = new Agent();
3268
- const proxies = /* @__PURE__ */ new Map();
3269
- proxyEnvDispatcher = {
3270
- dispatch(options, handler) {
3271
- try {
3272
- const origin = typeof options.origin === "string" ? new URL(options.origin) : options.origin;
3273
- const raw = getProxyForUrl(origin.toString());
3274
- const proxyUrl = raw && raw.length > 0 ? raw : void 0;
3275
- if (!proxyUrl) {
3276
- consola.debug(`HTTP proxy bypass: ${origin.hostname}`);
3277
- return direct.dispatch(options, handler);
3278
- }
3279
- let agent = proxies.get(proxyUrl);
3280
- if (!agent) {
3281
- agent = new ProxyAgent(proxyUrl);
3282
- proxies.set(proxyUrl, agent);
3283
- }
3284
- let label = proxyUrl;
3285
- try {
3286
- const u = new URL(proxyUrl);
3287
- label = `${u.protocol}//${u.host}`;
3288
- } catch {}
3289
- consola.debug(`HTTP proxy route: ${origin.hostname} via ${label}`);
3290
- return agent.dispatch(options, handler);
3291
- } catch {
3292
- return direct.dispatch(options, handler);
3293
- }
3294
- },
3295
- close() {
3296
- return direct.close();
3297
- },
3298
- destroy() {
3299
- return direct.destroy();
3300
- }
3301
- };
3302
- if (typeof Bun !== "undefined") {
3303
- consola.debug("WebSocket proxy configured from environment (per-URL)");
3304
- return;
3305
- }
3306
- setGlobalDispatcher(proxyEnvDispatcher);
3307
- consola.debug("HTTP proxy configured from environment (per-URL)");
3308
- } catch (err) {
3309
- consola.debug("Proxy setup skipped:", err);
2846
+ closer();
2847
+ } catch (error) {
2848
+ consola.warn("Failed to close Responses websocket bridge:", error);
3310
2849
  }
3311
- }
2850
+ };
3312
2851
  //#endregion
3313
- export { getModelRefreshIntervalMs as A, isResponsesApiWebSocketEnabled as B, getAnthropicApiKey as C, getLogLevel as D, getExtraPromptForModel as E, isForceAgentEnabled as F, resolveModelAlias as H, isMessageStartInputTokensFallbackEnabled as I, isMessagesApiEnabled as L, getReasoningEffortForModel as M, getSmallModel as N, getModelAliases as O, isAccountAffinityEnabled as P, isResponsesApiContextManagementModel as R, getAliasTargetSet as S, getConfig as T, shouldCompactUseSmallModel as U, mergeConfigWithDefaults as V, toLocalDateString as _, stopQuotaRefreshScheduler as a, isDevModeEnabled as b, applySharedSessionAffinityRetention as c, getClientIpInfo as d, getRequestHistoryStore as f, normalizeMessagesUsage as g, normalizeEmbeddingsUsage as h, startQuotaRefreshSchedulerFromConfig as i, getProviderConfig as j, getModelAliasesInfo as k, extractResponsesUsageFromResult as l, normalizeChatCompletionsUsage as m, initProxyFromEnv as n, updateQuotaRefreshSchedulerFromConfig as o, getStatsStore as p, registerQuotaRefreshSchedulerShutdownCleanup as r, accountsManager as s, getProxyEnvDispatcher as t, extractResponsesUsageFromStreamEvent as u, copilotFetch as v, getClaudeTokenMultiplier as w, PROVIDER_TYPE_ANTHROPIC as x, flushPendingCapture as y, isResponsesApiWebSearchEnabled as z };
2852
+ export { createUnauthorizedRawResponse as C, createAuthMiddleware as S, normalizeMessagesUsage as _, startQuotaRefreshSchedulerFromConfig as a, flushPendingCapture as b, accountsManager as c, extractResponsesUsageFromStreamEvent as d, getClientIpInfo as f, normalizeEmbeddingsUsage as g, normalizeChatCompletionsUsage as h, registerQuotaRefreshSchedulerShutdownCleanup as i, applySharedSessionAffinityRetention as l, getStatsStore as m, registerResponsesBridge as n, stopQuotaRefreshScheduler as o, getRequestHistoryStore as p, unregisterResponsesBridge as r, updateQuotaRefreshSchedulerFromConfig as s, closeResponsesBridge as t, extractResponsesUsageFromResult as u, toLocalDateString as v, isAuthorizedHeaders as w, isDevModeEnabled as x, copilotFetch as y };
3314
2853
 
3315
- //# sourceMappingURL=proxy-_U-hgwIn.js.map
2854
+ //# sourceMappingURL=responses-bridge-registry-BJ5Sbh6-.js.map