@jeffreycao/copilot-api 2.4.0 → 2.4.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,7 +1,7 @@
1
- import { A as isResponsesApiWebSocketEnabled, C as getClaudeTokenMultiplier, D as isAlphaSearchCodexPriorityEnabled, E as getResponsesTransportConfig, N as PATHS, O as isMessagesApiEnabled, S as getClaudeAutoModel, T as getMessageApiWebSearchModel, _ as resolveMappedModel, b as getAlphaSearchModel, d as getModelResponsesApiCompactThreshold$1, f as getReasoningEffortForModel, g as isGpt56OrAbove, h as isContextManagementEnabledForResponses, i as listEnabledProviders, k as isResponsesApiWebSearchEnabled, l as getExtraPromptForModel, m as isContextManagementEnabledForMessages, n as getRawProviderConfig, o as resolveEffectiveProviderType, p as getSmallModel, s as resolveProviderAuthType, t as getProviderConfig, u as getModelMappings, v as setModelMappings, x as getAnthropicApiKey } from "./config-ByEK-9s6.js";
2
- import { B as fetchResponsesWithLifecycle, C as compactAutoContinuePromptStarts, D as compactTextOnlyGuard, E as compactSystemPromptStarts, F as forwardCodexResponses, G as createWebSocketUrl, H as encodePoolKeyPart, I as generateTraceId, J as forwardError, K as state, L as requestContext, N as CODEX_API_BASE_URL, O as createAuthMiddleware, P as buildCodexRequestHeaders, R as resolveTraceId$1, U as isTerminalResponsesStreamChunk, V as createResponsesSafeStream, W as createPooledWebSocketStream, b as prepareInteractionHeaders, c as getUUID, d as isResponsesStream, f as parseUserIdMetadata, g as copilotHeaders, h as copilotBaseUrl, k as getConfiguredAdminApiKeys, l as isAsyncIterable, o as generateRequestIdFromPayload, p as getCopilotUsage, q as HTTPError, r as setupCodexToken, s as getRootSessionId, u as isNullish, v as copilotWebSocketHeaders, w as compactMessageSections, x as prepareMessageProxyHeaders, y as prepareForCompact, z as createResponsesHttpEventStream } from "./token-BYTz7z9X.js";
1
+ import { A as isResponsesApiWebSocketEnabled, C as getClaudeTokenMultiplier, D as isAlphaSearchCodexPriorityEnabled, E as getResponsesTransportConfig, N as PATHS, O as isMessagesApiEnabled, S as getClaudeAutoModel, T as getMessageApiWebSearchModel, _ as resolveMappedModel, b as getAlphaSearchModel, d as getModelResponsesApiCompactThreshold$1, f as getReasoningEffortForModel, g as isGpt56OrAbove, h as isContextManagementEnabledForResponses, i as listEnabledProviders, k as isResponsesApiWebSearchEnabled, l as getExtraPromptForModel, m as isContextManagementEnabledForMessages, n as getRawProviderConfig, o as resolveEffectiveProviderType, p as getSmallModel, s as resolveProviderAuthType, t as getProviderConfig, u as getModelMappings, v as setModelMappings, x as getAnthropicApiKey } from "./config-DVWu2hKk.js";
2
+ import { B as fetchResponsesWithLifecycle, C as compactAutoContinuePromptStarts, D as compactTextOnlyGuard, E as compactSystemPromptStarts, F as forwardCodexResponses, G as createWebSocketUrl, H as encodePoolKeyPart, I as generateTraceId, J as forwardError, K as state, L as requestContext, N as CODEX_API_BASE_URL, O as createAuthMiddleware, P as buildCodexRequestHeaders, R as resolveTraceId$1, U as isTerminalResponsesStreamChunk, V as createResponsesSafeStream, W as createPooledWebSocketStream, b as prepareInteractionHeaders, c as getUUID, d as isResponsesStream, f as parseUserIdMetadata, g as copilotHeaders, h as copilotBaseUrl, k as getConfiguredAdminApiKeys, l as isAsyncIterable, o as generateRequestIdFromPayload, p as getCopilotUsage, q as HTTPError, r as setupCodexToken, s as getRootSessionId, u as isNullish, v as copilotWebSocketHeaders, w as compactMessageSections, x as prepareMessageProxyHeaders, y as prepareForCompact, z as createResponsesHttpEventStream } from "./token-D0GGk3E8.js";
3
3
  import { a as isDeferredToolName, c as parseMcpToolSearchSentinel, d as shouldEnableResponsesToolSearch, i as isBridgeToolSearchName, l as resolveBridgeToolSearchName, o as listDeferredToolNames, r as formatToolSearchBridgeArguments, s as normalizeToolSearchBridgeArguments, t as BRIDGE_TOOL_SEARCH_NAME, u as selectDeferredToolsByNames } from "./tool-search-Ds1vbmGG.js";
4
- import { i as toClientModelId, r as normalizeSdkModelId, t as findEndpointModel } from "./models-BWI-BuVr.js";
4
+ import { i as toClientModelId, r as normalizeSdkModelId, t as findEndpointModel } from "./models-DKNAPa_j.js";
5
5
  import consola from "consola";
6
6
  import { createHash } from "node:crypto";
7
7
  import fs, { readFileSync } from "node:fs";
@@ -351,10 +351,33 @@ const createFallbackModel = (modelId) => ({
351
351
  });
352
352
  //#endregion
353
353
  //#region src/lib/provider-resolver.ts
354
+ const AZURE_OPENAI_SCOPE = "https://cognitiveservices.azure.com/.default";
355
+ let cachedAzureAccessTokenProvider = null;
356
+ /**
357
+ * Lazily creates the Entra token provider on first use and reuses it
358
+ * afterwards. Importing @azure/identity eagerly adds ~0.5s to every process
359
+ * startup, and constructing a new DefaultAzureCredential per call would drop
360
+ * the token cache, so both the import and the credential are singletons.
361
+ */
362
+ async function getAzureAccessTokenProvider() {
363
+ cachedAzureAccessTokenProvider ??= (async () => {
364
+ try {
365
+ const { DefaultAzureCredential, getBearerTokenProvider } = await import("@azure/identity");
366
+ return getBearerTokenProvider(new DefaultAzureCredential(), AZURE_OPENAI_SCOPE);
367
+ } catch (error) {
368
+ cachedAzureAccessTokenProvider = null;
369
+ throw error;
370
+ }
371
+ })();
372
+ return cachedAzureAccessTokenProvider;
373
+ }
374
+ async function getDefaultAzureAccessToken() {
375
+ return (await getAzureAccessTokenProvider())();
376
+ }
354
377
  function isMissingCodexCredentialsError(error) {
355
378
  return error instanceof Error && error.message === "Codex credentials not found. Run `copilot-api auth login --provider codex` first.";
356
379
  }
357
- async function resolveProviderConfig(providerName) {
380
+ async function resolveProviderConfig(providerName, getAzureAccessToken = getDefaultAzureAccessToken) {
358
381
  const normalizedProviderName = providerName.trim();
359
382
  if (!normalizedProviderName) return null;
360
383
  if (normalizedProviderName === "codex") {
@@ -372,7 +395,16 @@ async function resolveProviderConfig(providerName) {
372
395
  apiKey: state.codexAccessToken ?? providerConfig.apiKey
373
396
  };
374
397
  }
375
- return getProviderConfig(normalizedProviderName);
398
+ const providerConfig = getProviderConfig(normalizedProviderName);
399
+ if (providerConfig?.authType !== "azure-entra") return providerConfig;
400
+ try {
401
+ return {
402
+ ...providerConfig,
403
+ apiKey: await getAzureAccessToken()
404
+ };
405
+ } catch (error) {
406
+ throw new Error(`Failed to acquire Azure Entra token for provider '${normalizedProviderName}': ${error instanceof Error ? error.message : String(error)}`, { cause: error });
407
+ }
376
408
  }
377
409
  /**
378
410
  * Returns the parsed "provider/model" alias only when that provider is
@@ -1186,53 +1218,68 @@ async function flushTokenUsageEvents() {
1186
1218
  }
1187
1219
  }
1188
1220
  function getPeriodRange(period, now = /* @__PURE__ */ new Date()) {
1221
+ const nowMs = now.getTime();
1222
+ if (period === "lifetime") return {
1223
+ endMs: nowMs + 1,
1224
+ startMs: nowMs + 1
1225
+ };
1189
1226
  const start = new Date(now);
1190
1227
  start.setHours(0, 0, 0, 0);
1191
1228
  switch (period) {
1192
- case "day": break;
1193
- case "week":
1194
- start.setDate(start.getDate() - 6);
1195
- break;
1196
- case "month":
1197
- start.setDate(start.getDate() - 29);
1229
+ case "today": break;
1230
+ case "this_week": {
1231
+ const daysSinceMonday = (start.getDay() + 6) % 7;
1232
+ start.setDate(start.getDate() - daysSinceMonday);
1198
1233
  break;
1199
- default: break;
1200
- }
1201
- const end = new Date(start);
1202
- switch (period) {
1203
- case "day":
1204
- end.setDate(end.getDate() + 1);
1234
+ }
1235
+ case "last_7_days":
1236
+ start.setDate(start.getDate() - 6);
1205
1237
  break;
1206
- case "week":
1207
- end.setDate(end.getDate() + 7);
1238
+ case "this_month":
1239
+ start.setDate(1);
1208
1240
  break;
1209
- case "month":
1210
- end.setDate(end.getDate() + 30);
1241
+ case "last_30_days":
1242
+ start.setDate(start.getDate() - 29);
1211
1243
  break;
1212
1244
  default: break;
1213
1245
  }
1214
1246
  return {
1215
- endMs: end.getTime(),
1247
+ endMs: nowMs + 1,
1216
1248
  startMs: start.getTime()
1217
1249
  };
1218
1250
  }
1251
+ function getPeriodRangeFromDb(db, period, now = /* @__PURE__ */ new Date()) {
1252
+ if (period !== "lifetime") return getPeriodRange(period, now);
1253
+ const nowMs = now.getTime();
1254
+ const earliestEventMs = db.prepare(`
1255
+ SELECT MIN(created_at_ms) AS start_ms
1256
+ FROM token_usage_events
1257
+ WHERE created_at_ms <= ?
1258
+ `).get(nowMs)?.start_ms;
1259
+ const startMs = typeof earliestEventMs === "number" && Number.isFinite(earliestEventMs) ? earliestEventMs : nowMs + 1;
1260
+ return {
1261
+ endMs: nowMs + 1,
1262
+ startMs
1263
+ };
1264
+ }
1219
1265
  function formatLocalDate(date) {
1220
1266
  return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
1221
1267
  }
1222
1268
  function createDailyIntervals(range) {
1223
1269
  const intervals = [];
1224
1270
  const cursor = new Date(range.startMs);
1271
+ cursor.setHours(0, 0, 0, 0);
1225
1272
  while (cursor.getTime() < range.endMs) {
1226
- const startMs = cursor.getTime();
1273
+ const startMs = Math.max(cursor.getTime(), range.startMs);
1227
1274
  const next = new Date(cursor);
1228
1275
  next.setDate(next.getDate() + 1);
1229
1276
  const endMs = Math.min(next.getTime(), range.endMs);
1230
- intervals.push({
1277
+ if (startMs < endMs) intervals.push({
1231
1278
  date: formatLocalDate(cursor),
1232
1279
  endMs,
1233
1280
  startMs
1234
1281
  });
1235
- cursor.setTime(endMs);
1282
+ cursor.setTime(next.getTime());
1236
1283
  }
1237
1284
  return intervals;
1238
1285
  }
@@ -1487,6 +1534,58 @@ function getModelSummaries(db, range) {
1487
1534
  return modelSummaryFromRow(row, costMap.get(model) ?? []);
1488
1535
  });
1489
1536
  }
1537
+ function getDailyModelSummaries(db, range) {
1538
+ const usageDate = "strftime('%Y-%m-%d', created_at_ms / 1000.0, 'unixepoch', 'localtime')";
1539
+ const rows = db.prepare(`
1540
+ SELECT
1541
+ ${usageDate} AS usage_date,
1542
+ model,
1543
+ COUNT(*) AS request_count,
1544
+ COALESCE(SUM(input_tokens), 0) AS input_tokens,
1545
+ COALESCE(SUM(output_tokens), 0) AS output_tokens,
1546
+ COALESCE(SUM(cache_read_input_tokens), 0) AS cache_read_input_tokens,
1547
+ COALESCE(SUM(cache_creation_input_tokens), 0) AS cache_creation_input_tokens,
1548
+ SUM(total_nano_aiu) AS total_nano_aiu,
1549
+ COALESCE(SUM(total_tokens), 0) AS total_tokens
1550
+ FROM token_usage_events
1551
+ WHERE created_at_ms >= ? AND created_at_ms < ?
1552
+ GROUP BY usage_date, model
1553
+ ORDER BY usage_date ASC, total_tokens DESC, model ASC
1554
+ `).all(range.startMs, range.endMs);
1555
+ const costRows = db.prepare(`
1556
+ SELECT
1557
+ ${usageDate} AS usage_date,
1558
+ model,
1559
+ cost_currency,
1560
+ COALESCE(SUM(total_cost_nanos), 0) AS total_cost_nanos
1561
+ FROM token_usage_events
1562
+ WHERE created_at_ms >= ?
1563
+ AND created_at_ms < ?
1564
+ AND cost_currency IS NOT NULL
1565
+ AND total_cost_nanos IS NOT NULL
1566
+ GROUP BY usage_date, model, cost_currency
1567
+ ORDER BY usage_date ASC, model ASC, cost_currency ASC
1568
+ `).all(range.startMs, range.endMs);
1569
+ const costsByDateAndModel = /* @__PURE__ */ new Map();
1570
+ for (const row of costRows) {
1571
+ const date = stringFromRow(row, "usage_date");
1572
+ const model = stringFromRow(row, "model") || "unknown";
1573
+ const cost = costFromRow(row);
1574
+ if (!date || !cost) continue;
1575
+ const key = `${date}\u0000${model}`;
1576
+ costsByDateAndModel.set(key, [...costsByDateAndModel.get(key) ?? [], cost]);
1577
+ }
1578
+ const summariesByDate = /* @__PURE__ */ new Map();
1579
+ for (const row of rows) {
1580
+ const date = stringFromRow(row, "usage_date");
1581
+ if (!date) continue;
1582
+ const model = stringFromRow(row, "model") || "unknown";
1583
+ const summaries = summariesByDate.get(date) ?? [];
1584
+ summaries.push(modelSummaryFromRow(row, costsByDateAndModel.get(`${date}\u0000${model}`) ?? []));
1585
+ summariesByDate.set(date, summaries);
1586
+ }
1587
+ return summariesByDate;
1588
+ }
1490
1589
  function createDailyBucket(interval, byModel) {
1491
1590
  const totals = createEmptyTotals();
1492
1591
  for (const model of byModel) addTotals(totals, model);
@@ -1501,8 +1600,8 @@ function createDailyBucket(interval, byModel) {
1501
1600
  async function getTokenUsageSummary(period) {
1502
1601
  if (!isTokenUsageStorageEnabled()) return createEmptySummary(period);
1503
1602
  await flushTokenUsageEvents();
1504
- const range = getPeriodRange(period);
1505
1603
  const db = await getDb();
1604
+ const range = getPeriodRangeFromDb(db, period);
1506
1605
  const totalsRow = getTotalsRow(db, range);
1507
1606
  return {
1508
1607
  byModel: getModelSummaries(db, range),
@@ -1514,12 +1613,13 @@ async function getTokenUsageSummary(period) {
1514
1613
  async function getTokenUsageDailySummary(period) {
1515
1614
  if (!isTokenUsageStorageEnabled()) return createEmptyDailySummary(period);
1516
1615
  await flushTokenUsageEvents();
1517
- const range = getPeriodRange(period);
1518
1616
  const db = await getDb();
1617
+ const range = getPeriodRangeFromDb(db, period);
1519
1618
  const intervals = createDailyIntervals(range);
1619
+ const dailySummaries = getDailyModelSummaries(db, range);
1520
1620
  return {
1521
1621
  byModel: getModelSummaries(db, range),
1522
- days: intervals.map((interval) => createDailyBucket(interval, getModelSummaries(db, interval))),
1622
+ days: intervals.map((interval) => createDailyBucket(interval, dailySummaries.get(interval.date) ?? [])),
1523
1623
  period,
1524
1624
  range: rangePayload(range),
1525
1625
  totals: totalsFromRow(getTotalsRow(db, range), getCostRows(db, range))
@@ -1528,11 +1628,11 @@ async function getTokenUsageDailySummary(period) {
1528
1628
  async function getTokenUsageEventsPage(input) {
1529
1629
  if (!isTokenUsageStorageEnabled()) return createEmptyEventsPage(input);
1530
1630
  await flushTokenUsageEvents();
1531
- const range = getPeriodRange(input.period);
1532
1631
  const page = Math.max(1, Math.floor(input.page));
1533
1632
  const pageSize = Math.min(100, Math.max(1, Math.floor(input.pageSize)));
1534
1633
  const offset = (page - 1) * pageSize;
1535
1634
  const db = await getDb();
1635
+ const range = getPeriodRangeFromDb(db, input.period);
1536
1636
  const totalRow = db.prepare(`
1537
1637
  SELECT COUNT(*) AS total
1538
1638
  FROM token_usage_events
@@ -2270,6 +2370,20 @@ function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
2270
2370
  }
2271
2371
  return headers;
2272
2372
  }
2373
+ const OPENCODE_GO_PROVIDER_NAME = "opencode-go";
2374
+ const OPENCODE_SESSION_HEADER = "x-opencode-session";
2375
+ const resolveOpencodeMessagesSession = (payload) => {
2376
+ const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim();
2377
+ if (sessionAffinity) return sessionAffinity;
2378
+ const userId = payload.metadata?.user_id;
2379
+ if (!userId?.trim()) return;
2380
+ const { sessionId } = parseUserIdMetadata(userId);
2381
+ return sessionId ?? userId;
2382
+ };
2383
+ const applyOpencodeSessionHeader = (providerConfig, headers, session) => {
2384
+ if (providerConfig.name !== OPENCODE_GO_PROVIDER_NAME || !session) return;
2385
+ headers[OPENCODE_SESSION_HEADER] = session;
2386
+ };
2273
2387
  function createProviderProxyResponse(upstreamResponse, body) {
2274
2388
  const headers = new Headers(upstreamResponse.headers);
2275
2389
  for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
@@ -2281,26 +2395,32 @@ function createProviderProxyResponse(upstreamResponse, body) {
2281
2395
  }
2282
2396
  async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
2283
2397
  consola.log(`<-- model: ${payload.model}`);
2398
+ const headers = buildProviderUpstreamHeaders(providerConfig, requestHeaders);
2399
+ applyOpencodeSessionHeader(providerConfig, headers, resolveOpencodeMessagesSession(payload));
2284
2400
  return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
2285
2401
  method: "POST",
2286
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
2402
+ headers,
2287
2403
  body: JSON.stringify(payload)
2288
2404
  });
2289
2405
  }
2290
2406
  async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
2291
2407
  consola.log(`<-- model: ${payload.model}`);
2408
+ const headers = buildProviderUpstreamHeaders(providerConfig, requestHeaders);
2409
+ applyOpencodeSessionHeader(providerConfig, headers, payload.prompt_cache_key?.trim() || void 0);
2292
2410
  return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
2293
2411
  method: "POST",
2294
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
2412
+ headers,
2295
2413
  body: JSON.stringify(payload)
2296
2414
  });
2297
2415
  }
2298
2416
  async function forwardProviderResponses(providerConfig, payload, requestHeaders, options = {}) {
2299
2417
  consola.log(`<-- model: ${payload.model}`);
2300
2418
  const transportConfig = getResponsesTransportConfig();
2419
+ const headers = buildProviderUpstreamHeaders(providerConfig, requestHeaders);
2420
+ applyOpencodeSessionHeader(providerConfig, headers, payload.prompt_cache_key?.trim() || void 0);
2301
2421
  return await fetchResponsesWithLifecycle(`${providerConfig.baseUrl}/v1/responses`, {
2302
2422
  method: "POST",
2303
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
2423
+ headers,
2304
2424
  body: JSON.stringify(payload)
2305
2425
  }, {
2306
2426
  headersTimeoutMs: transportConfig.headersTimeoutMs,
@@ -7444,6 +7564,10 @@ function getModels() {
7444
7564
  //#region src/routes/provider/messages/handler.ts
7445
7565
  const logger$10 = createHandlerLogger("provider-messages-handler");
7446
7566
  const providerMessagesHandlerDependencies = { resolveProviderConfig };
7567
+ const resolveOverrideProviderAuthType = (providerConfig, effectiveType) => {
7568
+ if (providerConfig.authType === "azure-entra" || providerConfig.authType === "oauth2") return providerConfig.authType;
7569
+ return resolveProviderAuthType(providerConfig.name, void 0, effectiveType);
7570
+ };
7447
7571
  async function handleProviderMessages(c) {
7448
7572
  const provider = c.req.param("provider");
7449
7573
  const payload = await c.req.json();
@@ -7510,7 +7634,7 @@ async function handleProviderMessagesForProvider(c, options) {
7510
7634
  const upstreamResponse = await forwardProviderMessages(effectiveType === providerConfig.type ? providerConfig : {
7511
7635
  ...providerConfig,
7512
7636
  type: effectiveType,
7513
- authType: resolveProviderAuthType(providerConfig.name, void 0, effectiveType)
7637
+ authType: resolveOverrideProviderAuthType(providerConfig, effectiveType)
7514
7638
  }, payload, c.req.raw.headers);
7515
7639
  if (!upstreamResponse.ok) {
7516
7640
  logger$10.error("Failed to create responses", upstreamResponse);
@@ -11813,13 +11937,22 @@ responsesRoutes.post("/", async (c) => {
11813
11937
  //#region src/routes/token-usage/route.ts
11814
11938
  const tokenUsageRoute = new Hono();
11815
11939
  const periods = new Set([
11816
- "day",
11817
- "week",
11818
- "month"
11940
+ "today",
11941
+ "this_week",
11942
+ "last_7_days",
11943
+ "this_month",
11944
+ "last_30_days",
11945
+ "lifetime"
11819
11946
  ]);
11820
11947
  const DEFAULT_EVENTS_PAGE_SIZE = 20;
11948
+ const legacyPeriods = {
11949
+ day: "today",
11950
+ week: "last_7_days",
11951
+ month: "last_30_days"
11952
+ };
11821
11953
  function parsePeriod(value) {
11822
- return periods.has(value) ? value : "day";
11954
+ if (periods.has(value)) return value;
11955
+ return (value ? legacyPeriods[value] : void 0) ?? "today";
11823
11956
  }
11824
11957
  function parsePositiveInt(value, fallback) {
11825
11958
  const parsed = Number.parseInt(value ?? "", 10);
@@ -11908,4 +12041,4 @@ server.route("/:provider/images", providerImageRoutes);
11908
12041
  //#endregion
11909
12042
  export { server };
11910
12043
 
11911
- //# sourceMappingURL=server-d83RMV1W.js.map
12044
+ //# sourceMappingURL=server-HBeQNO83.js.map