heibaiapi 0.6.2 → 0.7.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.
package/dist/main.js CHANGED
@@ -5,11 +5,11 @@ 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 { getProxyForUrl } from "proxy-from-env";
9
- import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
10
8
  import clipboard from "clipboardy";
11
9
  import { serve } from "srvx";
12
10
  import invariant from "tiny-invariant";
11
+ import { getProxyForUrl } from "proxy-from-env";
12
+ import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
13
13
  import { execSync } from "node:child_process";
14
14
  import process$1 from "node:process";
15
15
  import { Hono } from "hono";
@@ -17,21 +17,17 @@ import { cors } from "hono/cors";
17
17
  import { logger } from "hono/logger";
18
18
  import { streamSSE } from "hono/streaming";
19
19
  import { events } from "fetch-event-stream";
20
- import fs$1 from "node:fs";
21
20
 
22
21
  //#region src/lib/paths.ts
23
22
  const APP_DIR = path.join(os.homedir(), ".local", "share", "copilot-api");
24
23
  const GITHUB_TOKEN_PATH = path.join(APP_DIR, "github_token");
25
- const CONFIG_PATH = path.join(APP_DIR, "config.json");
26
24
  const PATHS = {
27
25
  APP_DIR,
28
- GITHUB_TOKEN_PATH,
29
- CONFIG_PATH
26
+ GITHUB_TOKEN_PATH
30
27
  };
31
28
  async function ensurePaths() {
32
29
  await fs.mkdir(PATHS.APP_DIR, { recursive: true });
33
30
  await ensureFile(PATHS.GITHUB_TOKEN_PATH);
34
- await ensureFile(PATHS.CONFIG_PATH);
35
31
  }
36
32
  async function ensureFile(filePath) {
37
33
  try {
@@ -796,10 +792,8 @@ async function handleCompletion$1(c) {
796
792
  consola.debug("Request payload:", JSON.stringify(payload).slice(-400));
797
793
  const selectedModel = state.models?.data.find((model) => model.id === payload.model);
798
794
  try {
799
- if (selectedModel) {
800
- const tokenCount = await getTokenCount(payload, selectedModel);
801
- consola.info("Current token count:", tokenCount);
802
- } else consola.warn("No model selected, skipping token count calculation");
795
+ if (selectedModel) consola.info(`[ModelTranslation_OpenAI] 客户端请求: ${payload.model} -> 服务端使用: ${selectedModel.id}`);
796
+ else consola.warn("No model selected, skipping token count calculation");
803
797
  } catch (error) {
804
798
  consola.warn("Failed to calculate token count:", error);
805
799
  }
@@ -891,9 +885,14 @@ function translateToOpenAI(payload) {
891
885
  };
892
886
  }
893
887
  function translateModelName(model) {
894
- if (model.startsWith("claude-sonnet-4-")) return model.replace(/^claude-sonnet-4-.*/, "claude-sonnet-4");
895
- else if (model.startsWith("claude-opus-")) return model.replace(/^claude-opus-4-.*/, "claude-opus-4");
896
- return model;
888
+ let serverModel;
889
+ serverModel = model;
890
+ if (model.includes("claude-3-5")) serverModel = "claude-3.5-sonnet";
891
+ else if (model.includes("claude-opus")) serverModel = "claude-sonnet-4.5";
892
+ else if (model.includes("claude-sonnet-4.5")) serverModel = "claude-sonnet-4.5";
893
+ else if (model.includes("claude-sonnet-4")) serverModel = "claude-sonnet-4";
894
+ consola.info(`[ModelTranslation] 客户端请求: ${model} -> 服务端使用: ${serverModel}`);
895
+ return serverModel;
897
896
  }
898
897
  function translateAnthropicMessagesToOpenAI(anthropicMessages, system) {
899
898
  const systemMessages = handleSystemPrompt(system);
@@ -1084,7 +1083,6 @@ async function handleCountTokens(c) {
1084
1083
  let finalTokenCount = tokenCount.input + tokenCount.output;
1085
1084
  if (anthropicPayload.model.startsWith("claude")) finalTokenCount = Math.round(finalTokenCount * 1.15);
1086
1085
  else if (anthropicPayload.model.startsWith("grok")) finalTokenCount = Math.round(finalTokenCount * 1.03);
1087
- consola.info("Token count:", finalTokenCount);
1088
1086
  return c.json({ input_tokens: finalTokenCount });
1089
1087
  } catch (error) {
1090
1088
  consola.error("Error counting tokens:", error);
@@ -1092,805 +1090,6 @@ async function handleCountTokens(c) {
1092
1090
  }
1093
1091
  }
1094
1092
 
1095
- //#endregion
1096
- //#region src/services/copilot/create-responses.ts
1097
- const createResponses = async (payload, { vision, initiator }) => {
1098
- if (!state.copilotToken) throw new Error("Copilot token not found");
1099
- const headers = {
1100
- ...copilotHeaders(state, vision),
1101
- "X-Initiator": initiator
1102
- };
1103
- const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
1104
- method: "POST",
1105
- headers,
1106
- body: JSON.stringify(payload)
1107
- });
1108
- if (!response.ok) {
1109
- consola.error("Failed to create responses", response);
1110
- throw new HTTPError("Failed to create responses", response);
1111
- }
1112
- if (payload.stream) return events(response);
1113
- return await response.json();
1114
- };
1115
-
1116
- //#endregion
1117
- //#region src/lib/config.ts
1118
- const defaultConfig = { extraPrompts: { "gpt-5-codex": `
1119
- ## Tool use
1120
- - You have access to many tools. If a tool exists to perform a specific task, you MUST use that tool instead of running a terminal command to perform that task.
1121
- ### Bash tool
1122
- When using the Bash tool, follow these rules:
1123
- - always run_in_background set to false, unless you are running a long-running command (e.g., a server or a watch command).
1124
- ### BashOutput tool
1125
- When using the BashOutput tool, follow these rules:
1126
- - Only Bash Tool run_in_background set to true, Use BashOutput to read the output later
1127
- ### TodoWrite tool
1128
- When using the TodoWrite tool, follow these rules:
1129
- - Skip using the TodoWrite tool for tasks with three or fewer steps.
1130
- - Do not make single-step todo lists.
1131
- - When you made a todo, update it after having performed one of the sub-tasks that you shared on the todo list.
1132
- ## Special user requests
1133
- - If the user makes a simple request (such as asking for the time) which you can fulfill by running a terminal command (such as 'date'), you should do so.
1134
- ` } };
1135
- let cachedConfig = null;
1136
- function ensureConfigFile() {
1137
- try {
1138
- fs$1.accessSync(PATHS.CONFIG_PATH, fs$1.constants.R_OK | fs$1.constants.W_OK);
1139
- } catch {
1140
- fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(defaultConfig, null, 2)}\n`, "utf8");
1141
- try {
1142
- fs$1.chmodSync(PATHS.CONFIG_PATH, 384);
1143
- } catch {
1144
- return;
1145
- }
1146
- }
1147
- }
1148
- function readConfigFromDisk() {
1149
- ensureConfigFile();
1150
- try {
1151
- const raw = fs$1.readFileSync(PATHS.CONFIG_PATH, "utf8");
1152
- if (!raw.trim()) {
1153
- fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(defaultConfig, null, 2)}\n`, "utf8");
1154
- return defaultConfig;
1155
- }
1156
- return JSON.parse(raw);
1157
- } catch (error) {
1158
- consola.error("Failed to read config file, using default config", error);
1159
- return defaultConfig;
1160
- }
1161
- }
1162
- function getConfig() {
1163
- if (!cachedConfig) cachedConfig = readConfigFromDisk();
1164
- return cachedConfig;
1165
- }
1166
- function getExtraPromptForModel(model) {
1167
- return getConfig().extraPrompts?.[model] ?? "";
1168
- }
1169
-
1170
- //#endregion
1171
- //#region src/routes/messages/responses-translation.ts
1172
- const MESSAGE_TYPE = "message";
1173
- const translateAnthropicMessagesToResponsesPayload = (payload) => {
1174
- const input = [];
1175
- for (const message of payload.messages) input.push(...translateMessage(message));
1176
- const translatedTools = convertAnthropicTools(payload.tools);
1177
- const toolChoice = convertAnthropicToolChoice(payload.tool_choice);
1178
- const { safetyIdentifier, promptCacheKey } = parseUserId(payload.metadata?.user_id);
1179
- return {
1180
- model: payload.model,
1181
- input,
1182
- instructions: translateSystemPrompt(payload.system, payload.model),
1183
- temperature: payload.temperature ?? null,
1184
- top_p: payload.top_p ?? null,
1185
- max_output_tokens: payload.max_tokens,
1186
- tools: translatedTools,
1187
- tool_choice: toolChoice,
1188
- metadata: payload.metadata ? { ...payload.metadata } : null,
1189
- safety_identifier: safetyIdentifier,
1190
- prompt_cache_key: promptCacheKey,
1191
- stream: payload.stream ?? null,
1192
- store: false,
1193
- parallel_tool_calls: true,
1194
- reasoning: {
1195
- effort: "high",
1196
- summary: "detailed"
1197
- },
1198
- include: ["reasoning.encrypted_content"]
1199
- };
1200
- };
1201
- const translateMessage = (message) => {
1202
- if (message.role === "user") return translateUserMessage(message);
1203
- return translateAssistantMessage(message);
1204
- };
1205
- const translateUserMessage = (message) => {
1206
- if (typeof message.content === "string") return [createMessage("user", message.content)];
1207
- if (!Array.isArray(message.content)) return [];
1208
- const items = [];
1209
- const pendingContent = [];
1210
- for (const block of message.content) {
1211
- if (block.type === "tool_result") {
1212
- flushPendingContent("user", pendingContent, items);
1213
- items.push(createFunctionCallOutput(block));
1214
- continue;
1215
- }
1216
- const converted = translateUserContentBlock(block);
1217
- if (converted) pendingContent.push(converted);
1218
- }
1219
- flushPendingContent("user", pendingContent, items);
1220
- return items;
1221
- };
1222
- const translateAssistantMessage = (message) => {
1223
- if (typeof message.content === "string") return [createMessage("assistant", message.content)];
1224
- if (!Array.isArray(message.content)) return [];
1225
- const items = [];
1226
- const pendingContent = [];
1227
- for (const block of message.content) {
1228
- if (block.type === "tool_use") {
1229
- flushPendingContent("assistant", pendingContent, items);
1230
- items.push(createFunctionToolCall(block));
1231
- continue;
1232
- }
1233
- if (block.type === "thinking" && block.signature && block.signature.includes("@")) {
1234
- flushPendingContent("assistant", pendingContent, items);
1235
- items.push(createReasoningContent(block));
1236
- continue;
1237
- }
1238
- const converted = translateAssistantContentBlock(block);
1239
- if (converted) pendingContent.push(converted);
1240
- }
1241
- flushPendingContent("assistant", pendingContent, items);
1242
- return items;
1243
- };
1244
- const translateUserContentBlock = (block) => {
1245
- switch (block.type) {
1246
- case "text": return createTextContent(block.text);
1247
- case "image": return createImageContent(block);
1248
- default: return;
1249
- }
1250
- };
1251
- const translateAssistantContentBlock = (block) => {
1252
- switch (block.type) {
1253
- case "text": return createOutPutTextContent(block.text);
1254
- default: return;
1255
- }
1256
- };
1257
- const flushPendingContent = (role, pendingContent, target) => {
1258
- if (pendingContent.length === 0) return;
1259
- const messageContent = [...pendingContent];
1260
- target.push(createMessage(role, messageContent));
1261
- pendingContent.length = 0;
1262
- };
1263
- const createMessage = (role, content) => ({
1264
- type: MESSAGE_TYPE,
1265
- role,
1266
- content
1267
- });
1268
- const createTextContent = (text) => ({
1269
- type: "input_text",
1270
- text
1271
- });
1272
- const createOutPutTextContent = (text) => ({
1273
- type: "output_text",
1274
- text
1275
- });
1276
- const createImageContent = (block) => ({
1277
- type: "input_image",
1278
- image_url: `data:${block.source.media_type};base64,${block.source.data}`,
1279
- detail: "auto"
1280
- });
1281
- const createReasoningContent = (block) => {
1282
- const array = block.signature.split("@");
1283
- const signature = array[0];
1284
- return {
1285
- id: array[1],
1286
- type: "reasoning",
1287
- summary: [{
1288
- type: "summary_text",
1289
- text: block.thinking
1290
- }],
1291
- encrypted_content: signature
1292
- };
1293
- };
1294
- const createFunctionToolCall = (block) => ({
1295
- type: "function_call",
1296
- call_id: block.id,
1297
- name: block.name,
1298
- arguments: JSON.stringify(block.input),
1299
- status: "completed"
1300
- });
1301
- const createFunctionCallOutput = (block) => ({
1302
- type: "function_call_output",
1303
- call_id: block.tool_use_id,
1304
- output: convertToolResultContent(block.content),
1305
- status: block.is_error ? "incomplete" : "completed"
1306
- });
1307
- const translateSystemPrompt = (system, model) => {
1308
- if (!system) return null;
1309
- const extraPrompt = getExtraPromptForModel(model);
1310
- if (typeof system === "string") return system + extraPrompt;
1311
- const text = system.map((block, index) => {
1312
- if (index === 0) return block.text + extraPrompt;
1313
- return block.text;
1314
- }).join(" ");
1315
- return text.length > 0 ? text : null;
1316
- };
1317
- const convertAnthropicTools = (tools) => {
1318
- if (!tools || tools.length === 0) return null;
1319
- return tools.map((tool) => ({
1320
- type: "function",
1321
- name: tool.name,
1322
- parameters: tool.input_schema,
1323
- strict: false,
1324
- ...tool.description ? { description: tool.description } : {}
1325
- }));
1326
- };
1327
- const convertAnthropicToolChoice = (choice) => {
1328
- if (!choice) return "auto";
1329
- switch (choice.type) {
1330
- case "auto": return "auto";
1331
- case "any": return "required";
1332
- case "tool": return choice.name ? {
1333
- type: "function",
1334
- name: choice.name
1335
- } : "auto";
1336
- case "none": return "none";
1337
- default: return "auto";
1338
- }
1339
- };
1340
- const translateResponsesResultToAnthropic = (response) => {
1341
- const contentBlocks = mapOutputToAnthropicContent(response.output);
1342
- const usage = mapResponsesUsage(response);
1343
- let anthropicContent = fallbackContentBlocks(response.output_text);
1344
- if (contentBlocks.length > 0) anthropicContent = contentBlocks;
1345
- const stopReason = mapResponsesStopReason(response);
1346
- return {
1347
- id: response.id,
1348
- type: "message",
1349
- role: "assistant",
1350
- content: anthropicContent,
1351
- model: response.model,
1352
- stop_reason: stopReason,
1353
- stop_sequence: null,
1354
- usage
1355
- };
1356
- };
1357
- const mapOutputToAnthropicContent = (output) => {
1358
- const contentBlocks = [];
1359
- for (const item of output) switch (item.type) {
1360
- case "reasoning": {
1361
- const thinkingText = extractReasoningText(item);
1362
- if (thinkingText.length > 0) contentBlocks.push({
1363
- type: "thinking",
1364
- thinking: thinkingText,
1365
- signature: (item.encrypted_content ?? "") + "@" + item.id
1366
- });
1367
- break;
1368
- }
1369
- case "function_call": {
1370
- const toolUseBlock = createToolUseContentBlock(item);
1371
- if (toolUseBlock) contentBlocks.push(toolUseBlock);
1372
- break;
1373
- }
1374
- case "message": {
1375
- const combinedText = combineMessageTextContent(item.content);
1376
- if (combinedText.length > 0) contentBlocks.push({
1377
- type: "text",
1378
- text: combinedText
1379
- });
1380
- break;
1381
- }
1382
- default: {
1383
- const combinedText = combineMessageTextContent(item.content);
1384
- if (combinedText.length > 0) contentBlocks.push({
1385
- type: "text",
1386
- text: combinedText
1387
- });
1388
- }
1389
- }
1390
- return contentBlocks;
1391
- };
1392
- const combineMessageTextContent = (content) => {
1393
- if (!Array.isArray(content)) return "";
1394
- let aggregated = "";
1395
- for (const block of content) {
1396
- if (isResponseOutputText(block)) {
1397
- aggregated += block.text;
1398
- continue;
1399
- }
1400
- if (isResponseOutputRefusal(block)) {
1401
- aggregated += block.refusal;
1402
- continue;
1403
- }
1404
- if (typeof block.text === "string") {
1405
- aggregated += block.text;
1406
- continue;
1407
- }
1408
- if (typeof block.reasoning === "string") {
1409
- aggregated += block.reasoning;
1410
- continue;
1411
- }
1412
- }
1413
- return aggregated;
1414
- };
1415
- const extractReasoningText = (item) => {
1416
- const segments = [];
1417
- const collectFromBlocks = (blocks) => {
1418
- if (!Array.isArray(blocks)) return;
1419
- for (const block of blocks) if (typeof block.text === "string") {
1420
- segments.push(block.text);
1421
- continue;
1422
- }
1423
- };
1424
- collectFromBlocks(item.summary);
1425
- return segments.join("").trim();
1426
- };
1427
- const createToolUseContentBlock = (call) => {
1428
- const toolId = call.call_id;
1429
- if (!call.name || !toolId) return null;
1430
- const input = parseFunctionCallArguments(call.arguments);
1431
- return {
1432
- type: "tool_use",
1433
- id: toolId,
1434
- name: call.name,
1435
- input
1436
- };
1437
- };
1438
- const parseFunctionCallArguments = (rawArguments) => {
1439
- if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
1440
- try {
1441
- const parsed = JSON.parse(rawArguments);
1442
- if (Array.isArray(parsed)) return { arguments: parsed };
1443
- if (parsed && typeof parsed === "object") return parsed;
1444
- } catch (error) {
1445
- consola.warn("Failed to parse function call arguments", {
1446
- error,
1447
- rawArguments
1448
- });
1449
- }
1450
- return { raw_arguments: rawArguments };
1451
- };
1452
- const fallbackContentBlocks = (outputText) => {
1453
- if (!outputText) return [];
1454
- return [{
1455
- type: "text",
1456
- text: outputText
1457
- }];
1458
- };
1459
- const mapResponsesStopReason = (response) => {
1460
- const { status, incomplete_details: incompleteDetails } = response;
1461
- if (status === "completed") return "end_turn";
1462
- if (status === "incomplete") {
1463
- if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
1464
- if (incompleteDetails?.reason === "content_filter") return "end_turn";
1465
- }
1466
- return null;
1467
- };
1468
- const mapResponsesUsage = (response) => {
1469
- const inputTokens = response.usage?.input_tokens ?? 0;
1470
- const outputTokens = response.usage?.output_tokens ?? 0;
1471
- return {
1472
- input_tokens: inputTokens - (response.usage?.input_tokens_details?.cached_tokens ?? 0),
1473
- output_tokens: outputTokens,
1474
- ...response.usage?.input_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.input_tokens_details.cached_tokens }
1475
- };
1476
- };
1477
- const isRecord = (value) => typeof value === "object" && value !== null;
1478
- const isResponseOutputText = (block) => isRecord(block) && "type" in block && block.type === "output_text";
1479
- const isResponseOutputRefusal = (block) => isRecord(block) && "type" in block && block.type === "refusal";
1480
- const parseUserId = (userId) => {
1481
- if (!userId || typeof userId !== "string") return {
1482
- safetyIdentifier: null,
1483
- promptCacheKey: null
1484
- };
1485
- const userMatch = userId.match(/user_([^_]+)_account/);
1486
- const safetyIdentifier = userMatch ? userMatch[1] : null;
1487
- const sessionMatch = userId.match(/_session_(.+)$/);
1488
- return {
1489
- safetyIdentifier,
1490
- promptCacheKey: sessionMatch ? sessionMatch[1] : null
1491
- };
1492
- };
1493
- const convertToolResultContent = (content) => {
1494
- if (typeof content === "string") return content;
1495
- if (Array.isArray(content)) {
1496
- const result = [];
1497
- for (const block of content) switch (block.type) {
1498
- case "text":
1499
- result.push(createTextContent(block.text));
1500
- break;
1501
- case "image":
1502
- result.push(createImageContent(block));
1503
- break;
1504
- default: break;
1505
- }
1506
- return result;
1507
- }
1508
- return "";
1509
- };
1510
-
1511
- //#endregion
1512
- //#region src/routes/messages/responses-stream-translation.ts
1513
- const createResponsesStreamState = () => ({
1514
- messageStartSent: false,
1515
- messageCompleted: false,
1516
- nextContentBlockIndex: 0,
1517
- blockIndexByKey: /* @__PURE__ */ new Map(),
1518
- openBlocks: /* @__PURE__ */ new Set(),
1519
- blockHasDelta: /* @__PURE__ */ new Set(),
1520
- functionCallStateByOutputIndex: /* @__PURE__ */ new Map()
1521
- });
1522
- const translateResponsesStreamEvent = (rawEvent, state$1) => {
1523
- const eventType = rawEvent.type;
1524
- switch (eventType) {
1525
- case "response.created": return handleResponseCreated(rawEvent, state$1);
1526
- case "response.output_item.added": return handleOutputItemAdded(rawEvent, state$1);
1527
- case "response.reasoning_summary_text.delta": return handleReasoningSummaryTextDelta(rawEvent, state$1);
1528
- case "response.output_text.delta": return handleOutputTextDelta(rawEvent, state$1);
1529
- case "response.reasoning_summary_text.done": return handleReasoningSummaryTextDone(rawEvent, state$1);
1530
- case "response.output_text.done": return handleOutputTextDone(rawEvent, state$1);
1531
- case "response.output_item.done": return handleOutputItemDone(rawEvent, state$1);
1532
- case "response.function_call_arguments.delta": return handleFunctionCallArgumentsDelta(rawEvent, state$1);
1533
- case "response.function_call_arguments.done": return handleFunctionCallArgumentsDone(rawEvent, state$1);
1534
- case "response.completed":
1535
- case "response.incomplete": return handleResponseCompleted(rawEvent, state$1);
1536
- case "response.failed": return handleResponseFailed(rawEvent, state$1);
1537
- case "error": return handleErrorEvent(rawEvent, state$1);
1538
- default:
1539
- consola.debug("Unknown Responses stream event type:", eventType);
1540
- return [];
1541
- }
1542
- };
1543
- const handleResponseCreated = (rawEvent, state$1) => {
1544
- return messageStart(state$1, rawEvent.response);
1545
- };
1546
- const handleOutputItemAdded = (rawEvent, state$1) => {
1547
- const events$1 = new Array();
1548
- const functionCallDetails = extractFunctionCallDetails(rawEvent);
1549
- if (!functionCallDetails) return events$1;
1550
- const { outputIndex, toolCallId, name, initialArguments } = functionCallDetails;
1551
- const blockIndex = openFunctionCallBlock(state$1, {
1552
- outputIndex,
1553
- toolCallId,
1554
- name,
1555
- events: events$1
1556
- });
1557
- if (initialArguments !== void 0 && initialArguments.length > 0) {
1558
- events$1.push({
1559
- type: "content_block_delta",
1560
- index: blockIndex,
1561
- delta: {
1562
- type: "input_json_delta",
1563
- partial_json: initialArguments
1564
- }
1565
- });
1566
- state$1.blockHasDelta.add(blockIndex);
1567
- }
1568
- return events$1;
1569
- };
1570
- const handleOutputItemDone = (rawEvent, state$1) => {
1571
- const events$1 = new Array();
1572
- const item = rawEvent.item;
1573
- if (item.type !== "reasoning") return events$1;
1574
- const outputIndex = rawEvent.output_index;
1575
- const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
1576
- const signature = (item.encrypted_content ?? "") + "@" + item.id;
1577
- if (signature) {
1578
- events$1.push({
1579
- type: "content_block_delta",
1580
- index: blockIndex,
1581
- delta: {
1582
- type: "signature_delta",
1583
- signature
1584
- }
1585
- });
1586
- state$1.blockHasDelta.add(blockIndex);
1587
- }
1588
- closeBlockIfOpen(state$1, blockIndex, events$1);
1589
- return events$1;
1590
- };
1591
- const handleFunctionCallArgumentsDelta = (rawEvent, state$1) => {
1592
- const events$1 = new Array();
1593
- const outputIndex = rawEvent.output_index;
1594
- const deltaText = rawEvent.delta;
1595
- const blockIndex = openFunctionCallBlock(state$1, {
1596
- outputIndex,
1597
- events: events$1
1598
- });
1599
- events$1.push({
1600
- type: "content_block_delta",
1601
- index: blockIndex,
1602
- delta: {
1603
- type: "input_json_delta",
1604
- partial_json: deltaText
1605
- }
1606
- });
1607
- state$1.blockHasDelta.add(blockIndex);
1608
- return events$1;
1609
- };
1610
- const handleFunctionCallArgumentsDone = (rawEvent, state$1) => {
1611
- const events$1 = new Array();
1612
- const outputIndex = rawEvent.output_index;
1613
- const blockIndex = openFunctionCallBlock(state$1, {
1614
- outputIndex,
1615
- events: events$1
1616
- });
1617
- const finalArguments = typeof rawEvent.arguments === "string" ? rawEvent.arguments : void 0;
1618
- if (!state$1.blockHasDelta.has(blockIndex) && finalArguments) {
1619
- events$1.push({
1620
- type: "content_block_delta",
1621
- index: blockIndex,
1622
- delta: {
1623
- type: "input_json_delta",
1624
- partial_json: finalArguments
1625
- }
1626
- });
1627
- state$1.blockHasDelta.add(blockIndex);
1628
- }
1629
- closeBlockIfOpen(state$1, blockIndex, events$1);
1630
- state$1.functionCallStateByOutputIndex.delete(outputIndex);
1631
- return events$1;
1632
- };
1633
- const handleOutputTextDelta = (rawEvent, state$1) => {
1634
- const events$1 = new Array();
1635
- const outputIndex = rawEvent.output_index;
1636
- const contentIndex = rawEvent.content_index;
1637
- const deltaText = rawEvent.delta;
1638
- if (!deltaText) return events$1;
1639
- const blockIndex = openTextBlockIfNeeded(state$1, {
1640
- outputIndex,
1641
- contentIndex,
1642
- events: events$1
1643
- });
1644
- events$1.push({
1645
- type: "content_block_delta",
1646
- index: blockIndex,
1647
- delta: {
1648
- type: "text_delta",
1649
- text: deltaText
1650
- }
1651
- });
1652
- state$1.blockHasDelta.add(blockIndex);
1653
- return events$1;
1654
- };
1655
- const handleReasoningSummaryTextDelta = (rawEvent, state$1) => {
1656
- const outputIndex = rawEvent.output_index;
1657
- const deltaText = rawEvent.delta;
1658
- const events$1 = new Array();
1659
- const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
1660
- events$1.push({
1661
- type: "content_block_delta",
1662
- index: blockIndex,
1663
- delta: {
1664
- type: "thinking_delta",
1665
- thinking: deltaText
1666
- }
1667
- });
1668
- state$1.blockHasDelta.add(blockIndex);
1669
- return events$1;
1670
- };
1671
- const handleReasoningSummaryTextDone = (rawEvent, state$1) => {
1672
- const outputIndex = rawEvent.output_index;
1673
- const text = rawEvent.text;
1674
- const events$1 = new Array();
1675
- const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
1676
- if (text && !state$1.blockHasDelta.has(blockIndex)) events$1.push({
1677
- type: "content_block_delta",
1678
- index: blockIndex,
1679
- delta: {
1680
- type: "thinking_delta",
1681
- thinking: text
1682
- }
1683
- });
1684
- return events$1;
1685
- };
1686
- const handleOutputTextDone = (rawEvent, state$1) => {
1687
- const events$1 = new Array();
1688
- const outputIndex = rawEvent.output_index;
1689
- const contentIndex = rawEvent.content_index;
1690
- const text = rawEvent.text;
1691
- const blockIndex = openTextBlockIfNeeded(state$1, {
1692
- outputIndex,
1693
- contentIndex,
1694
- events: events$1
1695
- });
1696
- if (text && !state$1.blockHasDelta.has(blockIndex)) events$1.push({
1697
- type: "content_block_delta",
1698
- index: blockIndex,
1699
- delta: {
1700
- type: "text_delta",
1701
- text
1702
- }
1703
- });
1704
- closeBlockIfOpen(state$1, blockIndex, events$1);
1705
- return events$1;
1706
- };
1707
- const handleResponseCompleted = (rawEvent, state$1) => {
1708
- const response = rawEvent.response;
1709
- const events$1 = new Array();
1710
- closeAllOpenBlocks(state$1, events$1);
1711
- const anthropic = translateResponsesResultToAnthropic(response);
1712
- events$1.push({
1713
- type: "message_delta",
1714
- delta: {
1715
- stop_reason: anthropic.stop_reason,
1716
- stop_sequence: anthropic.stop_sequence
1717
- },
1718
- usage: anthropic.usage
1719
- }, { type: "message_stop" });
1720
- state$1.messageCompleted = true;
1721
- return events$1;
1722
- };
1723
- const handleResponseFailed = (rawEvent, state$1) => {
1724
- const response = rawEvent.response;
1725
- const events$1 = new Array();
1726
- closeAllOpenBlocks(state$1, events$1);
1727
- const message = response.error?.message ?? "The response failed due to an unknown error.";
1728
- events$1.push(buildErrorEvent(message));
1729
- state$1.messageCompleted = true;
1730
- return events$1;
1731
- };
1732
- const handleErrorEvent = (rawEvent, state$1) => {
1733
- const message = typeof rawEvent.message === "string" ? rawEvent.message : "An unexpected error occurred during streaming.";
1734
- state$1.messageCompleted = true;
1735
- return [buildErrorEvent(message)];
1736
- };
1737
- const messageStart = (state$1, response) => {
1738
- state$1.messageStartSent = true;
1739
- const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
1740
- const inputTokens = (response.usage?.input_tokens ?? 0) - (inputCachedTokens ?? 0);
1741
- return [{
1742
- type: "message_start",
1743
- message: {
1744
- id: response.id,
1745
- type: "message",
1746
- role: "assistant",
1747
- content: [],
1748
- model: response.model,
1749
- stop_reason: null,
1750
- stop_sequence: null,
1751
- usage: {
1752
- input_tokens: inputTokens,
1753
- output_tokens: 0,
1754
- ...inputCachedTokens !== void 0 && { cache_creation_input_tokens: inputCachedTokens }
1755
- }
1756
- }
1757
- }];
1758
- };
1759
- const openTextBlockIfNeeded = (state$1, params) => {
1760
- const { outputIndex, contentIndex, events: events$1 } = params;
1761
- const key = getBlockKey(outputIndex, contentIndex);
1762
- let blockIndex = state$1.blockIndexByKey.get(key);
1763
- if (blockIndex === void 0) {
1764
- blockIndex = state$1.nextContentBlockIndex;
1765
- state$1.nextContentBlockIndex += 1;
1766
- state$1.blockIndexByKey.set(key, blockIndex);
1767
- }
1768
- if (!state$1.openBlocks.has(blockIndex)) {
1769
- events$1.push({
1770
- type: "content_block_start",
1771
- index: blockIndex,
1772
- content_block: {
1773
- type: "text",
1774
- text: ""
1775
- }
1776
- });
1777
- state$1.openBlocks.add(blockIndex);
1778
- }
1779
- return blockIndex;
1780
- };
1781
- const openThinkingBlockIfNeeded = (state$1, outputIndex, events$1) => {
1782
- const key = getBlockKey(outputIndex, 0);
1783
- let blockIndex = state$1.blockIndexByKey.get(key);
1784
- if (blockIndex === void 0) {
1785
- blockIndex = state$1.nextContentBlockIndex;
1786
- state$1.nextContentBlockIndex += 1;
1787
- state$1.blockIndexByKey.set(key, blockIndex);
1788
- }
1789
- if (!state$1.openBlocks.has(blockIndex)) {
1790
- events$1.push({
1791
- type: "content_block_start",
1792
- index: blockIndex,
1793
- content_block: {
1794
- type: "thinking",
1795
- thinking: ""
1796
- }
1797
- });
1798
- state$1.openBlocks.add(blockIndex);
1799
- }
1800
- return blockIndex;
1801
- };
1802
- const closeBlockIfOpen = (state$1, blockIndex, events$1) => {
1803
- if (!state$1.openBlocks.has(blockIndex)) return;
1804
- events$1.push({
1805
- type: "content_block_stop",
1806
- index: blockIndex
1807
- });
1808
- state$1.openBlocks.delete(blockIndex);
1809
- state$1.blockHasDelta.delete(blockIndex);
1810
- };
1811
- const closeAllOpenBlocks = (state$1, events$1) => {
1812
- for (const blockIndex of state$1.openBlocks) closeBlockIfOpen(state$1, blockIndex, events$1);
1813
- state$1.functionCallStateByOutputIndex.clear();
1814
- };
1815
- const buildErrorEvent = (message) => ({
1816
- type: "error",
1817
- error: {
1818
- type: "api_error",
1819
- message
1820
- }
1821
- });
1822
- const getBlockKey = (outputIndex, contentIndex) => `${outputIndex}:${contentIndex}`;
1823
- const openFunctionCallBlock = (state$1, params) => {
1824
- const { outputIndex, toolCallId, name, events: events$1 } = params;
1825
- let functionCallState = state$1.functionCallStateByOutputIndex.get(outputIndex);
1826
- if (!functionCallState) {
1827
- const blockIndex$1 = state$1.nextContentBlockIndex;
1828
- state$1.nextContentBlockIndex += 1;
1829
- functionCallState = {
1830
- blockIndex: blockIndex$1,
1831
- toolCallId: toolCallId ?? `tool_call_${blockIndex$1}`,
1832
- name: name ?? "function"
1833
- };
1834
- state$1.functionCallStateByOutputIndex.set(outputIndex, functionCallState);
1835
- }
1836
- const { blockIndex } = functionCallState;
1837
- if (!state$1.openBlocks.has(blockIndex)) {
1838
- events$1.push({
1839
- type: "content_block_start",
1840
- index: blockIndex,
1841
- content_block: {
1842
- type: "tool_use",
1843
- id: functionCallState.toolCallId,
1844
- name: functionCallState.name,
1845
- input: {}
1846
- }
1847
- });
1848
- state$1.openBlocks.add(blockIndex);
1849
- }
1850
- return blockIndex;
1851
- };
1852
- const extractFunctionCallDetails = (rawEvent) => {
1853
- const item = rawEvent.item;
1854
- if (item.type !== "function_call") return;
1855
- return {
1856
- outputIndex: rawEvent.output_index,
1857
- toolCallId: item.call_id,
1858
- name: item.name,
1859
- initialArguments: item.arguments
1860
- };
1861
- };
1862
-
1863
- //#endregion
1864
- //#region src/routes/responses/utils.ts
1865
- const getResponsesRequestOptions = (payload) => {
1866
- return {
1867
- vision: hasVisionInput(payload),
1868
- initiator: hasAgentInitiator(payload) ? "agent" : "user"
1869
- };
1870
- };
1871
- const hasAgentInitiator = (payload) => getPayloadItems(payload).some((item) => {
1872
- if (!("role" in item) || !item.role) return true;
1873
- return (typeof item.role === "string" ? item.role.toLowerCase() : "") === "assistant";
1874
- });
1875
- const hasVisionInput = (payload) => {
1876
- return getPayloadItems(payload).some((item) => containsVisionContent(item));
1877
- };
1878
- const getPayloadItems = (payload) => {
1879
- const result = [];
1880
- const { input } = payload;
1881
- if (Array.isArray(input)) result.push(...input);
1882
- return result;
1883
- };
1884
- const containsVisionContent = (value) => {
1885
- if (!value) return false;
1886
- if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
1887
- if (typeof value !== "object") return false;
1888
- const record = value;
1889
- if ((typeof record.type === "string" ? record.type.toLowerCase() : void 0) === "input_image") return true;
1890
- if (Array.isArray(record.content)) return record.content.some((entry) => containsVisionContent(entry));
1891
- return false;
1892
- };
1893
-
1894
1093
  //#endregion
1895
1094
  //#region src/routes/messages/stream-translation.ts
1896
1095
  function isToolBlockOpen(state$1) {
@@ -2021,15 +1220,9 @@ async function handleCompletion(c) {
2021
1220
  await checkRateLimit(state);
2022
1221
  const anthropicPayload = await c.req.json();
2023
1222
  consola.debug("Anthropic request payload:", JSON.stringify(anthropicPayload));
2024
- const useResponsesApi = shouldUseResponsesApi(anthropicPayload.model);
2025
- if (state.manualApprove) await awaitApproval();
2026
- if (useResponsesApi) return await handleWithResponsesApi(c, anthropicPayload);
2027
- return await handleWithChatCompletions(c, anthropicPayload);
2028
- }
2029
- const RESPONSES_ENDPOINT$1 = "/responses";
2030
- const handleWithChatCompletions = async (c, anthropicPayload) => {
2031
1223
  const openAIPayload = translateToOpenAI(anthropicPayload);
2032
1224
  consola.debug("Translated OpenAI request payload:", JSON.stringify(openAIPayload));
1225
+ if (state.manualApprove) await awaitApproval();
2033
1226
  const response = await createChatCompletions(openAIPayload);
2034
1227
  if (isNonStreaming(response)) {
2035
1228
  consola.debug("Non-streaming response from Copilot:", JSON.stringify(response).slice(-400));
@@ -2059,60 +1252,8 @@ const handleWithChatCompletions = async (c, anthropicPayload) => {
2059
1252
  }
2060
1253
  }
2061
1254
  });
2062
- };
2063
- const handleWithResponsesApi = async (c, anthropicPayload) => {
2064
- const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload);
2065
- consola.debug("Translated Responses payload:", JSON.stringify(responsesPayload));
2066
- const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
2067
- const response = await createResponses(responsesPayload, {
2068
- vision,
2069
- initiator
2070
- });
2071
- if (responsesPayload.stream && isAsyncIterable$1(response)) {
2072
- consola.debug("Streaming response from Copilot (Responses API)");
2073
- return streamSSE(c, async (stream) => {
2074
- const streamState = createResponsesStreamState();
2075
- for await (const chunk of response) {
2076
- if (chunk.event === "ping") {
2077
- await stream.writeSSE({
2078
- event: "ping",
2079
- data: ""
2080
- });
2081
- continue;
2082
- }
2083
- const data = chunk.data;
2084
- if (!data) continue;
2085
- consola.debug("Responses raw stream event:", data);
2086
- const events$1 = translateResponsesStreamEvent(JSON.parse(data), streamState);
2087
- for (const event of events$1) {
2088
- const eventData = JSON.stringify(event);
2089
- consola.debug("Translated Anthropic event:", eventData);
2090
- await stream.writeSSE({
2091
- event: event.type,
2092
- data: eventData
2093
- });
2094
- }
2095
- }
2096
- if (!streamState.messageCompleted) {
2097
- consola.warn("Responses stream ended without completion; sending fallback message_stop");
2098
- const fallback = { type: "message_stop" };
2099
- await stream.writeSSE({
2100
- event: fallback.type,
2101
- data: JSON.stringify(fallback)
2102
- });
2103
- }
2104
- });
2105
- }
2106
- consola.debug("Non-streaming Responses result:", JSON.stringify(response).slice(-400));
2107
- const anthropicResponse = translateResponsesResultToAnthropic(response);
2108
- consola.debug("Translated Anthropic response:", JSON.stringify(anthropicResponse));
2109
- return c.json(anthropicResponse);
2110
- };
2111
- const shouldUseResponsesApi = (modelId) => {
2112
- return (state.models?.data.find((model) => model.id === modelId))?.supported_endpoints?.includes(RESPONSES_ENDPOINT$1) ?? false;
2113
- };
1255
+ }
2114
1256
  const isNonStreaming = (response) => Object.hasOwn(response, "choices");
2115
- const isAsyncIterable$1 = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
2116
1257
 
2117
1258
  //#endregion
2118
1259
  //#region src/routes/messages/route.ts
@@ -2157,68 +1298,6 @@ modelRoutes.get("/", async (c) => {
2157
1298
  }
2158
1299
  });
2159
1300
 
2160
- //#endregion
2161
- //#region src/routes/responses/handler.ts
2162
- const RESPONSES_ENDPOINT = "/responses";
2163
- const handleResponses = async (c) => {
2164
- await checkRateLimit(state);
2165
- const payload = await c.req.json();
2166
- consola.debug("Responses request payload:", JSON.stringify(payload));
2167
- if (!((state.models?.data.find((model) => model.id === payload.model))?.supported_endpoints?.includes(RESPONSES_ENDPOINT) ?? false)) return c.json({ error: {
2168
- message: "This model does not support the responses endpoint. Please choose a different model.",
2169
- type: "invalid_request_error"
2170
- } }, 400);
2171
- const { vision, initiator } = getResponsesRequestOptions(payload);
2172
- if (state.manualApprove) await awaitApproval();
2173
- const response = await createResponses(payload, {
2174
- vision,
2175
- initiator
2176
- });
2177
- if (isStreamingRequested(payload) && isAsyncIterable(response)) {
2178
- consola.debug("Forwarding native Responses stream");
2179
- return streamSSE(c, async (stream) => {
2180
- const pingInterval = setInterval(async () => {
2181
- try {
2182
- await stream.writeSSE({
2183
- event: "ping",
2184
- data: JSON.stringify({ timestamp: Date.now() })
2185
- });
2186
- } catch (error) {
2187
- consola.warn("Failed to send ping:", error);
2188
- clearInterval(pingInterval);
2189
- }
2190
- }, 3e3);
2191
- try {
2192
- for await (const chunk of response) {
2193
- consola.debug("Responses stream chunk:", JSON.stringify(chunk));
2194
- await stream.writeSSE({
2195
- id: chunk.id,
2196
- event: chunk.event,
2197
- data: chunk.data ?? ""
2198
- });
2199
- }
2200
- } finally {
2201
- clearInterval(pingInterval);
2202
- }
2203
- });
2204
- }
2205
- consola.debug("Forwarding native Responses result:", JSON.stringify(response).slice(-400));
2206
- return c.json(response);
2207
- };
2208
- const isAsyncIterable = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
2209
- const isStreamingRequested = (payload) => Boolean(payload.stream);
2210
-
2211
- //#endregion
2212
- //#region src/routes/responses/route.ts
2213
- const responsesRoutes = new Hono();
2214
- responsesRoutes.post("/", async (c) => {
2215
- try {
2216
- return await handleResponses(c);
2217
- } catch (error) {
2218
- return await forwardError(c, error);
2219
- }
2220
- });
2221
-
2222
1301
  //#endregion
2223
1302
  //#region src/routes/token/route.ts
2224
1303
  const tokenRoute = new Hono();
@@ -2258,16 +1337,15 @@ server.route("/models", modelRoutes);
2258
1337
  server.route("/embeddings", embeddingRoutes);
2259
1338
  server.route("/usage", usageRoute);
2260
1339
  server.route("/token", tokenRoute);
2261
- server.route("/responses", responsesRoutes);
2262
1340
  server.route("/v1/chat/completions", completionRoutes);
2263
1341
  server.route("/v1/models", modelRoutes);
2264
1342
  server.route("/v1/embeddings", embeddingRoutes);
2265
- server.route("/v1/responses", responsesRoutes);
2266
1343
  server.route("/v1/messages", messageRoutes);
2267
1344
 
2268
1345
  //#endregion
2269
1346
  //#region src/start.ts
2270
1347
  async function runServer(options) {
1348
+ if (options.proxyEnv) initProxyFromEnv();
2271
1349
  if (options.verbose) {
2272
1350
  consola.level = 5;
2273
1351
  consola.info("Verbose logging enabled");
@@ -2377,6 +1455,11 @@ const start = defineCommand({
2377
1455
  type: "boolean",
2378
1456
  default: false,
2379
1457
  description: "Show GitHub and Copilot tokens on fetch and refresh"
1458
+ },
1459
+ "proxy-env": {
1460
+ type: "boolean",
1461
+ default: false,
1462
+ description: "Initialize proxy from environment variables"
2380
1463
  }
2381
1464
  },
2382
1465
  run({ args }) {
@@ -2391,14 +1474,15 @@ const start = defineCommand({
2391
1474
  rateLimitWait: args.wait,
2392
1475
  githubToken: args["github-token"],
2393
1476
  claudeCode: args["claude-code"],
2394
- showToken: args["show-token"]
1477
+ showToken: args["show-token"],
1478
+ proxyEnv: args["proxy-env"]
2395
1479
  });
2396
1480
  }
2397
1481
  });
2398
1482
 
2399
1483
  //#endregion
2400
1484
  //#region src/main.ts
2401
- const main = defineCommand({
1485
+ await runMain(defineCommand({
2402
1486
  meta: {
2403
1487
  name: "copilot-api",
2404
1488
  description: "A wrapper around GitHub Copilot API to make it OpenAI compatible, making it usable for other tools."
@@ -2409,9 +1493,7 @@ const main = defineCommand({
2409
1493
  "check-usage": checkUsage,
2410
1494
  debug
2411
1495
  }
2412
- });
2413
- initProxyFromEnv();
2414
- await runMain(main);
1496
+ }));
2415
1497
 
2416
1498
  //#endregion
2417
1499
  export { };