xsai-codex 0.0.1 → 0.0.2
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 +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +35 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,12 +6,15 @@ Requires Baseline 2025 [`Uint8Array.fromBase64`](https://developer.mozilla.org/e
|
|
|
6
6
|
|
|
7
7
|
## Usage
|
|
8
8
|
|
|
9
|
+
> For more examples, please refer to the [xsAI Docs](https://xsai.js.org/docs/packages-ext/responses).
|
|
10
|
+
|
|
9
11
|
```ts
|
|
10
12
|
import { responses } from '@xsai-ext/responses'
|
|
11
13
|
import { authorizeCodexHeadless, createCodex } from 'xsai-codex'
|
|
12
14
|
|
|
13
15
|
const auth = await authorizeCodexHeadless({
|
|
14
16
|
onUserCode: ({ instructions }) => {
|
|
17
|
+
// Open https://auth.openai.com/codex/device and enter code: XXXX-XXXXX
|
|
15
18
|
console.log(instructions)
|
|
16
19
|
},
|
|
17
20
|
})
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var version = "0.0.
|
|
1
|
+
var version = "0.0.2";
|
|
2
2
|
|
|
3
3
|
const CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
4
4
|
const CODEX_ISSUER = "https://auth.openai.com";
|
|
@@ -9,15 +9,22 @@ const CODEX_DEVICE_URL = `${CODEX_ISSUER}/codex/device`;
|
|
|
9
9
|
const CODEX_DEVICE_REDIRECT_URL = `${CODEX_ISSUER}/deviceauth/callback`;
|
|
10
10
|
const CODEX_OAUTH_POLLING_SAFETY_MARGIN_MS = 3e3;
|
|
11
11
|
const CODEX_TOKEN_REFRESH_MARGIN_MS = 6e4;
|
|
12
|
-
const CODEX_DUMMY_API_KEY = "codex-oauth";
|
|
13
12
|
const CODEX_DEFAULT_ORIGINATOR = "xsai-codex";
|
|
14
13
|
const CODEX_DEFAULT_USER_AGENT = `xsai-codex/${version}`;
|
|
15
14
|
const CODEX_BASE_URL = "https://chatgpt.com/backend-api/codex/";
|
|
16
15
|
|
|
17
|
-
const sleep = async (ms) =>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
const sleep = async (ms, signal) => {
|
|
17
|
+
if (signal?.aborted) {
|
|
18
|
+
throw signal.reason;
|
|
19
|
+
}
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const timer = setTimeout(resolve, ms);
|
|
22
|
+
signal?.addEventListener("abort", () => {
|
|
23
|
+
clearTimeout(timer);
|
|
24
|
+
reject(signal.reason);
|
|
25
|
+
}, { once: true });
|
|
26
|
+
});
|
|
27
|
+
};
|
|
21
28
|
const formatResponseError = async (message, response) => {
|
|
22
29
|
const body = await response.text().catch(() => "");
|
|
23
30
|
const details = body.trim().slice(0, 500);
|
|
@@ -55,12 +62,19 @@ const extractAccountId = (tokens) => {
|
|
|
55
62
|
const claims = parseJwtClaims(tokens.access_token);
|
|
56
63
|
return claims === void 0 ? void 0 : extractAccountIdFromClaims(claims);
|
|
57
64
|
};
|
|
58
|
-
const toCodexAuthTokens = (tokens,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
refresh
|
|
62
|
-
|
|
63
|
-
}
|
|
65
|
+
const toCodexAuthTokens = (tokens, options = {}) => {
|
|
66
|
+
const accountId = options.accountId ?? extractAccountId(tokens);
|
|
67
|
+
const refresh = tokens.refresh_token ?? options.refreshToken;
|
|
68
|
+
if (refresh === void 0 || refresh.length === 0) {
|
|
69
|
+
throw new Error("Codex token response did not include a refresh token.");
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
access: tokens.access_token,
|
|
73
|
+
...accountId !== void 0 && accountId.length > 0 && { accountId },
|
|
74
|
+
expires: Date.now() + (tokens.expires_in ?? 3600) * 1e3,
|
|
75
|
+
refresh
|
|
76
|
+
};
|
|
77
|
+
};
|
|
64
78
|
const refreshCodexAccessToken = async (refreshToken) => {
|
|
65
79
|
const tokens = await jsonFetch(
|
|
66
80
|
CODEX_TOKEN_URL,
|
|
@@ -75,7 +89,7 @@ const refreshCodexAccessToken = async (refreshToken) => {
|
|
|
75
89
|
},
|
|
76
90
|
"Codex token refresh failed"
|
|
77
91
|
);
|
|
78
|
-
return toCodexAuthTokens(tokens);
|
|
92
|
+
return toCodexAuthTokens(tokens, { refreshToken });
|
|
79
93
|
};
|
|
80
94
|
const authorizeCodexHeadless = async (options = {}) => {
|
|
81
95
|
const deviceData = await jsonFetch(
|
|
@@ -86,7 +100,8 @@ const authorizeCodexHeadless = async (options = {}) => {
|
|
|
86
100
|
"Content-Type": "application/json",
|
|
87
101
|
"User-Agent": CODEX_DEFAULT_USER_AGENT
|
|
88
102
|
},
|
|
89
|
-
method: "POST"
|
|
103
|
+
method: "POST",
|
|
104
|
+
signal: options.signal
|
|
90
105
|
},
|
|
91
106
|
"Failed to initiate Codex device authorization"
|
|
92
107
|
);
|
|
@@ -107,7 +122,8 @@ const authorizeCodexHeadless = async (options = {}) => {
|
|
|
107
122
|
"Content-Type": "application/json",
|
|
108
123
|
"User-Agent": CODEX_DEFAULT_USER_AGENT
|
|
109
124
|
},
|
|
110
|
-
method: "POST"
|
|
125
|
+
method: "POST",
|
|
126
|
+
signal: options.signal
|
|
111
127
|
});
|
|
112
128
|
if (response.ok) {
|
|
113
129
|
const deviceToken = await response.json();
|
|
@@ -122,7 +138,8 @@ const authorizeCodexHeadless = async (options = {}) => {
|
|
|
122
138
|
redirect_uri: CODEX_DEVICE_REDIRECT_URL
|
|
123
139
|
}).toString(),
|
|
124
140
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
125
|
-
method: "POST"
|
|
141
|
+
method: "POST",
|
|
142
|
+
signal: options.signal
|
|
126
143
|
},
|
|
127
144
|
"Codex token exchange failed"
|
|
128
145
|
);
|
|
@@ -131,7 +148,7 @@ const authorizeCodexHeadless = async (options = {}) => {
|
|
|
131
148
|
if (response.status !== 403 && response.status !== 404) {
|
|
132
149
|
throw new Error(await formatResponseError("Codex device authorization failed", response));
|
|
133
150
|
}
|
|
134
|
-
await sleep(interval + CODEX_OAUTH_POLLING_SAFETY_MARGIN_MS);
|
|
151
|
+
await sleep(interval + CODEX_OAUTH_POLLING_SAFETY_MARGIN_MS, options.signal);
|
|
135
152
|
}
|
|
136
153
|
};
|
|
137
154
|
|
|
@@ -151,10 +168,9 @@ const createCodex = async (options) => {
|
|
|
151
168
|
return async (model) => {
|
|
152
169
|
const auth = await resolveAuth();
|
|
153
170
|
return {
|
|
154
|
-
apiKey:
|
|
171
|
+
apiKey: auth.access,
|
|
155
172
|
baseURL: CODEX_BASE_URL,
|
|
156
173
|
headers: {
|
|
157
|
-
"Authorization": `Bearer ${auth.access}`,
|
|
158
174
|
...auth.accountId !== void 0 && auth.accountId.length > 0 && { "ChatGPT-Account-Id": auth.accountId },
|
|
159
175
|
"originator": options.originator ?? CODEX_DEFAULT_ORIGINATOR,
|
|
160
176
|
"User-Agent": options.userAgent ?? CODEX_DEFAULT_USER_AGENT,
|