@jeffreycao/copilot-api 1.13.24 → 1.13.25

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/README.md CHANGED
@@ -661,9 +661,15 @@ These endpoints mimic the OpenAI API structure.
661
661
  | --------------------------- | ------ | ---------------------------------------------------------------- |
662
662
  | `POST /v1/responses` | `POST` | OpenAI Most advanced interface for generating model responses. Supports `provider/model` aliases for `openai-responses` providers. |
663
663
  | `POST /v1/chat/completions` | `POST` | Creates a model response for the given chat conversation. Supports `provider/model` aliases for `openai-compatible` providers and can be used without Copilot when the target provider is configured. |
664
- | `GET /v1/models` | `GET` | Lists Copilot models plus enabled provider models using `provider/model-id` IDs. |
664
+ | `GET /v1/models` | `GET` | Lists Copilot models plus enabled provider models using `provider/model-id` IDs. Requests from Codex clients (`User-Agent` beginning with `codex`) are forwarded to the Codex Models upstream. |
665
665
  | `POST /v1/embeddings` | `POST` | Creates an embedding vector representing the input text. |
666
666
 
667
+ ### Codex Backend Proxy Endpoints
668
+
669
+ | Endpoint | Method | Description |
670
+ | -------------------- | ------ | ----------- |
671
+ | `POST /alpha/search` | `POST` | Transparently forwards the JSON body and query parameters to the Codex Alpha Search upstream. The gateway replaces client authorization and account headers with the active Codex login, forwards compatible headers such as `accept`, `content-type`, `originator`, `user-agent`, and `cookie`, and returns the upstream status, headers, and body unchanged. |
672
+
667
673
  ### Anthropic Compatible Endpoints
668
674
 
669
675
  These endpoints are designed to be compatible with the Anthropic Messages API.
package/README.zh-CN.md CHANGED
@@ -667,9 +667,15 @@ curl http://localhost:4141/admin/config/model-mappings \
667
667
  | --- | --- | --- |
668
668
  | `POST /v1/responses` | `POST` | OpenAI 中用于生成模型响应的高级接口。支持 `openai-responses` provider 的 `provider/model` 别名。 |
669
669
  | `POST /v1/chat/completions` | `POST` | 为给定聊天对话创建模型响应。支持 `openai-compatible` provider 的 `provider/model` 别名;目标 provider 已配置时可在没有 Copilot 的情况下使用。 |
670
- | `GET /v1/models` | `GET` | 列出 Copilot 模型以及已启用 provider 的 `provider/model-id` 模型。 |
670
+ | `GET /v1/models` | `GET` | 列出 Copilot 模型以及已启用 provider 的 `provider/model-id` 模型。来自 Codex 客户端(`User-Agent` 以 `codex` 开头)的请求会转发到 Codex Models 上游。 |
671
671
  | `POST /v1/embeddings` | `POST` | 创建表示输入文本的向量嵌入。 |
672
672
 
673
+ ### Codex 后端代理端点
674
+
675
+ | 端点 | 方法 | 说明 |
676
+ | --- | --- | --- |
677
+ | `POST /alpha/search` | `POST` | 将 JSON 请求体和查询参数透明转发到 Codex Alpha Search 上游。网关会使用当前 Codex 登录态覆盖客户端的 authorization 和 account header,透传 `accept`、`content-type`、`originator`、`user-agent`、`cookie` 等兼容 header,并原样返回上游状态码、响应头和响应体。 |
678
+
673
679
  ### Anthropic 兼容端点
674
680
 
675
681
  这些端点设计为兼容 Anthropic Messages API。
package/dist/main.js CHANGED
@@ -25,7 +25,7 @@ bindElectronFetch();
25
25
  const { auth } = await import("./auth-ESiQzf-H.js");
26
26
  const { debug } = await import("./debug-DPB_2BUI.js");
27
27
  const { mcp } = await import("./mcp-BseuqgHR.js");
28
- const { start } = await import("./start-BKjjp9N8.js");
28
+ const { start } = await import("./start-rftAmv6G.js");
29
29
  await runMain(defineCommand({
30
30
  meta: {
31
31
  name: "copilot-api",
@@ -12,8 +12,8 @@ import { Hono } from "hono";
12
12
  import { cors } from "hono/cors";
13
13
  import { logger } from "hono/logger";
14
14
  import { decompress } from "fzstd";
15
- import { streamSSE } from "hono/streaming";
16
15
  import util from "node:util";
16
+ import { streamSSE } from "hono/streaming";
17
17
  //#region src/lib/request-auth.ts
18
18
  function normalizeApiKeys(apiKeys) {
19
19
  if (!Array.isArray(apiKeys)) {
@@ -321,6 +321,147 @@ const createHandlerLogger = (name) => {
321
321
  return instance;
322
322
  };
323
323
  //#endregion
324
+ //#region src/lib/provider-resolver.ts
325
+ function isMissingCodexCredentialsError(error) {
326
+ return error instanceof Error && error.message === "Codex credentials not found. Run `copilot-api auth login --provider codex` first.";
327
+ }
328
+ async function resolveProviderConfig(providerName) {
329
+ const normalizedProviderName = providerName.trim();
330
+ if (!normalizedProviderName) return null;
331
+ if (normalizedProviderName === "codex") {
332
+ if (getRawProviderConfig(normalizedProviderName)?.enabled === false) return null;
333
+ try {
334
+ await setupCodexToken();
335
+ } catch (error) {
336
+ if (isMissingCodexCredentialsError(error)) return null;
337
+ throw error;
338
+ }
339
+ const providerConfig = getProviderConfig(normalizedProviderName);
340
+ if (!providerConfig) return null;
341
+ return {
342
+ ...providerConfig,
343
+ apiKey: state.codexAccessToken ?? providerConfig.apiKey
344
+ };
345
+ }
346
+ return getProviderConfig(normalizedProviderName);
347
+ }
348
+ //#endregion
349
+ //#region src/services/codex/alpha-search.ts
350
+ const CODEX_ALPHA_SEARCH_URL = `${CODEX_API_BASE_URL}/codex/alpha/search`;
351
+ function resolveCodexAlphaSearchUrl(requestUrl) {
352
+ const upstreamUrl = new URL(CODEX_ALPHA_SEARCH_URL);
353
+ upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
354
+ return upstreamUrl.toString();
355
+ }
356
+ async function forwardCodexAlphaSearch(request) {
357
+ const headers = buildCodexRequestHeaders(request.headers);
358
+ if (!headers.has("accept")) headers.set("accept", "application/json");
359
+ const body = await request.arrayBuffer();
360
+ if (!headers.has("content-type")) headers.set("content-type", "application/json");
361
+ return await fetch(resolveCodexAlphaSearchUrl(request.url), {
362
+ method: "POST",
363
+ headers,
364
+ body
365
+ });
366
+ }
367
+ //#endregion
368
+ //#region src/services/providers/provider-proxy.ts
369
+ const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
370
+ const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
371
+ const STRIPPED_RESPONSE_HEADERS = [
372
+ "connection",
373
+ "content-encoding",
374
+ "content-length",
375
+ "keep-alive",
376
+ "proxy-authenticate",
377
+ "proxy-authorization",
378
+ "te",
379
+ "trailer",
380
+ "transfer-encoding",
381
+ "upgrade"
382
+ ];
383
+ function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
384
+ const authHeaders = {};
385
+ if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
386
+ else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
387
+ const headers = {
388
+ "content-type": "application/json",
389
+ accept: "application/json",
390
+ ...authHeaders
391
+ };
392
+ for (const headerName of SHARED_FORWARDABLE_HEADERS) {
393
+ const headerValue = requestHeaders.get(headerName);
394
+ if (headerValue) headers[headerName] = headerValue;
395
+ }
396
+ if (providerConfig.type !== "anthropic") return headers;
397
+ for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
398
+ const headerValue = requestHeaders.get(headerName);
399
+ if (headerValue) headers[headerName] = headerValue;
400
+ }
401
+ return headers;
402
+ }
403
+ function createProviderProxyResponse(upstreamResponse, body) {
404
+ const headers = new Headers(upstreamResponse.headers);
405
+ for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
406
+ return new Response(body ?? upstreamResponse.body, {
407
+ headers,
408
+ status: upstreamResponse.status,
409
+ statusText: upstreamResponse.statusText
410
+ });
411
+ }
412
+ async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
413
+ consola.log(`<-- model: ${payload.model}`);
414
+ return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
415
+ method: "POST",
416
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
417
+ body: JSON.stringify(payload)
418
+ });
419
+ }
420
+ async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
421
+ consola.log(`<-- model: ${payload.model}`);
422
+ return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
423
+ method: "POST",
424
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
425
+ body: JSON.stringify(payload)
426
+ });
427
+ }
428
+ async function forwardProviderResponses(providerConfig, payload, requestHeaders) {
429
+ consola.log(`<-- model: ${payload.model}`);
430
+ return await fetch(`${providerConfig.baseUrl}/v1/responses`, {
431
+ method: "POST",
432
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
433
+ body: JSON.stringify(payload)
434
+ });
435
+ }
436
+ async function forwardProviderModels(providerConfig, requestHeaders) {
437
+ return await fetch(`${providerConfig.baseUrl}/v1/models`, {
438
+ method: "GET",
439
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
440
+ });
441
+ }
442
+ //#endregion
443
+ //#region src/routes/alpha-search/route.ts
444
+ const logger$10 = createHandlerLogger("alpha-search-handler");
445
+ const alphaSearchRoutes = new Hono();
446
+ alphaSearchRoutes.post("/", async (c) => {
447
+ try {
448
+ if (!await resolveProviderConfig("codex")) return c.json({ error: {
449
+ message: "Provider 'codex' not found or disabled",
450
+ type: "invalid_request_error"
451
+ } }, 404);
452
+ debugJson(logger$10, "alpha_search.codex.request", { body: await c.req.raw.clone().text() });
453
+ const upstreamResponse = await forwardCodexAlphaSearch(c.req.raw);
454
+ debugJson(logger$10, "alpha_search.codex.response", {
455
+ body: await upstreamResponse.clone().text(),
456
+ statusCode: upstreamResponse.status
457
+ });
458
+ return createProviderProxyResponse(upstreamResponse);
459
+ } catch (error) {
460
+ logger$10.error("alpha_search.codex.error", { error });
461
+ return await forwardError(c, error);
462
+ }
463
+ });
464
+ //#endregion
324
465
  //#region src/lib/provider-model.ts
325
466
  const parseProviderModelAlias = (model) => {
326
467
  const separatorIndex = model.indexOf("/");
@@ -1447,106 +1588,6 @@ const setContextCacheControl = (part) => {
1447
1588
  part.cache_control = { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL };
1448
1589
  };
1449
1590
  //#endregion
1450
- //#region src/lib/provider-resolver.ts
1451
- function isMissingCodexCredentialsError(error) {
1452
- return error instanceof Error && error.message === "Codex credentials not found. Run `copilot-api auth login --provider codex` first.";
1453
- }
1454
- async function resolveProviderConfig(providerName) {
1455
- const normalizedProviderName = providerName.trim();
1456
- if (!normalizedProviderName) return null;
1457
- if (normalizedProviderName === "codex") {
1458
- if (getRawProviderConfig(normalizedProviderName)?.enabled === false) return null;
1459
- try {
1460
- await setupCodexToken();
1461
- } catch (error) {
1462
- if (isMissingCodexCredentialsError(error)) return null;
1463
- throw error;
1464
- }
1465
- const providerConfig = getProviderConfig(normalizedProviderName);
1466
- if (!providerConfig) return null;
1467
- return {
1468
- ...providerConfig,
1469
- apiKey: state.codexAccessToken ?? providerConfig.apiKey
1470
- };
1471
- }
1472
- return getProviderConfig(normalizedProviderName);
1473
- }
1474
- //#endregion
1475
- //#region src/services/providers/provider-proxy.ts
1476
- const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
1477
- const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
1478
- const STRIPPED_RESPONSE_HEADERS = [
1479
- "connection",
1480
- "content-encoding",
1481
- "content-length",
1482
- "keep-alive",
1483
- "proxy-authenticate",
1484
- "proxy-authorization",
1485
- "te",
1486
- "trailer",
1487
- "transfer-encoding",
1488
- "upgrade"
1489
- ];
1490
- function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
1491
- const authHeaders = {};
1492
- if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
1493
- else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
1494
- const headers = {
1495
- "content-type": "application/json",
1496
- accept: "application/json",
1497
- ...authHeaders
1498
- };
1499
- for (const headerName of SHARED_FORWARDABLE_HEADERS) {
1500
- const headerValue = requestHeaders.get(headerName);
1501
- if (headerValue) headers[headerName] = headerValue;
1502
- }
1503
- if (providerConfig.type !== "anthropic") return headers;
1504
- for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
1505
- const headerValue = requestHeaders.get(headerName);
1506
- if (headerValue) headers[headerName] = headerValue;
1507
- }
1508
- return headers;
1509
- }
1510
- function createProviderProxyResponse(upstreamResponse, body) {
1511
- const headers = new Headers(upstreamResponse.headers);
1512
- for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
1513
- return new Response(body ?? upstreamResponse.body, {
1514
- headers,
1515
- status: upstreamResponse.status,
1516
- statusText: upstreamResponse.statusText
1517
- });
1518
- }
1519
- async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
1520
- consola.log(`<-- model: ${payload.model}`);
1521
- return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
1522
- method: "POST",
1523
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
1524
- body: JSON.stringify(payload)
1525
- });
1526
- }
1527
- async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
1528
- consola.log(`<-- model: ${payload.model}`);
1529
- return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
1530
- method: "POST",
1531
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
1532
- body: JSON.stringify(payload)
1533
- });
1534
- }
1535
- async function forwardProviderResponses(providerConfig, payload, requestHeaders) {
1536
- consola.log(`<-- model: ${payload.model}`);
1537
- return await fetch(`${providerConfig.baseUrl}/v1/responses`, {
1538
- method: "POST",
1539
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
1540
- body: JSON.stringify(payload)
1541
- });
1542
- }
1543
- async function forwardProviderModels(providerConfig, requestHeaders) {
1544
- return await fetch(`${providerConfig.baseUrl}/v1/models`, {
1545
- method: "GET",
1546
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
1547
- });
1548
- }
1549
- //#endregion
1550
1591
  //#region src/routes/provider/chat-completions/handler.ts
1551
1592
  const logger$9 = createHandlerLogger("provider-chat-completions-handler");
1552
1593
  async function handleProviderChatCompletionsForProvider(c, options) {
@@ -3567,7 +3608,8 @@ const translateAnthropicMessagesToResponsesPayload = (payload, subagentAgentId)
3567
3608
  parallel_tool_calls: true,
3568
3609
  reasoning: {
3569
3610
  effort: getReasoningEffortForModel(payload.model),
3570
- summary: "detailed"
3611
+ summary: "detailed",
3612
+ context: "all_turns"
3571
3613
  },
3572
3614
  include: ["reasoning.encrypted_content"]
3573
3615
  };
@@ -5191,16 +5233,16 @@ const CODEX_MODELS = [
5191
5233
  name: "GPT-5.6 Luna"
5192
5234
  }
5193
5235
  ];
5194
- function resolveCodexModelsUrl(requestUrl, baseUrl = CODEX_API_BASE_URL) {
5195
- const modelsUrl = `${(baseUrl.trim().replace(/\/+$/u, "") || "https://chatgpt.com/backend-api").replace(/\/codex(?:\/models)?$/u, "")}/codex/models`;
5196
- const upstreamUrl = new URL(modelsUrl);
5236
+ const CODEX_MODELS_URL = `${CODEX_API_BASE_URL}/codex/models`;
5237
+ function resolveCodexModelsUrl(requestUrl) {
5238
+ const upstreamUrl = new URL(CODEX_MODELS_URL);
5197
5239
  upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
5198
5240
  return upstreamUrl.toString();
5199
5241
  }
5200
- async function forwardCodexModels(requestUrl, requestHeaders, baseUrl = CODEX_API_BASE_URL) {
5242
+ async function forwardCodexModels(requestUrl, requestHeaders) {
5201
5243
  const headers = buildCodexRequestHeaders(requestHeaders);
5202
5244
  if (!headers.has("accept")) headers.set("accept", "application/json");
5203
- return await fetch(resolveCodexModelsUrl(requestUrl, baseUrl), {
5245
+ return await fetch(resolveCodexModelsUrl(requestUrl), {
5204
5246
  method: "GET",
5205
5247
  headers
5206
5248
  });
@@ -6293,7 +6335,7 @@ async function getAggregatedModels(requestHeaders) {
6293
6335
  async function logCodexModelsResponse(response) {
6294
6336
  try {
6295
6337
  const responseText = await response.clone().text();
6296
- logger$4.debug("models.codex.response", {
6338
+ debugJson(logger$4, "models.codex.response", {
6297
6339
  statusCode: response.status,
6298
6340
  models: responseText
6299
6341
  });
@@ -6304,12 +6346,11 @@ async function logCodexModelsResponse(response) {
6304
6346
  modelRoutes.get("/", async (c) => {
6305
6347
  try {
6306
6348
  if (isCodexUserAgent(c.req.header("user-agent"))) {
6307
- const codexProviderConfig = await resolveProviderConfig("codex");
6308
- if (!codexProviderConfig) return c.json({ error: {
6349
+ if (!await resolveProviderConfig("codex")) return c.json({ error: {
6309
6350
  message: "Provider 'codex' not found or disabled",
6310
6351
  type: "invalid_request_error"
6311
6352
  } }, 404);
6312
- const upstreamResponse = await forwardCodexModels(c.req.url, c.req.raw.headers, codexProviderConfig.baseUrl);
6353
+ const upstreamResponse = await forwardCodexModels(c.req.url, c.req.raw.headers);
6313
6354
  await logCodexModelsResponse(upstreamResponse);
6314
6355
  return createProviderProxyResponse(upstreamResponse);
6315
6356
  }
@@ -6793,6 +6834,7 @@ server.route("/usage", usageRoute);
6793
6834
  server.route("/token-usage", tokenUsageRoute);
6794
6835
  server.route("/token", tokenRoute);
6795
6836
  server.route("/responses", responsesRoutes);
6837
+ server.route("/alpha/search", alphaSearchRoutes);
6796
6838
  server.route("/v1/chat/completions", completionRoutes);
6797
6839
  server.route("/v1/models", modelRoutes);
6798
6840
  server.route("/v1/embeddings", embeddingRoutes);
@@ -6803,4 +6845,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
6803
6845
  //#endregion
6804
6846
  export { server };
6805
6847
 
6806
- //# sourceMappingURL=server-PtFlSWWL.js.map
6848
+ //# sourceMappingURL=server-BUJBoWLv.js.map