@jeffreycao/copilot-api 1.3.4 → 1.3.7

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.
@@ -0,0 +1,33 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+
5
+ //#region src/lib/paths.ts
6
+ const AUTH_APP = process.env.COPILOT_API_OAUTH_APP?.trim() || "";
7
+ const ENTERPRISE_PREFIX = process.env.COPILOT_API_ENTERPRISE_URL ? "ent_" : "";
8
+ const DEFAULT_DIR = path.join(os.homedir(), ".local", "share", "copilot-api");
9
+ const APP_DIR = process.env.COPILOT_API_HOME || DEFAULT_DIR;
10
+ const GITHUB_TOKEN_PATH = path.join(APP_DIR, AUTH_APP, ENTERPRISE_PREFIX + "github_token");
11
+ const CONFIG_PATH = path.join(APP_DIR, "config.json");
12
+ const PATHS = {
13
+ APP_DIR,
14
+ GITHUB_TOKEN_PATH,
15
+ CONFIG_PATH
16
+ };
17
+ async function ensurePaths() {
18
+ await fs.mkdir(path.join(PATHS.APP_DIR, AUTH_APP), { recursive: true });
19
+ await ensureFile(PATHS.GITHUB_TOKEN_PATH);
20
+ await ensureFile(PATHS.CONFIG_PATH);
21
+ }
22
+ async function ensureFile(filePath) {
23
+ try {
24
+ await fs.access(filePath, fs.constants.W_OK);
25
+ } catch {
26
+ await fs.writeFile(filePath, "");
27
+ await fs.chmod(filePath, 384);
28
+ }
29
+ }
30
+
31
+ //#endregion
32
+ export { PATHS, ensurePaths };
33
+ //# sourceMappingURL=paths-Cla6y5eD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths-Cla6y5eD.js","names":[],"sources":["../src/lib/paths.ts"],"sourcesContent":["import fs from \"node:fs/promises\"\nimport os from \"node:os\"\nimport path from \"node:path\"\n\nconst AUTH_APP = process.env.COPILOT_API_OAUTH_APP?.trim() || \"\"\nconst ENTERPRISE_PREFIX = process.env.COPILOT_API_ENTERPRISE_URL ? \"ent_\" : \"\"\n\nconst DEFAULT_DIR = path.join(os.homedir(), \".local\", \"share\", \"copilot-api\")\nconst APP_DIR = process.env.COPILOT_API_HOME || DEFAULT_DIR\n\nconst GITHUB_TOKEN_PATH = path.join(\n APP_DIR,\n AUTH_APP,\n ENTERPRISE_PREFIX + \"github_token\",\n)\nconst CONFIG_PATH = path.join(APP_DIR, \"config.json\")\n\nexport const PATHS = {\n APP_DIR,\n GITHUB_TOKEN_PATH,\n CONFIG_PATH,\n}\n\nexport async function ensurePaths(): Promise<void> {\n await fs.mkdir(path.join(PATHS.APP_DIR, AUTH_APP), { recursive: true })\n await ensureFile(PATHS.GITHUB_TOKEN_PATH)\n await ensureFile(PATHS.CONFIG_PATH)\n}\n\nasync function ensureFile(filePath: string): Promise<void> {\n try {\n await fs.access(filePath, fs.constants.W_OK)\n } catch {\n await fs.writeFile(filePath, \"\")\n await fs.chmod(filePath, 0o600)\n }\n}\n"],"mappings":";;;;;AAIA,MAAM,WAAW,QAAQ,IAAI,uBAAuB,MAAM,IAAI;AAC9D,MAAM,oBAAoB,QAAQ,IAAI,6BAA6B,SAAS;AAE5E,MAAM,cAAc,KAAK,KAAK,GAAG,SAAS,EAAE,UAAU,SAAS,cAAc;AAC7E,MAAM,UAAU,QAAQ,IAAI,oBAAoB;AAEhD,MAAM,oBAAoB,KAAK,KAC7B,SACA,UACA,oBAAoB,eACrB;AACD,MAAM,cAAc,KAAK,KAAK,SAAS,cAAc;AAErD,MAAa,QAAQ;CACnB;CACA;CACA;CACD;AAED,eAAsB,cAA6B;AACjD,OAAM,GAAG,MAAM,KAAK,KAAK,MAAM,SAAS,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AACvE,OAAM,WAAW,MAAM,kBAAkB;AACzC,OAAM,WAAW,MAAM,YAAY;;AAGrC,eAAe,WAAW,UAAiC;AACzD,KAAI;AACF,QAAM,GAAG,OAAO,UAAU,GAAG,UAAU,KAAK;SACtC;AACN,QAAM,GAAG,UAAU,UAAU,GAAG;AAChC,QAAM,GAAG,MAAM,UAAU,IAAM"}
@@ -1,10 +1,14 @@
1
- import { HTTPError, PATHS, cacheModels, copilotBaseUrl, copilotHeaders, forwardError, generateRequestIdFromPayload, getConfig, getCopilotUsage, getExtraPromptForModel, getProviderConfig, getReasoningEffortForModel, getRootSessionId, getSmallModel, getUUID, isMessagesApiEnabled, isNullish, isResponsesApiContextManagementModel, prepareInteractionHeaders, shouldCompactUseSmallModel, sleep, state } from "./config-DqFPDXKy.js";
1
+ import { PATHS } from "./paths-Cla6y5eD.js";
2
+ import { HTTPError, cacheModels, copilotBaseUrl, copilotHeaders, forwardError, generateRequestIdFromPayload, getRootSessionId, getUUID, isNullish, prepareForCompact, prepareInteractionHeaders, sleep, state } from "./utils-DKqD66k9.js";
3
+ import { getCopilotUsage } from "./get-copilot-usage-CuVET98U.js";
4
+ import { getConfig, getExtraPromptForModel, getProviderConfig, getReasoningEffortForModel, getSmallModel, isMessagesApiEnabled, isResponsesApiContextManagementModel } from "./config-DYOE_-p1.js";
2
5
  import consola from "consola";
3
6
  import path from "node:path";
4
- import fs, { readFileSync } from "node:fs";
5
7
  import { Hono } from "hono";
6
8
  import { cors } from "hono/cors";
7
9
  import { logger } from "hono/logger";
10
+ import fs, { readFileSync } from "node:fs";
11
+ import { AsyncLocalStorage } from "node:async_hooks";
8
12
  import { streamSSE } from "hono/streaming";
9
13
  import util from "node:util";
10
14
  import { events } from "fetch-event-stream";
@@ -54,6 +58,40 @@ function createAuthMiddleware(options = {}) {
54
58
  };
55
59
  }
56
60
 
61
+ //#endregion
62
+ //#region src/lib/request-context.ts
63
+ const TRACE_ID_MAX_LENGTH = 64;
64
+ const TRACE_ID_PATTERN = /^\w[\w.-]*$/;
65
+ const asyncLocalStorage = new AsyncLocalStorage();
66
+ const requestContext = {
67
+ getStore: () => asyncLocalStorage.getStore(),
68
+ run: (context, callback) => asyncLocalStorage.run(context, callback)
69
+ };
70
+ function generateTraceId() {
71
+ const timestamp = Date.now().toString(36);
72
+ const random = Math.random().toString(36).slice(2, 8);
73
+ return `${timestamp}-${random}`;
74
+ }
75
+ function resolveTraceId(traceId) {
76
+ const candidate = traceId?.trim();
77
+ if (!candidate || candidate.length > TRACE_ID_MAX_LENGTH || !TRACE_ID_PATTERN.test(candidate)) return generateTraceId();
78
+ return candidate;
79
+ }
80
+
81
+ //#endregion
82
+ //#region src/lib/trace.ts
83
+ const traceIdMiddleware = async (c, next) => {
84
+ const traceId = resolveTraceId(c.req.header("x-trace-id"));
85
+ c.header("x-trace-id", traceId);
86
+ const context = {
87
+ traceId,
88
+ startTime: Date.now()
89
+ };
90
+ await requestContext.run(context, async () => {
91
+ await next();
92
+ });
93
+ };
94
+
57
95
  //#endregion
58
96
  //#region src/lib/approval.ts
59
97
  const awaitApproval = async () => {
@@ -162,12 +200,14 @@ const createHandlerLogger = (name) => {
162
200
  cleanupOldLogs();
163
201
  lastCleanup = Date.now();
164
202
  }
203
+ const traceId = requestContext.getStore()?.traceId;
165
204
  const date = logObj.date;
166
205
  const dateKey = date.toLocaleDateString("sv-SE");
167
206
  const timestamp = date.toLocaleString("sv-SE", { hour12: false });
168
207
  const filePath = path.join(LOG_DIR, `${sanitizedName}-${dateKey}.log`);
169
208
  const message = formatArgs(logObj.args);
170
- const line = `[${timestamp}] [${logObj.type}] [${logObj.tag || name}]${message ? ` ${message}` : ""}`;
209
+ const traceIdStr = traceId ? ` [${traceId}]` : "";
210
+ const line = `[${timestamp}] [${logObj.type}] [${logObj.tag || name}]${traceIdStr}${message ? ` ${message}` : ""}`;
171
211
  appendLine(filePath, line);
172
212
  } });
173
213
  return instance;
@@ -430,6 +470,7 @@ const createChatCompletions = async (payload, options) => {
430
470
  "x-initiator": isAgentCall ? "agent" : "user"
431
471
  };
432
472
  prepareInteractionHeaders(options.sessionId, Boolean(options.subagentMarker), headers);
473
+ prepareForCompact(headers, options.isCompact);
433
474
  const response = await fetch(`${copilotBaseUrl(state)}/chat/completions`, {
434
475
  method: "POST",
435
476
  headers,
@@ -836,13 +877,14 @@ async function handleCountTokens(c) {
836
877
 
837
878
  //#endregion
838
879
  //#region src/services/copilot/create-responses.ts
839
- const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId }) => {
880
+ const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, isCompact }) => {
840
881
  if (!state.copilotToken) throw new Error("Copilot token not found");
841
882
  const headers = {
842
883
  ...copilotHeaders(state, requestId, vision),
843
884
  "x-initiator": initiator
844
885
  };
845
886
  prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
887
+ prepareForCompact(headers, isCompact);
846
888
  payload.service_tier = null;
847
889
  const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
848
890
  method: "POST",
@@ -1813,6 +1855,7 @@ const createMessages = async (payload, anthropicBetaHeader, options) => {
1813
1855
  "x-initiator": isInitiateRequest ? "user" : "agent"
1814
1856
  };
1815
1857
  prepareInteractionHeaders(options.sessionId, Boolean(options.subagentMarker), headers);
1858
+ prepareForCompact(headers, options.isCompact);
1816
1859
  const anthropicBeta = buildAnthropicBetaHeader(anthropicBetaHeader, payload.thinking);
1817
1860
  if (anthropicBeta) headers["anthropic-beta"] = anthropicBeta;
1818
1861
  const response = await fetch(`${copilotBaseUrl(state)}/v1/messages`, {
@@ -2140,10 +2183,8 @@ async function handleCompletion(c) {
2140
2183
  logger$5.debug("Anthropic Beta header:", anthropicBeta);
2141
2184
  const noTools = !anthropicPayload.tools || anthropicPayload.tools.length === 0;
2142
2185
  if (anthropicBeta && noTools && !isCompact) anthropicPayload.model = getSmallModel();
2143
- if (isCompact) {
2144
- logger$5.debug("Is compact request:", isCompact);
2145
- if (shouldCompactUseSmallModel()) anthropicPayload.model = getSmallModel();
2146
- } else mergeToolResultForClaude(anthropicPayload);
2186
+ if (isCompact) logger$5.debug("Is compact request:", isCompact);
2187
+ else mergeToolResultForClaude(anthropicPayload);
2147
2188
  const requestId = generateRequestIdFromPayload(anthropicPayload, sessionId);
2148
2189
  logger$5.debug("Generated request ID:", requestId);
2149
2190
  if (state.manualApprove) await awaitApproval();
@@ -2154,30 +2195,34 @@ async function handleCompletion(c) {
2154
2195
  subagentMarker,
2155
2196
  selectedModel,
2156
2197
  requestId,
2157
- sessionId
2198
+ sessionId,
2199
+ isCompact
2158
2200
  });
2159
2201
  if (shouldUseResponsesApi(selectedModel)) return await handleWithResponsesApi(c, anthropicPayload, {
2160
2202
  subagentMarker,
2161
2203
  selectedModel,
2162
2204
  requestId,
2163
- sessionId
2205
+ sessionId,
2206
+ isCompact
2164
2207
  });
2165
2208
  return await handleWithChatCompletions(c, anthropicPayload, {
2166
2209
  subagentMarker,
2167
2210
  requestId,
2168
- sessionId
2211
+ sessionId,
2212
+ isCompact
2169
2213
  });
2170
2214
  }
2171
2215
  const RESPONSES_ENDPOINT$1 = "/responses";
2172
2216
  const MESSAGES_ENDPOINT = "/v1/messages";
2173
2217
  const handleWithChatCompletions = async (c, anthropicPayload, options) => {
2174
- const { subagentMarker, requestId, sessionId } = options;
2218
+ const { subagentMarker, requestId, sessionId, isCompact } = options;
2175
2219
  const openAIPayload = translateToOpenAI(anthropicPayload);
2176
2220
  logger$5.debug("Translated OpenAI request payload:", JSON.stringify(openAIPayload));
2177
2221
  const response = await createChatCompletions(openAIPayload, {
2178
2222
  subagentMarker,
2179
2223
  requestId,
2180
- sessionId
2224
+ sessionId,
2225
+ isCompact
2181
2226
  });
2182
2227
  if (isNonStreaming(response)) {
2183
2228
  logger$5.debug("Non-streaming response from Copilot:", JSON.stringify(response));
@@ -2211,7 +2256,7 @@ const handleWithChatCompletions = async (c, anthropicPayload, options) => {
2211
2256
  });
2212
2257
  };
2213
2258
  const handleWithResponsesApi = async (c, anthropicPayload, options) => {
2214
- const { subagentMarker, selectedModel, requestId, sessionId } = options;
2259
+ const { subagentMarker, selectedModel, requestId, sessionId, isCompact } = options;
2215
2260
  const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload);
2216
2261
  applyResponsesApiContextManagement(responsesPayload, selectedModel?.capabilities.limits.max_prompt_tokens);
2217
2262
  compactInputByLatestCompaction(responsesPayload);
@@ -2222,7 +2267,8 @@ const handleWithResponsesApi = async (c, anthropicPayload, options) => {
2222
2267
  initiator,
2223
2268
  subagentMarker,
2224
2269
  requestId,
2225
- sessionId
2270
+ sessionId,
2271
+ isCompact
2226
2272
  });
2227
2273
  if (responsesPayload.stream && isAsyncIterable$1(response)) {
2228
2274
  logger$5.debug("Streaming response from Copilot (Responses API)");
@@ -2269,7 +2315,7 @@ const handleWithResponsesApi = async (c, anthropicPayload, options) => {
2269
2315
  return c.json(anthropicResponse);
2270
2316
  };
2271
2317
  const handleWithMessagesApi = async (c, anthropicPayload, options) => {
2272
- const { anthropicBetaHeader, subagentMarker, selectedModel, requestId, sessionId } = options;
2318
+ const { anthropicBetaHeader, subagentMarker, selectedModel, requestId, sessionId, isCompact } = options;
2273
2319
  for (const msg of anthropicPayload.messages) if (msg.role === "assistant" && Array.isArray(msg.content)) msg.content = msg.content.filter((block) => {
2274
2320
  if (block.type !== "thinking") return true;
2275
2321
  return block.thinking && block.thinking !== "Thinking..." && block.signature && !block.signature.includes("@");
@@ -2284,7 +2330,8 @@ const handleWithMessagesApi = async (c, anthropicPayload, options) => {
2284
2330
  const response = await createMessages(anthropicPayload, anthropicBetaHeader, {
2285
2331
  subagentMarker,
2286
2332
  requestId,
2287
- sessionId
2333
+ sessionId,
2334
+ isCompact
2288
2335
  });
2289
2336
  if (isAsyncIterable$1(response)) {
2290
2337
  logger$5.debug("Streaming response from Copilot (Messages API)");
@@ -2541,14 +2588,37 @@ async function handleProviderMessages(c) {
2541
2588
  provider
2542
2589
  }));
2543
2590
  const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
2591
+ if (!upstreamResponse.ok) {
2592
+ logger$3.error("Failed to create responses", upstreamResponse);
2593
+ throw new HTTPError("Failed to create responses", upstreamResponse);
2594
+ }
2544
2595
  const contentType = upstreamResponse.headers.get("content-type") ?? "";
2545
2596
  if (Boolean(payload.stream) && contentType.includes("text/event-stream")) {
2546
2597
  logger$3.debug("provider.messages.streaming");
2547
2598
  return streamSSE(c, async (stream) => {
2548
- for await (const event of events(upstreamResponse)) {
2549
- const eventName = event.event;
2550
- const data = event.data ?? "";
2551
- logger$3.debug("provider.messages.raw_stream_event", data);
2599
+ for await (const chunk of events(upstreamResponse)) {
2600
+ logger$3.debug("provider.messages.raw_stream_event:", chunk.data);
2601
+ const eventName = chunk.event;
2602
+ if (eventName === "ping") {
2603
+ await stream.writeSSE({
2604
+ event: "ping",
2605
+ data: "{\"type\":\"ping\"}"
2606
+ });
2607
+ continue;
2608
+ }
2609
+ let data = chunk.data;
2610
+ if (!data) continue;
2611
+ try {
2612
+ const parsed = JSON.parse(data);
2613
+ if (parsed.type === "message_start") adjustInputTokens(providerConfig, parsed.message.usage);
2614
+ else if (parsed.type === "message_delta") adjustInputTokens(providerConfig, parsed.usage);
2615
+ data = JSON.stringify(parsed);
2616
+ } catch (error) {
2617
+ logger$3.error("provider.messages.streaming.adjust_tokens_error", {
2618
+ error,
2619
+ originalData: data
2620
+ });
2621
+ }
2552
2622
  await stream.writeSSE({
2553
2623
  event: eventName,
2554
2624
  data
@@ -2556,7 +2626,10 @@ async function handleProviderMessages(c) {
2556
2626
  }
2557
2627
  });
2558
2628
  }
2559
- return createProviderProxyResponse(upstreamResponse);
2629
+ const jsonBody = await upstreamResponse.json();
2630
+ adjustInputTokens(providerConfig, jsonBody.usage);
2631
+ logger$3.debug("provider.messages.no_stream result:", JSON.stringify(jsonBody));
2632
+ return c.json(jsonBody);
2560
2633
  } catch (error) {
2561
2634
  logger$3.error("provider.messages.error", {
2562
2635
  provider,
@@ -2565,6 +2638,11 @@ async function handleProviderMessages(c) {
2565
2638
  throw error;
2566
2639
  }
2567
2640
  }
2641
+ const adjustInputTokens = (providerConfig, usage) => {
2642
+ if (!providerConfig.adjustInputTokens || !usage) return;
2643
+ usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
2644
+ logger$3.debug("provider.messages.adjusted_usage:", JSON.stringify(usage));
2645
+ };
2568
2646
 
2569
2647
  //#endregion
2570
2648
  //#region src/routes/provider/messages/route.ts
@@ -2772,6 +2850,7 @@ usageRoute.get("/", async (c) => {
2772
2850
  //#endregion
2773
2851
  //#region src/server.ts
2774
2852
  const server = new Hono();
2853
+ server.use(traceIdMiddleware);
2775
2854
  server.use(logger());
2776
2855
  server.use(cors());
2777
2856
  server.use("*", createAuthMiddleware({ allowUnauthenticatedPaths: [
@@ -2801,4 +2880,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
2801
2880
 
2802
2881
  //#endregion
2803
2882
  export { server };
2804
- //# sourceMappingURL=server-C-LzaXzh.js.map
2883
+ //# sourceMappingURL=server-BBSm2bHY.js.map