@ljoukov/llm 7.0.18 → 7.0.19
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 -0
- package/dist/index.cjs +64 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +64 -22
- package/dist/index.js.map +1 -1
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -108,13 +108,22 @@ refresh-token rotation and serves short-lived access tokens.
|
|
|
108
108
|
- `CHATGPT_AUTH_TOKEN_PROVIDER_URL` (example: `https://chatgpt-auth.<your-domain>`)
|
|
109
109
|
- `CHATGPT_AUTH_API_KEY` (shared secret; sent as `Authorization: Bearer ...` and `x-chatgpt-auth: ...`)
|
|
110
110
|
- `CHATGPT_AUTH_TOKEN_PROVIDER_STORE` (`kv` or `d1`, defaults to `kv`)
|
|
111
|
+
- `CHATGPT_CODEX_PROXY_URL` (optional Vercel proxy endpoint, for example `https://<project>.vercel.app/api/codex/responses`)
|
|
112
|
+
- `CHATGPT_CODEX_PROXY_API_KEY` (bearer token for `CHATGPT_CODEX_PROXY_URL`)
|
|
113
|
+
- `CHATGPT_CODEX_ENDPOINT` (optional direct endpoint override; defaults to `https://chatgpt.com/backend-api/codex/responses`)
|
|
111
114
|
- `CHATGPT_RESPONSES_WEBSOCKET_MODE` (`auto` | `off` | `only`, default: `auto`)
|
|
112
115
|
|
|
113
116
|
This repo includes a Cloudflare Workers token provider implementation in `workers/chatgpt-auth/`.
|
|
117
|
+
It also includes a minimal Vercel streaming proxy app in `vercel/codex-proxy/`.
|
|
114
118
|
|
|
115
119
|
If `CHATGPT_AUTH_TOKEN_PROVIDER_URL` + `CHATGPT_AUTH_API_KEY` are set, `chatgpt-*` models will fetch tokens from the
|
|
116
120
|
token provider and will not read the local Codex auth store.
|
|
117
121
|
|
|
122
|
+
If `CHATGPT_CODEX_PROXY_URL` + `CHATGPT_CODEX_PROXY_API_KEY` are set, `chatgpt-*` text requests are sent through that
|
|
123
|
+
proxy and the local process does not need access to the Codex auth store or token provider. The Vercel proxy fetches
|
|
124
|
+
short-lived ChatGPT access tokens from `workers/chatgpt-auth` and streams the upstream Codex response body back to the
|
|
125
|
+
caller.
|
|
126
|
+
|
|
118
127
|
### Responses transport
|
|
119
128
|
|
|
120
129
|
For OpenAI and `chatgpt-*` model paths, this library now tries **Responses WebSocket transport first** and falls back
|
package/dist/index.cjs
CHANGED
|
@@ -712,6 +712,10 @@ function resolveOpenAiImagePriceResolution(imageSize) {
|
|
|
712
712
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
713
713
|
var import_node_util = require("util");
|
|
714
714
|
|
|
715
|
+
// src/utils/env.ts
|
|
716
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
717
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
718
|
+
|
|
715
719
|
// src/utils/runtimeSingleton.ts
|
|
716
720
|
var runtimeSingletonStoreKey = /* @__PURE__ */ Symbol.for("@ljoukov/llm.runtimeSingletonStore");
|
|
717
721
|
function getRuntimeSingletonStore() {
|
|
@@ -740,16 +744,7 @@ function getRuntimeSingleton(key, create) {
|
|
|
740
744
|
return createdValue;
|
|
741
745
|
}
|
|
742
746
|
|
|
743
|
-
// src/openai/chatgpt-auth.ts
|
|
744
|
-
var import_node_buffer = require("buffer");
|
|
745
|
-
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
746
|
-
var import_node_os = __toESM(require("os"), 1);
|
|
747
|
-
var import_node_path2 = __toESM(require("path"), 1);
|
|
748
|
-
var import_zod = require("zod");
|
|
749
|
-
|
|
750
747
|
// src/utils/env.ts
|
|
751
|
-
var import_node_fs = __toESM(require("fs"), 1);
|
|
752
|
-
var import_node_path = __toESM(require("path"), 1);
|
|
753
748
|
var envState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.envState"), () => ({
|
|
754
749
|
envLoaded: false
|
|
755
750
|
}));
|
|
@@ -811,6 +806,11 @@ function parseEnvLine(line) {
|
|
|
811
806
|
}
|
|
812
807
|
|
|
813
808
|
// src/openai/chatgpt-auth.ts
|
|
809
|
+
var import_node_buffer = require("buffer");
|
|
810
|
+
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
811
|
+
var import_node_os = __toESM(require("os"), 1);
|
|
812
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
813
|
+
var import_zod = require("zod");
|
|
814
814
|
var CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_URL";
|
|
815
815
|
var CHATGPT_AUTH_TOKEN_PROVIDER_STORE_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_STORE";
|
|
816
816
|
var CHATGPT_AUTH_API_KEY_ENV = "CHATGPT_AUTH_API_KEY";
|
|
@@ -1718,19 +1718,21 @@ function createAbortError(reason) {
|
|
|
1718
1718
|
|
|
1719
1719
|
// src/openai/chatgpt-codex.ts
|
|
1720
1720
|
var CHATGPT_CODEX_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
|
|
1721
|
+
var CHATGPT_CODEX_ENDPOINT_ENV = "CHATGPT_CODEX_ENDPOINT";
|
|
1722
|
+
var CHATGPT_CODEX_PROXY_URL_ENV = "CHATGPT_CODEX_PROXY_URL";
|
|
1723
|
+
var CHATGPT_CODEX_PROXY_API_KEY_ENV = "CHATGPT_CODEX_PROXY_API_KEY";
|
|
1721
1724
|
var CHATGPT_RESPONSES_EXPERIMENTAL_HEADER = "responses=experimental";
|
|
1722
1725
|
var chatGptCodexState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.chatGptCodexState"), () => ({
|
|
1723
1726
|
cachedResponsesWebSocketMode: null,
|
|
1724
1727
|
chatGptResponsesWebSocketDisabled: false
|
|
1725
1728
|
}));
|
|
1726
1729
|
async function streamChatGptCodexResponse(options) {
|
|
1727
|
-
const
|
|
1730
|
+
const endpointConfig = await resolveChatGptCodexEndpointConfig();
|
|
1728
1731
|
const mode = resolveChatGptResponsesWebSocketMode();
|
|
1729
1732
|
const fallbackStreamFactory = () => {
|
|
1730
1733
|
const streamPromise = streamChatGptCodexResponseSse({
|
|
1731
1734
|
request: options.request,
|
|
1732
|
-
|
|
1733
|
-
accountId,
|
|
1735
|
+
endpointConfig,
|
|
1734
1736
|
sessionId: options.sessionId,
|
|
1735
1737
|
signal: options.signal
|
|
1736
1738
|
});
|
|
@@ -1752,15 +1754,14 @@ async function streamChatGptCodexResponse(options) {
|
|
|
1752
1754
|
return fallbackStreamFactory();
|
|
1753
1755
|
}
|
|
1754
1756
|
const websocketHeaders = buildChatGptCodexHeaders({
|
|
1755
|
-
|
|
1756
|
-
accountId,
|
|
1757
|
+
endpointConfig,
|
|
1757
1758
|
sessionId: options.sessionId,
|
|
1758
1759
|
useWebSocket: true
|
|
1759
1760
|
});
|
|
1760
1761
|
return createAdaptiveResponsesStream({
|
|
1761
1762
|
mode,
|
|
1762
1763
|
createWebSocketStream: async () => await createResponsesWebSocketStream({
|
|
1763
|
-
url: toWebSocketUrl(
|
|
1764
|
+
url: toWebSocketUrl(endpointConfig.url),
|
|
1764
1765
|
headers: websocketHeaders,
|
|
1765
1766
|
request: options.request,
|
|
1766
1767
|
signal: options.signal
|
|
@@ -1773,14 +1774,13 @@ async function streamChatGptCodexResponse(options) {
|
|
|
1773
1774
|
}
|
|
1774
1775
|
async function streamChatGptCodexResponseSse(options) {
|
|
1775
1776
|
const headers = buildChatGptCodexHeaders({
|
|
1776
|
-
|
|
1777
|
-
accountId: options.accountId,
|
|
1777
|
+
endpointConfig: options.endpointConfig,
|
|
1778
1778
|
sessionId: options.sessionId,
|
|
1779
1779
|
useWebSocket: false
|
|
1780
1780
|
});
|
|
1781
1781
|
headers.Accept = "text/event-stream";
|
|
1782
1782
|
headers["Content-Type"] = "application/json";
|
|
1783
|
-
const response = await fetch(
|
|
1783
|
+
const response = await fetch(options.endpointConfig.url, {
|
|
1784
1784
|
method: "POST",
|
|
1785
1785
|
headers,
|
|
1786
1786
|
body: JSON.stringify(options.request),
|
|
@@ -1800,24 +1800,66 @@ function resolveChatGptResponsesWebSocketMode() {
|
|
|
1800
1800
|
if (chatGptCodexState.cachedResponsesWebSocketMode) {
|
|
1801
1801
|
return chatGptCodexState.cachedResponsesWebSocketMode;
|
|
1802
1802
|
}
|
|
1803
|
+
const explicitMode = process.env.CHATGPT_RESPONSES_WEBSOCKET_MODE ?? process.env.OPENAI_RESPONSES_WEBSOCKET_MODE;
|
|
1804
|
+
const defaultMode = resolveChatGptCodexProxyConfig() ? "off" : "auto";
|
|
1803
1805
|
chatGptCodexState.cachedResponsesWebSocketMode = resolveResponsesWebSocketMode(
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
+
explicitMode,
|
|
1807
|
+
defaultMode
|
|
1806
1808
|
);
|
|
1807
1809
|
return chatGptCodexState.cachedResponsesWebSocketMode;
|
|
1808
1810
|
}
|
|
1811
|
+
async function resolveChatGptCodexEndpointConfig() {
|
|
1812
|
+
const proxy = resolveChatGptCodexProxyConfig();
|
|
1813
|
+
if (proxy) {
|
|
1814
|
+
return proxy;
|
|
1815
|
+
}
|
|
1816
|
+
const { access, accountId } = await getChatGptAuthProfile();
|
|
1817
|
+
return {
|
|
1818
|
+
kind: "direct",
|
|
1819
|
+
url: resolveChatGptCodexEndpoint(),
|
|
1820
|
+
access,
|
|
1821
|
+
accountId
|
|
1822
|
+
};
|
|
1823
|
+
}
|
|
1824
|
+
function resolveChatGptCodexEndpoint() {
|
|
1825
|
+
loadLocalEnv();
|
|
1826
|
+
return process.env[CHATGPT_CODEX_ENDPOINT_ENV]?.trim() || CHATGPT_CODEX_ENDPOINT;
|
|
1827
|
+
}
|
|
1828
|
+
function resolveChatGptCodexProxyConfig() {
|
|
1829
|
+
loadLocalEnv();
|
|
1830
|
+
const url = process.env[CHATGPT_CODEX_PROXY_URL_ENV]?.trim();
|
|
1831
|
+
if (!url) {
|
|
1832
|
+
return null;
|
|
1833
|
+
}
|
|
1834
|
+
const apiKey = process.env[CHATGPT_CODEX_PROXY_API_KEY_ENV]?.trim();
|
|
1835
|
+
if (!apiKey) {
|
|
1836
|
+
throw new Error(
|
|
1837
|
+
`${CHATGPT_CODEX_PROXY_API_KEY_ENV} must be provided when ${CHATGPT_CODEX_PROXY_URL_ENV} is set.`
|
|
1838
|
+
);
|
|
1839
|
+
}
|
|
1840
|
+
return {
|
|
1841
|
+
kind: "proxy",
|
|
1842
|
+
url,
|
|
1843
|
+
apiKey
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1809
1846
|
function buildChatGptCodexHeaders(options) {
|
|
1810
1847
|
const openAiBeta = options.useWebSocket ? mergeOpenAiBetaHeader(
|
|
1811
1848
|
CHATGPT_RESPONSES_EXPERIMENTAL_HEADER,
|
|
1812
1849
|
OPENAI_BETA_RESPONSES_WEBSOCKETS_V2
|
|
1813
1850
|
) : CHATGPT_RESPONSES_EXPERIMENTAL_HEADER;
|
|
1814
1851
|
const headers = {
|
|
1815
|
-
Authorization: `Bearer ${options.access}`,
|
|
1816
|
-
"chatgpt-account-id": options.accountId,
|
|
1817
1852
|
"OpenAI-Beta": openAiBeta,
|
|
1818
1853
|
originator: "llm",
|
|
1819
1854
|
"User-Agent": buildUserAgent()
|
|
1820
1855
|
};
|
|
1856
|
+
if (options.endpointConfig.kind === "proxy") {
|
|
1857
|
+
headers.Authorization = `Bearer ${options.endpointConfig.apiKey}`;
|
|
1858
|
+
headers["x-codex-proxy-auth"] = options.endpointConfig.apiKey;
|
|
1859
|
+
} else {
|
|
1860
|
+
headers.Authorization = `Bearer ${options.endpointConfig.access}`;
|
|
1861
|
+
headers["chatgpt-account-id"] = options.endpointConfig.accountId;
|
|
1862
|
+
}
|
|
1821
1863
|
if (options.sessionId) {
|
|
1822
1864
|
headers.session_id = options.sessionId;
|
|
1823
1865
|
}
|