@jeffreycao/copilot-api 1.14.6 → 1.14.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/README.zh-CN.md +7 -1
- package/dist/main.js +1 -1
- package/dist/{server-uQjD_RpA.js → server-CF0okzl9.js} +93 -20
- package/dist/server-CF0okzl9.js.map +1 -0
- package/dist/{start-DNPIXvm4.js → start-juT2yVlW.js} +2 -2
- package/dist/{start-DNPIXvm4.js.map → start-juT2yVlW.js.map} +1 -1
- package/package.json +1 -1
- package/dist/server-uQjD_RpA.js.map +0 -1
package/README.md
CHANGED
|
@@ -668,9 +668,15 @@ These endpoints mimic the OpenAI API structure.
|
|
|
668
668
|
|
|
669
669
|
### Codex Backend Proxy Endpoints
|
|
670
670
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
|
671
|
+
These endpoints require an active Codex login. Each endpoint is available both without a version prefix and under `/v1`.
|
|
672
|
+
|
|
673
|
+
| Endpoint | Method | Description |
|
|
674
|
+
| --- | --- | --- |
|
|
675
|
+
| `POST /alpha/search`<br>`POST /v1/alpha/search` | `POST` | Transparently forwards the JSON body and query parameters to the Codex Alpha Search upstream. |
|
|
676
|
+
| `POST /images/generations`<br>`POST /v1/images/generations` | `POST` | Forwards a JSON image generation request to the Codex Images upstream. When the request omits `Content-Type`, the gateway defaults it to `application/json`. |
|
|
677
|
+
| `POST /images/edits`<br>`POST /v1/images/edits` | `POST` | Forwards an image edit request to the Codex Images upstream. Send this request as `multipart/form-data` and let the HTTP client generate the `boundary`; the gateway preserves the incoming content type and streams the upload body. |
|
|
678
|
+
|
|
679
|
+
For every endpoint above, the gateway replaces client authorization and account headers with the active Codex login, preserves query parameters and compatible request headers, and returns the upstream status, headers, and body.
|
|
674
680
|
|
|
675
681
|
### Anthropic Compatible Endpoints
|
|
676
682
|
|
package/README.zh-CN.md
CHANGED
|
@@ -674,9 +674,15 @@ curl http://localhost:4141/admin/config/model-mappings \
|
|
|
674
674
|
|
|
675
675
|
### Codex 后端代理端点
|
|
676
676
|
|
|
677
|
+
这些端点要求已有可用的 Codex 登录态。每个端点同时提供无版本前缀和 `/v1` 两种路径。
|
|
678
|
+
|
|
677
679
|
| 端点 | 方法 | 说明 |
|
|
678
680
|
| --- | --- | --- |
|
|
679
|
-
| `POST /alpha/search` | `POST` | 将 JSON 请求体和查询参数透明转发到 Codex Alpha Search
|
|
681
|
+
| `POST /alpha/search`<br>`POST /v1/alpha/search` | `POST` | 将 JSON 请求体和查询参数透明转发到 Codex Alpha Search 上游。 |
|
|
682
|
+
| `POST /images/generations`<br>`POST /v1/images/generations` | `POST` | 将 JSON 图片生成请求转发到 Codex Images 上游。请求未携带 `Content-Type` 时,网关默认补充 `application/json`。 |
|
|
683
|
+
| `POST /images/edits`<br>`POST /v1/images/edits` | `POST` | 将图片编辑请求转发到 Codex Images 上游。请使用 `multipart/form-data`,并让 HTTP 客户端自动生成 `boundary`;网关会保留传入的 content type,并以流式方式转发上传请求体。 |
|
|
684
|
+
|
|
685
|
+
对于以上所有端点,网关都会使用当前 Codex 登录态覆盖客户端的 authorization 和 account header,保留查询参数及兼容的请求头,并返回上游状态码、响应头和响应体。
|
|
680
686
|
|
|
681
687
|
### Anthropic 兼容端点
|
|
682
688
|
|
package/dist/main.js
CHANGED
|
@@ -25,7 +25,7 @@ bindElectronFetch();
|
|
|
25
25
|
const { auth } = await import("./auth-DvJ8r_tN.js");
|
|
26
26
|
const { debug } = await import("./debug-Bb5hamXX.js");
|
|
27
27
|
const { mcp } = await import("./mcp-BseuqgHR.js");
|
|
28
|
-
const { start } = await import("./start-
|
|
28
|
+
const { start } = await import("./start-juT2yVlW.js");
|
|
29
29
|
await runMain(defineCommand({
|
|
30
30
|
meta: {
|
|
31
31
|
name: "copilot-api",
|
|
@@ -7,6 +7,7 @@ import fs, { readFileSync } from "node:fs";
|
|
|
7
7
|
import fs$1 from "node:fs/promises";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { events } from "fetch-event-stream";
|
|
10
|
+
import { fetch as fetch$1, getGlobalDispatcher } from "undici";
|
|
10
11
|
import { z } from "zod";
|
|
11
12
|
import { Hono } from "hono";
|
|
12
13
|
import { cors } from "hono/cors";
|
|
@@ -299,6 +300,10 @@ const debugLazy = (logger, factory) => {
|
|
|
299
300
|
const debugJson = (logger, label, value) => {
|
|
300
301
|
debugLazy(logger, () => [label, JSON.stringify(value)]);
|
|
301
302
|
};
|
|
303
|
+
const debugJsonAsync = async (logger, label, factory) => {
|
|
304
|
+
if (!state.verbose) return;
|
|
305
|
+
logger.debug(label, JSON.stringify(await factory()));
|
|
306
|
+
};
|
|
302
307
|
const debugJsonTail = (logger, label, { value, tailLength = 400 }) => {
|
|
303
308
|
debugLazy(logger, () => [label, JSON.stringify(value).slice(-tailLength)]);
|
|
304
309
|
};
|
|
@@ -441,7 +446,7 @@ async function forwardProviderModels(providerConfig, requestHeaders) {
|
|
|
441
446
|
}
|
|
442
447
|
//#endregion
|
|
443
448
|
//#region src/routes/alpha-search/route.ts
|
|
444
|
-
const logger$
|
|
449
|
+
const logger$11 = createHandlerLogger("alpha-search-handler");
|
|
445
450
|
const alphaSearchRoutes = new Hono();
|
|
446
451
|
alphaSearchRoutes.post("/", async (c) => {
|
|
447
452
|
try {
|
|
@@ -449,15 +454,15 @@ alphaSearchRoutes.post("/", async (c) => {
|
|
|
449
454
|
message: "Provider 'codex' not found or disabled",
|
|
450
455
|
type: "invalid_request_error"
|
|
451
456
|
} }, 404);
|
|
452
|
-
|
|
457
|
+
await debugJsonAsync(logger$11, "alpha_search.codex.request", async () => ({ body: await c.req.raw.clone().text() }));
|
|
453
458
|
const upstreamResponse = await forwardCodexAlphaSearch(c.req.raw);
|
|
454
|
-
|
|
459
|
+
await debugJsonAsync(logger$11, "alpha_search.codex.response", async () => ({
|
|
455
460
|
body: await upstreamResponse.clone().text(),
|
|
456
461
|
statusCode: upstreamResponse.status
|
|
457
|
-
});
|
|
462
|
+
}));
|
|
458
463
|
return createProviderProxyResponse(upstreamResponse);
|
|
459
464
|
} catch (error) {
|
|
460
|
-
logger$
|
|
465
|
+
logger$11.error("alpha_search.codex.error", { error });
|
|
461
466
|
return await forwardError(c, error);
|
|
462
467
|
}
|
|
463
468
|
});
|
|
@@ -1589,7 +1594,7 @@ const setContextCacheControl = (part) => {
|
|
|
1589
1594
|
};
|
|
1590
1595
|
//#endregion
|
|
1591
1596
|
//#region src/routes/provider/chat-completions/handler.ts
|
|
1592
|
-
const logger$
|
|
1597
|
+
const logger$10 = createHandlerLogger("provider-chat-completions-handler");
|
|
1593
1598
|
async function handleProviderChatCompletionsForProvider(c, options) {
|
|
1594
1599
|
const { payload, provider } = options;
|
|
1595
1600
|
const providerConfig = await resolveProviderConfig(provider);
|
|
@@ -1603,13 +1608,13 @@ async function handleProviderChatCompletionsForProvider(c, options) {
|
|
|
1603
1608
|
applyProviderStreamOptions(payload);
|
|
1604
1609
|
applyDashScopePreserveThinkingDefault(payload, providerConfig);
|
|
1605
1610
|
applyProviderContextCache(payload, modelConfig, providerConfig);
|
|
1606
|
-
debugJson(logger$
|
|
1611
|
+
debugJson(logger$10, "provider.chat_completions.request", {
|
|
1607
1612
|
payload,
|
|
1608
1613
|
provider
|
|
1609
1614
|
});
|
|
1610
1615
|
const upstreamResponse = await forwardProviderChatCompletions(providerConfig, payload, c.req.raw.headers);
|
|
1611
1616
|
if (!upstreamResponse.ok) {
|
|
1612
|
-
logger$
|
|
1617
|
+
logger$10.error("Failed to create provider chat completions", {
|
|
1613
1618
|
provider,
|
|
1614
1619
|
statusCode: upstreamResponse.status
|
|
1615
1620
|
});
|
|
@@ -1623,7 +1628,7 @@ async function handleProviderChatCompletionsForProvider(c, options) {
|
|
|
1623
1628
|
});
|
|
1624
1629
|
const responseBody = await upstreamResponse.clone().json();
|
|
1625
1630
|
recordUsage(normalizeOpenAIUsage(responseBody.usage));
|
|
1626
|
-
debugJson(logger$
|
|
1631
|
+
debugJson(logger$10, "provider.chat_completions.response", responseBody);
|
|
1627
1632
|
return createProviderProxyResponse(upstreamResponse);
|
|
1628
1633
|
}
|
|
1629
1634
|
const applyProviderModelDefaults = (payload, modelConfig) => {
|
|
@@ -1653,12 +1658,12 @@ const createProviderChatCompletionsUsageRecorder = (payload, provider, modelConf
|
|
|
1653
1658
|
providerName: provider
|
|
1654
1659
|
});
|
|
1655
1660
|
const streamProviderChatCompletions = (c, upstreamResponse, options) => {
|
|
1656
|
-
logger$
|
|
1661
|
+
logger$10.debug("provider.chat_completions.streaming", { provider: options.provider });
|
|
1657
1662
|
return streamSSE(c, async (stream) => {
|
|
1658
1663
|
let usage = {};
|
|
1659
1664
|
try {
|
|
1660
1665
|
for await (const chunk of events(upstreamResponse)) {
|
|
1661
|
-
debugJson(logger$
|
|
1666
|
+
debugJson(logger$10, "provider.chat_completions.stream_chunk", chunk);
|
|
1662
1667
|
if (chunk.data && chunk.data !== "[DONE]") {
|
|
1663
1668
|
const parsedChunk = parseChatCompletionChunkData(chunk.data);
|
|
1664
1669
|
if (parsedChunk?.usage) usage = normalizeOpenAIUsage(parsedChunk.usage);
|
|
@@ -1785,7 +1790,7 @@ const createChatCompletions = async (payload, options) => {
|
|
|
1785
1790
|
};
|
|
1786
1791
|
//#endregion
|
|
1787
1792
|
//#region src/routes/chat-completions/handler.ts
|
|
1788
|
-
const logger$
|
|
1793
|
+
const logger$9 = createHandlerLogger("chat-completions-handler");
|
|
1789
1794
|
async function handleCompletion$1(c) {
|
|
1790
1795
|
let payload = await c.req.json();
|
|
1791
1796
|
const requestedModel = payload.model;
|
|
@@ -1799,23 +1804,23 @@ async function handleCompletion$1(c) {
|
|
|
1799
1804
|
provider: providerModelAlias.provider
|
|
1800
1805
|
});
|
|
1801
1806
|
}
|
|
1802
|
-
debugJson(logger$
|
|
1807
|
+
debugJson(logger$9, "Request payload:", payload);
|
|
1803
1808
|
const selectedModel = state.models?.data.find((model) => model.id === payload.model);
|
|
1804
1809
|
if (isNullish(payload.max_tokens) && isNullish(payload.max_completion_tokens)) {
|
|
1805
1810
|
payload = {
|
|
1806
1811
|
...payload,
|
|
1807
1812
|
max_tokens: selectedModel?.capabilities.limits.max_output_tokens
|
|
1808
1813
|
};
|
|
1809
|
-
debugJson(logger$
|
|
1814
|
+
debugJson(logger$9, "Set max_tokens to:", payload.max_tokens);
|
|
1810
1815
|
}
|
|
1811
1816
|
if (payload.model.includes("gpt")) {
|
|
1812
1817
|
if (isNullish(payload.max_completion_tokens)) payload.max_completion_tokens = payload.max_tokens;
|
|
1813
1818
|
delete payload.max_tokens;
|
|
1814
1819
|
}
|
|
1815
1820
|
const requestId = generateRequestIdFromPayload(payload);
|
|
1816
|
-
logger$
|
|
1821
|
+
logger$9.debug("Generated request ID:", requestId);
|
|
1817
1822
|
const sessionId = getUUID(requestId);
|
|
1818
|
-
logger$
|
|
1823
|
+
logger$9.debug("Extracted session ID:", sessionId);
|
|
1819
1824
|
const recordUsage = createCopilotTokenUsageRecorder({
|
|
1820
1825
|
endpoint: "chat_completions",
|
|
1821
1826
|
fallbackSessionId: sessionId,
|
|
@@ -1826,18 +1831,18 @@ async function handleCompletion$1(c) {
|
|
|
1826
1831
|
sessionId
|
|
1827
1832
|
});
|
|
1828
1833
|
if (isNonStreaming$1(response)) {
|
|
1829
|
-
debugJson(logger$
|
|
1834
|
+
debugJson(logger$9, "Non-streaming response:", response);
|
|
1830
1835
|
recordUsage({
|
|
1831
1836
|
...normalizeOpenAIUsage(response.usage),
|
|
1832
1837
|
total_nano_aiu: normalizeOptionalToken(response.copilot_usage?.total_nano_aiu)
|
|
1833
1838
|
});
|
|
1834
1839
|
return c.json(response);
|
|
1835
1840
|
}
|
|
1836
|
-
logger$
|
|
1841
|
+
logger$9.debug("Streaming response");
|
|
1837
1842
|
return streamSSE(c, async (stream) => {
|
|
1838
1843
|
let usage = {};
|
|
1839
1844
|
for await (const chunk of response) {
|
|
1840
|
-
debugJson(logger$
|
|
1845
|
+
debugJson(logger$9, "Streaming chunk:", chunk);
|
|
1841
1846
|
const parsedChunk = parseChatCompletionChunk(chunk);
|
|
1842
1847
|
if (parsedChunk?.usage || parsedChunk?.copilot_usage) usage = {
|
|
1843
1848
|
...normalizeOpenAIUsage(parsedChunk.usage),
|
|
@@ -1926,6 +1931,71 @@ embeddingRoutes.post("/", async (c) => {
|
|
|
1926
1931
|
}
|
|
1927
1932
|
});
|
|
1928
1933
|
//#endregion
|
|
1934
|
+
//#region src/services/codex/images.ts
|
|
1935
|
+
const CODEX_IMAGES_TIMEOUT_MS = 900 * 1e3;
|
|
1936
|
+
const codexImagesDispatcher = { dispatch(options, handler) {
|
|
1937
|
+
return getGlobalDispatcher().dispatch({
|
|
1938
|
+
...options,
|
|
1939
|
+
bodyTimeout: CODEX_IMAGES_TIMEOUT_MS,
|
|
1940
|
+
headersTimeout: CODEX_IMAGES_TIMEOUT_MS
|
|
1941
|
+
}, handler);
|
|
1942
|
+
} };
|
|
1943
|
+
function resolveCodexImagesUrl(requestUrl, operation) {
|
|
1944
|
+
const upstreamUrl = new URL(`${CODEX_API_BASE_URL}/codex/images/${operation}`);
|
|
1945
|
+
upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
|
|
1946
|
+
return upstreamUrl.toString();
|
|
1947
|
+
}
|
|
1948
|
+
async function forwardCodexImages(request, operation) {
|
|
1949
|
+
const headers = buildCodexRequestHeaders(request.headers);
|
|
1950
|
+
if (!headers.has("accept")) headers.set("accept", "application/json");
|
|
1951
|
+
if (operation === "generations" && !headers.has("content-type")) headers.set("content-type", "application/json");
|
|
1952
|
+
const init = {
|
|
1953
|
+
method: "POST",
|
|
1954
|
+
headers,
|
|
1955
|
+
body: request.body,
|
|
1956
|
+
duplex: "half",
|
|
1957
|
+
signal: AbortSignal.timeout(CODEX_IMAGES_TIMEOUT_MS)
|
|
1958
|
+
};
|
|
1959
|
+
const upstreamUrl = resolveCodexImagesUrl(request.url, operation);
|
|
1960
|
+
if (typeof Bun !== "undefined") return await fetch(upstreamUrl, init);
|
|
1961
|
+
return await fetch$1(upstreamUrl, {
|
|
1962
|
+
...init,
|
|
1963
|
+
dispatcher: codexImagesDispatcher
|
|
1964
|
+
});
|
|
1965
|
+
}
|
|
1966
|
+
//#endregion
|
|
1967
|
+
//#region src/routes/images/route.ts
|
|
1968
|
+
const logger$8 = createHandlerLogger("images-handler");
|
|
1969
|
+
const imageRoutes = new Hono();
|
|
1970
|
+
const imageRouteDependencies = { debugJsonAsync };
|
|
1971
|
+
function getContentMetadata(headers) {
|
|
1972
|
+
return {
|
|
1973
|
+
contentType: headers.get("content-type"),
|
|
1974
|
+
contentLength: headers.get("content-length")
|
|
1975
|
+
};
|
|
1976
|
+
}
|
|
1977
|
+
async function handleCodexImages(c, operation) {
|
|
1978
|
+
try {
|
|
1979
|
+
if (!await resolveProviderConfig("codex")) return c.json({ error: {
|
|
1980
|
+
message: "Provider 'codex' not found or disabled",
|
|
1981
|
+
type: "invalid_request_error"
|
|
1982
|
+
} }, 404);
|
|
1983
|
+
if (operation === "generations") await imageRouteDependencies.debugJsonAsync(logger$8, "images.generations.codex.request", async () => ({ body: await c.req.raw.clone().text() }));
|
|
1984
|
+
else debugJson(logger$8, "images.edits.codex.request", getContentMetadata(c.req.raw.headers));
|
|
1985
|
+
const upstreamResponse = await forwardCodexImages(c.req.raw, operation);
|
|
1986
|
+
debugJson(logger$8, `images.${operation}.codex.response`, {
|
|
1987
|
+
...getContentMetadata(upstreamResponse.headers),
|
|
1988
|
+
statusCode: upstreamResponse.status
|
|
1989
|
+
});
|
|
1990
|
+
return createProviderProxyResponse(upstreamResponse);
|
|
1991
|
+
} catch (error) {
|
|
1992
|
+
logger$8.error(`images.${operation}.codex.error`, { error });
|
|
1993
|
+
return await forwardError(c, error);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
imageRoutes.post("/generations", (c) => handleCodexImages(c, "generations"));
|
|
1997
|
+
imageRoutes.post("/edits", (c) => handleCodexImages(c, "edits"));
|
|
1998
|
+
//#endregion
|
|
1929
1999
|
//#region src/lib/tokenizer.ts
|
|
1930
2000
|
const ENCODING_MAP = {
|
|
1931
2001
|
o200k_base: () => import("gpt-tokenizer/encoding/o200k_base"),
|
|
@@ -6910,14 +6980,17 @@ server.route("/token-usage", tokenUsageRoute);
|
|
|
6910
6980
|
server.route("/token", tokenRoute);
|
|
6911
6981
|
server.route("/responses", responsesRoutes);
|
|
6912
6982
|
server.route("/alpha/search", alphaSearchRoutes);
|
|
6983
|
+
server.route("/images", imageRoutes);
|
|
6913
6984
|
server.route("/v1/chat/completions", completionRoutes);
|
|
6914
6985
|
server.route("/v1/models", modelRoutes);
|
|
6915
6986
|
server.route("/v1/embeddings", embeddingRoutes);
|
|
6916
6987
|
server.route("/v1/responses", responsesRoutes);
|
|
6988
|
+
server.route("/v1/alpha/search", alphaSearchRoutes);
|
|
6989
|
+
server.route("/v1/images", imageRoutes);
|
|
6917
6990
|
server.route("/v1/messages", messageRoutes);
|
|
6918
6991
|
server.route("/:provider/v1/messages", providerMessageRoutes);
|
|
6919
6992
|
server.route("/:provider/v1/models", providerModelRoutes);
|
|
6920
6993
|
//#endregion
|
|
6921
6994
|
export { server };
|
|
6922
6995
|
|
|
6923
|
-
//# sourceMappingURL=server-
|
|
6996
|
+
//# sourceMappingURL=server-CF0okzl9.js.map
|