@ashsec/copilot-api 0.8.0 → 0.9.0

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 (3) hide show
  1. package/dist/main.js +159 -227
  2. package/dist/main.js.map +1 -1
  3. package/package.json +69 -69
package/dist/main.js CHANGED
@@ -5,7 +5,6 @@ import fs from "node:fs/promises";
5
5
  import os from "node:os";
6
6
  import path from "node:path";
7
7
  import { randomUUID } from "node:crypto";
8
- import { events } from "fetch-event-stream";
9
8
  import clipboard from "clipboardy";
10
9
  import { serve } from "srvx";
11
10
  import invariant from "tiny-invariant";
@@ -17,28 +16,102 @@ import process$1 from "node:process";
17
16
  import { Hono } from "hono";
18
17
  import { cors } from "hono/cors";
19
18
  import { streamSSE } from "hono/streaming";
19
+ import { events } from "fetch-event-stream";
20
20
  import util from "node:util";
21
21
 
22
22
  //#region package.json
23
- var version = "0.8.0";
23
+ var name = "@ashsec/copilot-api";
24
+ var version = "0.9.0";
25
+ var description = "Turn GitHub Copilot into OpenAI/Anthropic API compatible server. Usable with Claude Code!";
26
+ var keywords = [
27
+ "proxy",
28
+ "github-copilot",
29
+ "openai-compatible"
30
+ ];
31
+ var homepage = "https://github.com/ericc-ch/copilot-api";
32
+ var bugs = "https://github.com/ericc-ch/copilot-api/issues";
33
+ var repository = {
34
+ "type": "git",
35
+ "url": "git+https://github.com/ericc-ch/copilot-api.git"
36
+ };
37
+ var author = "Erick Christian <erickchristian48@gmail.com>";
38
+ var type = "module";
39
+ var bin = { "copilot-api": "./dist/main.js" };
40
+ var files = ["dist"];
41
+ var scripts = {
42
+ "build": "tsdown",
43
+ "dev": "bun run --watch ./src/main.ts",
44
+ "knip": "knip-bun",
45
+ "lint": "eslint --cache",
46
+ "lint:all": "eslint --cache .",
47
+ "prepack": "bun run build",
48
+ "prepare": "simple-git-hooks",
49
+ "release": "bumpp && bun publish --access public",
50
+ "start": "NODE_ENV=production bun run ./src/main.ts",
51
+ "typecheck": "tsc"
52
+ };
53
+ var simple_git_hooks = { "pre-commit": "bunx lint-staged" };
54
+ var lint_staged = { "*": "bun run lint --fix" };
55
+ var dependencies = {
56
+ "citty": "^0.1.6",
57
+ "clipboardy": "^5.0.0",
58
+ "consola": "^3.4.2",
59
+ "fetch-event-stream": "^0.1.5",
60
+ "gpt-tokenizer": "^3.0.1",
61
+ "hono": "^4.9.9",
62
+ "ms": "^2.1.3",
63
+ "proxy-from-env": "^1.1.0",
64
+ "srvx": "^0.8.9",
65
+ "tiny-invariant": "^1.3.3",
66
+ "undici": "^7.16.0",
67
+ "zod": "^4.1.11"
68
+ };
69
+ var devDependencies = {
70
+ "@echristian/eslint-config": "^0.0.54",
71
+ "@types/bun": "^1.2.23",
72
+ "@types/proxy-from-env": "^1.0.4",
73
+ "bumpp": "^10.2.3",
74
+ "eslint": "^9.37.0",
75
+ "knip": "^5.64.1",
76
+ "lint-staged": "^16.2.3",
77
+ "prettier-plugin-packagejson": "^2.5.19",
78
+ "simple-git-hooks": "^2.13.1",
79
+ "tsdown": "^0.15.6",
80
+ "typescript": "^5.9.3"
81
+ };
82
+ var package_default = {
83
+ name,
84
+ version,
85
+ description,
86
+ keywords,
87
+ homepage,
88
+ bugs,
89
+ repository,
90
+ author,
91
+ type,
92
+ bin,
93
+ files,
94
+ scripts,
95
+ "simple-git-hooks": simple_git_hooks,
96
+ "lint-staged": lint_staged,
97
+ dependencies,
98
+ devDependencies
99
+ };
24
100
 
25
101
  //#endregion
26
102
  //#region src/lib/paths.ts
27
103
  const APP_DIR = path.join(os.homedir(), ".local", "share", "copilot-api");
28
104
  const GITHUB_TOKEN_PATH = path.join(APP_DIR, "github_token");
29
- const AZURE_OPENAI_CONFIG_PATH = path.join(APP_DIR, "azure_openai_config");
30
105
  const REPLACEMENTS_CONFIG_PATH = path.join(APP_DIR, "replacements.json");
31
106
  const PATHS = {
32
107
  APP_DIR,
33
108
  CONFIG_PATH: path.join(APP_DIR, "config.json"),
34
109
  GITHUB_TOKEN_PATH,
35
- AZURE_OPENAI_CONFIG_PATH,
36
110
  REPLACEMENTS_CONFIG_PATH
37
111
  };
38
112
  async function ensurePaths() {
39
113
  await fs.mkdir(PATHS.APP_DIR, { recursive: true });
40
114
  await ensureFile(PATHS.GITHUB_TOKEN_PATH);
41
- await ensureFile(PATHS.AZURE_OPENAI_CONFIG_PATH);
42
115
  }
43
116
  async function ensureFile(filePath) {
44
117
  try {
@@ -180,56 +253,6 @@ async function getGitHubUser() {
180
253
  return await response.json();
181
254
  }
182
255
 
183
- //#endregion
184
- //#region src/services/azure-openai/config.ts
185
- const AZURE_OPENAI_MODEL_PREFIX = "azure_openai_";
186
- async function loadAzureOpenAIConfig() {
187
- try {
188
- const content = await fs.readFile(PATHS.AZURE_OPENAI_CONFIG_PATH, "utf8");
189
- if (!content.trim()) return null;
190
- const decoded = Buffer.from(content.trim(), "base64").toString("utf8");
191
- const config$1 = JSON.parse(decoded);
192
- if (!config$1.endpoint || !config$1.apiKey) return null;
193
- return config$1;
194
- } catch {
195
- return null;
196
- }
197
- }
198
- async function saveAzureOpenAIConfig(config$1) {
199
- const encoded = Buffer.from(JSON.stringify(config$1)).toString("base64");
200
- await fs.writeFile(PATHS.AZURE_OPENAI_CONFIG_PATH, encoded, "utf8");
201
- await fs.chmod(PATHS.AZURE_OPENAI_CONFIG_PATH, 384);
202
- consola.success("Azure OpenAI configuration saved");
203
- }
204
- async function promptAzureOpenAISetup() {
205
- if (!await consola.prompt("Would you like to add a custom Azure OpenAI endpoint?", {
206
- type: "confirm",
207
- initial: false
208
- })) return null;
209
- const endpoint = await consola.prompt("Enter your Azure OpenAI endpoint URL (e.g., https://your-resource.openai.azure.com):", { type: "text" });
210
- if (!endpoint || typeof endpoint !== "string" || !endpoint.trim()) {
211
- consola.warn("No endpoint provided, skipping Azure OpenAI setup");
212
- return null;
213
- }
214
- const apiKey = await consola.prompt("Enter your Azure OpenAI API key:", { type: "text" });
215
- if (!apiKey || typeof apiKey !== "string" || !apiKey.trim()) {
216
- consola.warn("No API key provided, skipping Azure OpenAI setup");
217
- return null;
218
- }
219
- const config$1 = {
220
- endpoint: endpoint.trim().replace(/\/$/, ""),
221
- apiKey: apiKey.trim()
222
- };
223
- await saveAzureOpenAIConfig(config$1);
224
- return config$1;
225
- }
226
- function isAzureOpenAIModel(modelId) {
227
- return modelId.startsWith(AZURE_OPENAI_MODEL_PREFIX);
228
- }
229
- function getAzureDeploymentName(modelId) {
230
- return modelId.slice(13);
231
- }
232
-
233
256
  //#endregion
234
257
  //#region src/lib/retry-fetch.ts
235
258
  const RETRY_DELAYS_MS = [
@@ -299,62 +322,6 @@ async function fetchWithRetry(input, init) {
299
322
  throw lastError;
300
323
  }
301
324
 
302
- //#endregion
303
- //#region src/services/azure-openai/create-chat-completions.ts
304
- const AZURE_API_VERSION = "2024-10-21";
305
- async function createAzureOpenAIChatCompletions(config$1, payload) {
306
- const deploymentName = getAzureDeploymentName(payload.model);
307
- const { max_tokens,...restPayload } = payload;
308
- const azurePayload = {
309
- ...restPayload,
310
- model: deploymentName,
311
- ...max_tokens != null && { max_completion_tokens: max_tokens }
312
- };
313
- const response = await fetchWithRetry(`${config$1.endpoint}/openai/deployments/${deploymentName}/chat/completions?api-version=${AZURE_API_VERSION}`, {
314
- method: "POST",
315
- headers: {
316
- "api-key": config$1.apiKey,
317
- "Content-Type": "application/json"
318
- },
319
- body: JSON.stringify(azurePayload)
320
- });
321
- if (!response.ok) {
322
- consola.error("Failed to create Azure OpenAI chat completions:", response);
323
- throw new HTTPError("Failed to create Azure OpenAI chat completions", response, payload);
324
- }
325
- if (payload.stream) return events(response);
326
- return await response.json();
327
- }
328
-
329
- //#endregion
330
- //#region src/services/azure-openai/get-models.ts
331
- const AZURE_DEPLOYMENTS_API_VERSION = "2022-12-01";
332
- async function getAzureOpenAIDeployments(config$1) {
333
- try {
334
- const response = await fetchWithRetry(`${config$1.endpoint}/openai/deployments?api-version=${AZURE_DEPLOYMENTS_API_VERSION}`, { headers: {
335
- "api-key": config$1.apiKey,
336
- "Content-Type": "application/json"
337
- } });
338
- if (!response.ok) {
339
- const errorText = await response.text().catch(() => "");
340
- consola.error(`Failed to fetch Azure OpenAI deployments: ${response.status}`, errorText);
341
- throw new HTTPError("Failed to fetch Azure OpenAI deployments", response);
342
- }
343
- return (await response.json()).data.filter((deployment) => deployment.status === "succeeded").map((deployment) => ({
344
- id: `${AZURE_OPENAI_MODEL_PREFIX}${deployment.id}`,
345
- deploymentName: deployment.id,
346
- model: deployment.model,
347
- created: deployment.created_at,
348
- object: "deployment",
349
- owned_by: deployment.owner || "azure-openai"
350
- }));
351
- } catch (error) {
352
- if (error instanceof HTTPError) throw error;
353
- consola.error("Failed to fetch Azure OpenAI deployments:", error);
354
- return [];
355
- }
356
- }
357
-
358
325
  //#endregion
359
326
  //#region src/services/copilot/get-models.ts
360
327
  const getModels = async () => {
@@ -418,24 +385,6 @@ const cacheVSCodeVersion = async () => {
418
385
  state.vsCodeVersion = response;
419
386
  consola.info(`Using VSCode version: ${response}`);
420
387
  };
421
- async function setupAzureOpenAI() {
422
- let config$1 = await loadAzureOpenAIConfig();
423
- if (!config$1) config$1 = await promptAzureOpenAISetup();
424
- if (!config$1) {
425
- consola.info("Azure OpenAI not configured");
426
- return;
427
- }
428
- state.azureOpenAIConfig = config$1;
429
- consola.info("Azure OpenAI configuration loaded");
430
- try {
431
- const deployments = await getAzureOpenAIDeployments(config$1);
432
- state.azureOpenAIDeployments = deployments;
433
- if (deployments.length > 0) consola.info(`Loaded ${deployments.length} Azure OpenAI deployment(s):\n${deployments.map((d) => `- ${d.id} (${d.model})`).join("\n")}`);
434
- else consola.warn("No Azure OpenAI deployments found");
435
- } catch (error) {
436
- consola.warn("Failed to fetch Azure OpenAI deployments:", error);
437
- }
438
- }
439
388
 
440
389
  //#endregion
441
390
  //#region src/services/github/poll-access-token.ts
@@ -583,13 +532,13 @@ const checkUsage = defineCommand({
583
532
  const premiumUsed = premiumTotal - premium.remaining;
584
533
  const premiumPercentUsed = premiumTotal > 0 ? premiumUsed / premiumTotal * 100 : 0;
585
534
  const premiumPercentRemaining = premium.percent_remaining;
586
- function summarizeQuota(name, snap) {
587
- if (!snap) return `${name}: N/A`;
535
+ function summarizeQuota(name$1, snap) {
536
+ if (!snap) return `${name$1}: N/A`;
588
537
  const total = snap.entitlement;
589
538
  const used = total - snap.remaining;
590
539
  const percentUsed = total > 0 ? used / total * 100 : 0;
591
540
  const percentRemaining = snap.percent_remaining;
592
- return `${name}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`;
541
+ return `${name$1}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`;
593
542
  }
594
543
  const premiumLine = `Premium: ${premiumUsed}/${premiumTotal} used (${premiumPercentUsed.toFixed(1)}% used, ${premiumPercentRemaining.toFixed(1)}% remaining)`;
595
544
  const chatLine = summarizeQuota("Chat", usage.quota_snapshots.chat);
@@ -665,11 +614,11 @@ async function getUserReplacements() {
665
614
  * Add a new user replacement rule
666
615
  */
667
616
  async function addReplacement(pattern, replacement, options) {
668
- const { isRegex = false, name } = options ?? {};
617
+ const { isRegex = false, name: name$1 } = options ?? {};
669
618
  await ensureLoaded();
670
619
  const rule = {
671
620
  id: `user-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
672
- name,
621
+ name: name$1,
673
622
  pattern,
674
623
  replacement,
675
624
  isRegex,
@@ -820,11 +769,11 @@ async function applyReplacementsToPayload(payload) {
820
769
  //#region src/config.ts
821
770
  function formatRule(rule, index) {
822
771
  const status = rule.enabled ? "✓" : "✗";
823
- const type = rule.isRegex ? "regex" : "string";
772
+ const type$1 = rule.isRegex ? "regex" : "string";
824
773
  const system = rule.isSystem ? " [system]" : "";
825
- const name = rule.name ? ` "${rule.name}"` : "";
774
+ const name$1 = rule.name ? ` "${rule.name}"` : "";
826
775
  const replacement = rule.replacement || "(empty)";
827
- return `${index + 1}. [${status}] (${type})${system}${name} "${rule.pattern}" → "${replacement}"`;
776
+ return `${index + 1}. [${status}] (${type$1})${system}${name$1} "${rule.pattern}" → "${replacement}"`;
828
777
  }
829
778
  async function listReplacements() {
830
779
  const all = await getAllReplacements();
@@ -837,11 +786,11 @@ async function listReplacements() {
837
786
  console.log();
838
787
  }
839
788
  async function addNewReplacement() {
840
- const name = await consola.prompt("Name (optional, short description):", {
789
+ const name$1 = await consola.prompt("Name (optional, short description):", {
841
790
  type: "text",
842
791
  default: ""
843
792
  });
844
- if (typeof name === "symbol") {
793
+ if (typeof name$1 === "symbol") {
845
794
  consola.info("Cancelled.");
846
795
  return;
847
796
  }
@@ -878,7 +827,7 @@ async function addNewReplacement() {
878
827
  consola.info("Cancelled.");
879
828
  return;
880
829
  }
881
- const rule = await addReplacement(pattern, replacement, matchType === "regex", name || void 0);
830
+ const rule = await addReplacement(pattern, replacement, matchType === "regex", name$1 || void 0);
882
831
  consola.success(`Added rule: ${rule.name || rule.id}`);
883
832
  }
884
833
  async function editExistingReplacement() {
@@ -906,11 +855,11 @@ async function editExistingReplacement() {
906
855
  }
907
856
  consola.info(`\nEditing rule: ${rule.name || rule.id}`);
908
857
  consola.info("Press Enter to keep current value.\n");
909
- const name = await consola.prompt("Name:", {
858
+ const name$1 = await consola.prompt("Name:", {
910
859
  type: "text",
911
860
  default: rule.name || ""
912
861
  });
913
- if (typeof name === "symbol") {
862
+ if (typeof name$1 === "symbol") {
914
863
  consola.info("Cancelled.");
915
864
  return;
916
865
  }
@@ -952,7 +901,7 @@ async function editExistingReplacement() {
952
901
  return;
953
902
  }
954
903
  const updated = await updateReplacement(selected, {
955
- name: name || void 0,
904
+ name: name$1 || void 0,
956
905
  pattern,
957
906
  replacement,
958
907
  isRegex: matchType === "regex"
@@ -1269,7 +1218,8 @@ function mergeDefaultExtraPrompts(config$1) {
1269
1218
  };
1270
1219
  }
1271
1220
  function mergeConfigWithDefaults() {
1272
- const { mergedConfig, changed } = mergeDefaultExtraPrompts(readConfigFromDisk());
1221
+ const config$1 = readConfigFromDisk();
1222
+ const { mergedConfig, changed } = mergeDefaultExtraPrompts(config$1);
1273
1223
  if (changed) try {
1274
1224
  fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(mergedConfig, null, 2)}\n`, "utf8");
1275
1225
  } catch (writeError) {
@@ -1347,7 +1297,8 @@ function getShell() {
1347
1297
  const { platform, ppid, env } = process$1;
1348
1298
  if (platform === "win32") {
1349
1299
  try {
1350
- if (execSync(`wmic process get ParentProcessId,Name | findstr "${ppid}"`, { stdio: "pipe" }).toString().toLowerCase().includes("powershell.exe")) return "powershell";
1300
+ const command = `wmic process get ParentProcessId,Name | findstr "${ppid}"`;
1301
+ if (execSync(command, { stdio: "pipe" }).toString().toLowerCase().includes("powershell.exe")) return "powershell";
1351
1302
  } catch {
1352
1303
  return "cmd";
1353
1304
  }
@@ -1405,7 +1356,8 @@ function normalizeApiKeys(apiKeys) {
1405
1356
  return [...new Set(normalizedKeys)];
1406
1357
  }
1407
1358
  function getConfiguredApiKeys() {
1408
- return normalizeApiKeys(getConfig().auth?.apiKeys);
1359
+ const config$1 = getConfig();
1360
+ return normalizeApiKeys(config$1.auth?.apiKeys);
1409
1361
  }
1410
1362
  function extractRequestApiKey(c) {
1411
1363
  const xApiKey = c.req.header("x-api-key")?.trim();
@@ -1498,7 +1450,8 @@ async function logRawRequest(c) {
1498
1450
  if (method !== "GET" && method !== "HEAD") try {
1499
1451
  const body = await c.req.raw.clone().text();
1500
1452
  if (body) try {
1501
- const sanitized = sanitizeRequestBody(JSON.parse(body));
1453
+ const parsed = JSON.parse(body);
1454
+ const sanitized = sanitizeRequestBody(parsed);
1502
1455
  lines.push(`${colors.dim}Body (sanitized):${colors.reset}`, ` ${JSON.stringify(sanitized, null, 2).split("\n").join("\n ")}`);
1503
1456
  } catch {
1504
1457
  lines.push(`${colors.dim}Body:${colors.reset} [${body.length} bytes]`);
@@ -1539,7 +1492,7 @@ async function requestLogger(c, next) {
1539
1492
  const durationStr = `${colors.cyan}${duration}s${colors.reset}`;
1540
1493
  lines.push(`${colors.bold}${method}${colors.reset} ${path$1} ${statusBadge} ${durationStr}`);
1541
1494
  if (ctx?.provider && ctx.model) {
1542
- const providerColor = ctx.provider === "Azure OpenAI" ? colors.blue : colors.magenta;
1495
+ const providerColor = colors.magenta;
1543
1496
  lines.push(` ${colors.gray}Provider:${colors.reset} ${providerColor}${ctx.provider}${colors.reset} ${colors.gray}->${colors.reset} ${colors.white}${ctx.model}${colors.reset}`);
1544
1497
  }
1545
1498
  if (ctx?.inputTokens !== void 0 || ctx?.outputTokens !== void 0) {
@@ -1778,7 +1731,8 @@ const numTokensForTools = (tools, encoder, constants) => {
1778
1731
  * Calculate the token count of messages, supporting multiple GPT encoders
1779
1732
  */
1780
1733
  const getTokenCount = async (payload, model) => {
1781
- const encoder = await getEncodeChatFunction(getTokenizerFromModel(model));
1734
+ const tokenizer = getTokenizerFromModel(model);
1735
+ const encoder = await getEncodeChatFunction(tokenizer);
1782
1736
  const simplifiedMessages = payload.messages;
1783
1737
  const inputMessages = simplifiedMessages.filter((msg) => msg.role !== "assistant");
1784
1738
  const outputMessages = simplifiedMessages.filter((msg) => msg.role === "assistant");
@@ -1823,50 +1777,23 @@ const createChatCompletions = async (payload, options) => {
1823
1777
  //#region src/routes/chat-completions/handler.ts
1824
1778
  async function handleCompletion$1(c) {
1825
1779
  await checkRateLimit(state);
1826
- let payload = await applyReplacementsToPayload(await c.req.json());
1780
+ const rawPayload = await c.req.json();
1781
+ let payload = await applyReplacementsToPayload(rawPayload);
1827
1782
  payload = {
1828
1783
  ...payload,
1829
1784
  model: normalizeModelName(payload.model)
1830
1785
  };
1831
1786
  consola.debug("Request payload:", JSON.stringify(payload).slice(-400));
1832
- if (isAzureOpenAIModel(payload.model)) {
1833
- if (!state.azureOpenAIConfig) return c.json({ error: "Azure OpenAI not configured" }, 500);
1834
- setRequestContext(c, {
1835
- provider: "Azure OpenAI",
1836
- model: payload.model
1837
- });
1838
- if (state.manualApprove) await awaitApproval();
1839
- const response$1 = await createAzureOpenAIChatCompletions(state.azureOpenAIConfig, payload);
1840
- if (isNonStreaming$1(response$1)) {
1841
- consola.debug("Non-streaming response:", JSON.stringify(response$1));
1842
- if (response$1.usage) setRequestContext(c, {
1843
- inputTokens: response$1.usage.prompt_tokens,
1844
- outputTokens: response$1.usage.completion_tokens
1845
- });
1846
- return c.json(response$1);
1847
- }
1848
- consola.debug("Streaming response");
1849
- return streamSSE(c, async (stream) => {
1850
- for await (const chunk of response$1) {
1851
- consola.debug("Streaming chunk:", JSON.stringify(chunk));
1852
- if (chunk.data && chunk.data !== "[DONE]") {
1853
- const parsed = JSON.parse(chunk.data);
1854
- if (parsed.usage) setRequestContext(c, {
1855
- inputTokens: parsed.usage.prompt_tokens,
1856
- outputTokens: parsed.usage.completion_tokens
1857
- });
1858
- }
1859
- await stream.writeSSE(chunk);
1860
- }
1861
- });
1862
- }
1863
1787
  setRequestContext(c, {
1864
1788
  provider: "Copilot",
1865
1789
  model: payload.model
1866
1790
  });
1867
1791
  const selectedModel = state.models?.data.find((model) => model.id === payload.model);
1868
1792
  try {
1869
- if (selectedModel) setRequestContext(c, { inputTokens: (await getTokenCount(payload, selectedModel)).input });
1793
+ if (selectedModel) {
1794
+ const tokenCount = await getTokenCount(payload, selectedModel);
1795
+ setRequestContext(c, { inputTokens: tokenCount.input });
1796
+ }
1870
1797
  } catch (error) {
1871
1798
  consola.warn("Failed to calculate token count:", error);
1872
1799
  }
@@ -1933,7 +1860,8 @@ const createEmbeddings = async (payload) => {
1933
1860
  const embeddingRoutes = new Hono();
1934
1861
  embeddingRoutes.post("/", async (c) => {
1935
1862
  try {
1936
- const response = await createEmbeddings(await c.req.json());
1863
+ const paylod = await c.req.json();
1864
+ const response = await createEmbeddings(paylod);
1937
1865
  return c.json(response);
1938
1866
  } catch (error) {
1939
1867
  return await forwardError(c, error);
@@ -2219,8 +2147,8 @@ const formatArgs = (args) => args.map((arg) => typeof arg === "string" ? arg : u
2219
2147
  depth: null,
2220
2148
  colors: false
2221
2149
  })).join(" ");
2222
- const sanitizeName = (name) => {
2223
- const normalized = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "");
2150
+ const sanitizeName = (name$1) => {
2151
+ const normalized = name$1.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "");
2224
2152
  return normalized === "" ? "handler" : normalized;
2225
2153
  };
2226
2154
  const getLogStream = (filePath) => {
@@ -2274,10 +2202,10 @@ process.on("SIGTERM", () => {
2274
2202
  process.exit(0);
2275
2203
  });
2276
2204
  let lastCleanup = 0;
2277
- const createHandlerLogger = (name) => {
2205
+ const createHandlerLogger = (name$1) => {
2278
2206
  ensureLogDirectory();
2279
- const sanitizedName = sanitizeName(name);
2280
- const instance = consola.withTag(name);
2207
+ const sanitizedName = sanitizeName(name$1);
2208
+ const instance = consola.withTag(name$1);
2281
2209
  if (state.verbose) instance.level = 5;
2282
2210
  instance.setReporters([]);
2283
2211
  instance.addReporter({ log(logObj) {
@@ -2291,7 +2219,8 @@ const createHandlerLogger = (name) => {
2291
2219
  const timestamp = date.toLocaleString("sv-SE", { hour12: false });
2292
2220
  const filePath = path.join(LOG_DIR, `${sanitizedName}-${dateKey}.log`);
2293
2221
  const message = formatArgs(logObj.args);
2294
- appendLine(filePath, `[${timestamp}] [${logObj.type}] [${logObj.tag || name}]${message ? ` ${message}` : ""}`);
2222
+ const line = `[${timestamp}] [${logObj.type}] [${logObj.tag || name$1}]${message ? ` ${message}` : ""}`;
2223
+ appendLine(filePath, line);
2295
2224
  } });
2296
2225
  return instance;
2297
2226
  };
@@ -2646,8 +2575,9 @@ const mapResponsesStopReason = (response) => {
2646
2575
  const mapResponsesUsage = (response) => {
2647
2576
  const inputTokens = response.usage?.input_tokens ?? 0;
2648
2577
  const outputTokens = response.usage?.output_tokens ?? 0;
2578
+ const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
2649
2579
  return {
2650
- input_tokens: inputTokens - (response.usage?.input_tokens_details?.cached_tokens ?? 0),
2580
+ input_tokens: inputTokens - (inputCachedTokens ?? 0),
2651
2581
  output_tokens: outputTokens,
2652
2582
  ...response.usage?.input_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.input_tokens_details.cached_tokens }
2653
2583
  };
@@ -2663,9 +2593,10 @@ const parseUserId = (userId) => {
2663
2593
  const userMatch = userId.match(/user_([^_]+)_account/);
2664
2594
  const safetyIdentifier = userMatch ? userMatch[1] : null;
2665
2595
  const sessionMatch = userId.match(/_session_(.+)$/);
2596
+ const promptCacheKey = sessionMatch ? sessionMatch[1] : null;
2666
2597
  return {
2667
2598
  safetyIdentifier,
2668
- promptCacheKey: sessionMatch ? sessionMatch[1] : null
2599
+ promptCacheKey
2669
2600
  };
2670
2601
  };
2671
2602
  const convertToolResultContent = (content) => {
@@ -2747,11 +2678,11 @@ const handleOutputItemAdded$1 = (rawEvent, state$1) => {
2747
2678
  const events$1 = new Array();
2748
2679
  const functionCallDetails = extractFunctionCallDetails(rawEvent);
2749
2680
  if (!functionCallDetails) return events$1;
2750
- const { outputIndex, toolCallId, name, initialArguments } = functionCallDetails;
2681
+ const { outputIndex, toolCallId, name: name$1, initialArguments } = functionCallDetails;
2751
2682
  const blockIndex = openFunctionCallBlock(state$1, {
2752
2683
  outputIndex,
2753
2684
  toolCallId,
2754
- name,
2685
+ name: name$1,
2755
2686
  events: events$1
2756
2687
  });
2757
2688
  if (initialArguments !== void 0 && initialArguments.length > 0) {
@@ -3044,15 +2975,16 @@ const buildErrorEvent = (message) => ({
3044
2975
  });
3045
2976
  const getBlockKey = (outputIndex, contentIndex) => `${outputIndex}:${contentIndex}`;
3046
2977
  const openFunctionCallBlock = (state$1, params) => {
3047
- const { outputIndex, toolCallId, name, events: events$1 } = params;
2978
+ const { outputIndex, toolCallId, name: name$1, events: events$1 } = params;
3048
2979
  let functionCallState = state$1.functionCallStateByOutputIndex.get(outputIndex);
3049
2980
  if (!functionCallState) {
3050
2981
  const blockIndex$1 = state$1.nextContentBlockIndex;
3051
2982
  state$1.nextContentBlockIndex += 1;
2983
+ const resolvedToolCallId = toolCallId ?? `tool_call_${blockIndex$1}`;
3052
2984
  functionCallState = {
3053
2985
  blockIndex: blockIndex$1,
3054
- toolCallId: toolCallId ?? `tool_call_${blockIndex$1}`,
3055
- name: name ?? "function",
2986
+ toolCallId: resolvedToolCallId,
2987
+ name: name$1 ?? "function",
3056
2988
  consecutiveWhitespaceCount: 0
3057
2989
  };
3058
2990
  state$1.functionCallStateByOutputIndex.set(outputIndex, functionCallState);
@@ -3077,20 +3009,26 @@ const openFunctionCallBlock = (state$1, params) => {
3077
3009
  const extractFunctionCallDetails = (rawEvent) => {
3078
3010
  const item = rawEvent.item;
3079
3011
  if (item.type !== "function_call") return;
3012
+ const outputIndex = rawEvent.output_index;
3013
+ const toolCallId = item.call_id;
3014
+ const name$1 = item.name;
3015
+ const initialArguments = item.arguments;
3080
3016
  return {
3081
- outputIndex: rawEvent.output_index,
3082
- toolCallId: item.call_id,
3083
- name: item.name,
3084
- initialArguments: item.arguments
3017
+ outputIndex,
3018
+ toolCallId,
3019
+ name: name$1,
3020
+ initialArguments
3085
3021
  };
3086
3022
  };
3087
3023
 
3088
3024
  //#endregion
3089
3025
  //#region src/routes/responses/utils.ts
3090
3026
  const getResponsesRequestOptions = (payload) => {
3027
+ const vision = hasVisionInput(payload);
3028
+ const initiator = hasAgentInitiator(payload) ? "agent" : "user";
3091
3029
  return {
3092
- vision: hasVisionInput(payload),
3093
- initiator: hasAgentInitiator(payload) ? "agent" : "user"
3030
+ vision,
3031
+ initiator
3094
3032
  };
3095
3033
  };
3096
3034
  const hasAgentInitiator = (payload) => {
@@ -3382,13 +3320,14 @@ async function handleCompletion(c) {
3382
3320
  const RESPONSES_ENDPOINT$1 = "/responses";
3383
3321
  const MESSAGES_ENDPOINT = "/v1/messages";
3384
3322
  const handleWithChatCompletions = async (c, anthropicPayload, initiatorOverride) => {
3385
- let finalPayload = await applyReplacementsToPayload(translateToOpenAI(anthropicPayload));
3323
+ const openAIPayload = translateToOpenAI(anthropicPayload);
3324
+ let finalPayload = await applyReplacementsToPayload(openAIPayload);
3386
3325
  finalPayload = {
3387
3326
  ...finalPayload,
3388
3327
  model: normalizeModelName(finalPayload.model)
3389
3328
  };
3390
3329
  logger$1.debug("Translated OpenAI request payload:", JSON.stringify(finalPayload));
3391
- const response = isAzureOpenAIModel(finalPayload.model) && state.azureOpenAIConfig ? await createAzureOpenAIChatCompletions(state.azureOpenAIConfig, finalPayload) : await createChatCompletions(finalPayload, { initiator: initiatorOverride });
3330
+ const response = await createChatCompletions(finalPayload, { initiator: initiatorOverride });
3392
3331
  if (isNonStreaming(response)) {
3393
3332
  logger$1.debug("Non-streaming response from Copilot:", JSON.stringify(response).slice(-400));
3394
3333
  const anthropicResponse = translateToAnthropic(response);
@@ -3407,7 +3346,8 @@ const handleWithChatCompletions = async (c, anthropicPayload, initiatorOverride)
3407
3346
  logger$1.debug("Copilot raw stream event:", JSON.stringify(rawEvent));
3408
3347
  if (rawEvent.data === "[DONE]") break;
3409
3348
  if (!rawEvent.data) continue;
3410
- const events$1 = translateChunkToAnthropicEvents(JSON.parse(rawEvent.data), streamState);
3349
+ const chunk = JSON.parse(rawEvent.data);
3350
+ const events$1 = translateChunkToAnthropicEvents(chunk, streamState);
3411
3351
  for (const event of events$1) {
3412
3352
  logger$1.debug("Translated Anthropic event:", JSON.stringify(event));
3413
3353
  await stream.writeSSE({
@@ -3597,19 +3537,9 @@ modelRoutes.get("/", async (c) => {
3597
3537
  owned_by: model.vendor,
3598
3538
  display_name: model.name
3599
3539
  })) ?? [];
3600
- const azureModels = state.azureOpenAIDeployments?.map((deployment) => ({
3601
- id: deployment.id,
3602
- object: "model",
3603
- type: "model",
3604
- created: deployment.created,
3605
- created_at: (/* @__PURE__ */ new Date(deployment.created * 1e3)).toISOString(),
3606
- owned_by: deployment.owned_by,
3607
- display_name: `${deployment.deploymentName} (${deployment.model})`
3608
- })) ?? [];
3609
- const allModels = [...copilotModels, ...azureModels];
3610
3540
  return c.json({
3611
3541
  object: "list",
3612
- data: allModels,
3542
+ data: copilotModels,
3613
3543
  has_more: false
3614
3544
  });
3615
3545
  } catch (error) {
@@ -3633,16 +3563,20 @@ replacementsRoute.post("/", async (c) => {
3633
3563
  return c.json(rule, 201);
3634
3564
  });
3635
3565
  replacementsRoute.delete("/:id", async (c) => {
3636
- if (!await removeReplacement(c.req.param("id"))) return c.json({ error: "Replacement not found or is a system rule" }, 404);
3566
+ const id = c.req.param("id");
3567
+ if (!await removeReplacement(id)) return c.json({ error: "Replacement not found or is a system rule" }, 404);
3637
3568
  return c.json({ success: true });
3638
3569
  });
3639
3570
  replacementsRoute.patch("/:id", async (c) => {
3640
- const rule = await updateReplacement(c.req.param("id"), await c.req.json());
3571
+ const id = c.req.param("id");
3572
+ const body = await c.req.json();
3573
+ const rule = await updateReplacement(id, body);
3641
3574
  if (!rule) return c.json({ error: "Replacement not found or is a system rule" }, 404);
3642
3575
  return c.json(rule);
3643
3576
  });
3644
3577
  replacementsRoute.patch("/:id/toggle", async (c) => {
3645
- const rule = await toggleReplacement(c.req.param("id"));
3578
+ const id = c.req.param("id");
3579
+ const rule = await toggleReplacement(id);
3646
3580
  if (!rule) return c.json({ error: "Replacement not found or is a system rule" }, 404);
3647
3581
  return c.json(rule);
3648
3582
  });
@@ -3826,7 +3760,7 @@ server.route("/v1/messages", messageRoutes);
3826
3760
  //#endregion
3827
3761
  //#region src/start.ts
3828
3762
  async function runServer(options) {
3829
- consola.info(`copilot-api v${version}`);
3763
+ consola.info(`copilot-api v${package_default.version}`);
3830
3764
  if (options.insecure) {
3831
3765
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
3832
3766
  consola.warn("SSL certificate verification disabled (insecure mode)");
@@ -3854,10 +3788,7 @@ async function runServer(options) {
3854
3788
  } else await setupGitHubToken();
3855
3789
  await setupCopilotToken();
3856
3790
  await cacheModels();
3857
- await setupAzureOpenAI();
3858
- const copilotModelIds = state.models?.data.map((model) => model.id) ?? [];
3859
- const azureModelIds = state.azureOpenAIDeployments?.map((deployment) => deployment.id) ?? [];
3860
- const allModelIds = [...copilotModelIds, ...azureModelIds];
3791
+ const allModelIds = state.models?.data.map((model) => model.id) ?? [];
3861
3792
  consola.info(`Available models: \n${allModelIds.map((id) => `- ${id}`).join("\n")}`);
3862
3793
  const serverUrl = `http://localhost:${options.port}`;
3863
3794
  if (options.claudeCode) {
@@ -3990,10 +3921,10 @@ const start = defineCommand({
3990
3921
 
3991
3922
  //#endregion
3992
3923
  //#region src/main.ts
3993
- await runMain(defineCommand({
3924
+ const main = defineCommand({
3994
3925
  meta: {
3995
3926
  name: "copilot-api",
3996
- version,
3927
+ version: package_default.version,
3997
3928
  description: "A wrapper around GitHub Copilot API to make it OpenAI compatible, making it usable for other tools."
3998
3929
  },
3999
3930
  subCommands: {
@@ -4003,7 +3934,8 @@ await runMain(defineCommand({
4003
3934
  debug,
4004
3935
  config
4005
3936
  }
4006
- }));
3937
+ });
3938
+ await runMain(main);
4007
3939
 
4008
3940
  //#endregion
4009
3941
  export { };