@juspay/neurolink 11.2.0 → 11.2.1
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/CHANGELOG.md +6 -0
- package/dist/auth/codexOAuth.d.ts +67 -0
- package/dist/auth/codexOAuth.js +202 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.js +4 -0
- package/dist/browser/neurolink.min.js +419 -419
- package/dist/cli/commands/auth.d.ts +27 -8
- package/dist/cli/commands/auth.js +425 -6
- package/dist/cli/commands/proxy.js +230 -5
- package/dist/cli/factories/authCommandFactory.d.ts +8 -0
- package/dist/cli/factories/authCommandFactory.js +74 -1
- package/dist/lib/auth/codexOAuth.d.ts +67 -0
- package/dist/lib/auth/codexOAuth.js +203 -0
- package/dist/lib/auth/index.d.ts +1 -0
- package/dist/lib/auth/index.js +4 -0
- package/dist/lib/proxy/accountCooldown.js +35 -2
- package/dist/lib/proxy/accountQuota.d.ts +29 -3
- package/dist/lib/proxy/accountQuota.js +203 -12
- package/dist/lib/proxy/accountUsage.js +15 -2
- package/dist/lib/proxy/codexAccountUsage.d.ts +26 -0
- package/dist/lib/proxy/codexAccountUsage.js +174 -0
- package/dist/lib/proxy/proxyAnalysis.js +12 -1
- package/dist/lib/proxy/proxyConfig.js +24 -0
- package/dist/lib/proxy/routingEvidence.d.ts +12 -1
- package/dist/lib/proxy/routingEvidence.js +23 -0
- package/dist/lib/proxy/runtimeConfig.js +3 -0
- package/dist/lib/server/routes/claudeProxyRoutes.d.ts +79 -5
- package/dist/lib/server/routes/claudeProxyRoutes.js +653 -72
- package/dist/lib/server/routes/codexProxyRoutes.d.ts +64 -0
- package/dist/lib/server/routes/codexProxyRoutes.js +454 -0
- package/dist/lib/types/cli.d.ts +7 -1
- package/dist/lib/types/codex.d.ts +95 -0
- package/dist/lib/types/codex.js +15 -0
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/types/index.js +1 -0
- package/dist/lib/types/proxy.d.ts +83 -0
- package/dist/lib/types/subscription.d.ts +13 -0
- package/dist/proxy/accountCooldown.js +35 -2
- package/dist/proxy/accountQuota.d.ts +29 -3
- package/dist/proxy/accountQuota.js +203 -12
- package/dist/proxy/accountUsage.js +15 -2
- package/dist/proxy/codexAccountUsage.d.ts +26 -0
- package/dist/proxy/codexAccountUsage.js +173 -0
- package/dist/proxy/proxyAnalysis.js +12 -1
- package/dist/proxy/proxyConfig.js +24 -0
- package/dist/proxy/routingEvidence.d.ts +12 -1
- package/dist/proxy/routingEvidence.js +23 -0
- package/dist/proxy/runtimeConfig.js +3 -0
- package/dist/server/routes/claudeProxyRoutes.d.ts +79 -5
- package/dist/server/routes/claudeProxyRoutes.js +653 -72
- package/dist/server/routes/codexProxyRoutes.d.ts +64 -0
- package/dist/server/routes/codexProxyRoutes.js +453 -0
- package/dist/types/cli.d.ts +7 -1
- package/dist/types/codex.d.ts +95 -0
- package/dist/types/codex.js +14 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/proxy.d.ts +83 -0
- package/dist/types/subscription.d.ts +13 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [11.2.1](https://github.com/juspay/neurolink/compare/v11.2.0...v11.2.1) (2026-08-18)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
- **(proxy):** rotate off entitlement-blocked accounts and route model-scoped quota ([a634f8a](https://github.com/juspay/neurolink/commit/a634f8ae5124daec78ea336f5d211f3b960690be))
|
|
6
|
+
|
|
1
7
|
## [11.2.0](https://github.com/juspay/neurolink/compare/v11.1.1...v11.2.0) (2026-08-18)
|
|
2
8
|
|
|
3
9
|
### Features
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex (OpenAI ChatGPT) OAuth + identity helpers.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the role of `anthropicOAuth.ts` for the Codex pool engine. Codex signs
|
|
5
|
+
* in with a ChatGPT account (PKCE OAuth against auth.openai.com) and talks to the
|
|
6
|
+
* ChatGPT backend Responses API. The proxy pools multiple ChatGPT accounts, so it
|
|
7
|
+
* needs to: import an existing `~/.codex/auth.json`, refresh access tokens, and
|
|
8
|
+
* derive the per-account `chatgpt-account-id` the backend requires.
|
|
9
|
+
*
|
|
10
|
+
* Constants verified against Codex CLI 0.144.4.
|
|
11
|
+
*/
|
|
12
|
+
import type { CodexImportedCredential } from "../types/index.js";
|
|
13
|
+
export declare const CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
14
|
+
export declare const CODEX_AUTH_URL = "https://auth.openai.com/oauth/authorize";
|
|
15
|
+
export declare const CODEX_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
16
|
+
export declare const CODEX_REVOKE_URL = "https://auth.openai.com/oauth/revoke";
|
|
17
|
+
export declare const CODEX_REDIRECT_URI = "http://localhost:1455/auth/callback";
|
|
18
|
+
export declare const CODEX_DEFAULT_SCOPES: readonly ["openid", "profile", "email", "offline_access"];
|
|
19
|
+
export declare const CODEX_BACKEND_BASE_URL = "https://chatgpt.com/backend-api/codex";
|
|
20
|
+
export declare const CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
21
|
+
export declare const CODEX_USAGE_URL = "https://chatgpt.com/backend-api/codex/usage";
|
|
22
|
+
export declare const CODEX_VERSION = "0.144.4";
|
|
23
|
+
export declare const CODEX_USER_AGENT = "codex_cli_rs/0.144.4 (external; neurolink-proxy)";
|
|
24
|
+
export declare const CODEX_ORIGINATOR = "codex_cli_rs";
|
|
25
|
+
/** Decode the ChatGPT account id, plan type, and expiry from an access token. */
|
|
26
|
+
export declare function decodeCodexAccessToken(accessToken: string): {
|
|
27
|
+
accountId?: string;
|
|
28
|
+
planType?: string;
|
|
29
|
+
expiresAt?: number;
|
|
30
|
+
};
|
|
31
|
+
/** Decode the account email from an id token (best-effort, for the label). */
|
|
32
|
+
export declare function decodeCodexEmail(idToken?: string): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* The chatgpt-account-id the backend requires for a given access token.
|
|
35
|
+
* Prefer the value stored on the account; fall back to decoding the JWT so a
|
|
36
|
+
* rotated token always carries a coherent account id.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveCodexAccountId(accessToken: string, storedAccountId?: string): string | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Import an existing Codex credential from `~/.codex/auth.json` (or a custom
|
|
41
|
+
* path). This is the verified, robust login path: the user logs into Codex
|
|
42
|
+
* normally, then imports the current account into the proxy pool.
|
|
43
|
+
*/
|
|
44
|
+
export declare function importCodexAuthFile(authFilePath?: string): Promise<CodexImportedCredential>;
|
|
45
|
+
/**
|
|
46
|
+
* Refresh a Codex access token. Verified: POST form-urlencoded to
|
|
47
|
+
* auth.openai.com/oauth/token with grant_type=refresh_token and the Codex
|
|
48
|
+
* client id. Returns a fresh credential (access + rotated refresh token).
|
|
49
|
+
*/
|
|
50
|
+
export declare function refreshCodexToken(refreshToken: string, options?: {
|
|
51
|
+
timeoutMs?: number;
|
|
52
|
+
}): Promise<CodexImportedCredential>;
|
|
53
|
+
/**
|
|
54
|
+
* Whether a {@link refreshCodexToken} failure means the refresh token itself is
|
|
55
|
+
* no longer usable, as opposed to the request never getting a verdict.
|
|
56
|
+
*
|
|
57
|
+
* Only the authorization server rejecting the grant is permanent. A 5xx, a
|
|
58
|
+
* timeout, or a DNS failure says nothing about the credential, and treating
|
|
59
|
+
* those as permanent disables a working account — which `auth cleanup` then
|
|
60
|
+
* deletes.
|
|
61
|
+
*/
|
|
62
|
+
export declare function isPermanentCodexRefreshFailure(error: unknown): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Whether an imported credential's access token is expired (or within the
|
|
65
|
+
* buffer window) and should be refreshed before use.
|
|
66
|
+
*/
|
|
67
|
+
export declare function codexTokenNeedsRefresh(expiresAt: number | undefined, bufferMs?: number): boolean;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex (OpenAI ChatGPT) OAuth + identity helpers.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the role of `anthropicOAuth.ts` for the Codex pool engine. Codex signs
|
|
5
|
+
* in with a ChatGPT account (PKCE OAuth against auth.openai.com) and talks to the
|
|
6
|
+
* ChatGPT backend Responses API. The proxy pools multiple ChatGPT accounts, so it
|
|
7
|
+
* needs to: import an existing `~/.codex/auth.json`, refresh access tokens, and
|
|
8
|
+
* derive the per-account `chatgpt-account-id` the backend requires.
|
|
9
|
+
*
|
|
10
|
+
* Constants verified against Codex CLI 0.144.4.
|
|
11
|
+
*/
|
|
12
|
+
import { readFile } from "node:fs/promises";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { logger } from "../utils/logger.js";
|
|
16
|
+
// OAuth client + endpoints (Codex CLI values).
|
|
17
|
+
export const CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
18
|
+
export const CODEX_AUTH_URL = "https://auth.openai.com/oauth/authorize";
|
|
19
|
+
export const CODEX_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
20
|
+
export const CODEX_REVOKE_URL = "https://auth.openai.com/oauth/revoke";
|
|
21
|
+
export const CODEX_REDIRECT_URI = "http://localhost:1455/auth/callback";
|
|
22
|
+
export const CODEX_DEFAULT_SCOPES = [
|
|
23
|
+
"openid",
|
|
24
|
+
"profile",
|
|
25
|
+
"email",
|
|
26
|
+
"offline_access",
|
|
27
|
+
];
|
|
28
|
+
// Upstream backend (ChatGPT subscription path).
|
|
29
|
+
export const CODEX_BACKEND_BASE_URL = "https://chatgpt.com/backend-api/codex";
|
|
30
|
+
export const CODEX_RESPONSES_URL = `${CODEX_BACKEND_BASE_URL}/responses`;
|
|
31
|
+
export const CODEX_USAGE_URL = `${CODEX_BACKEND_BASE_URL}/usage`;
|
|
32
|
+
// Client fingerprint. The real Codex CLI sends these; the proxy defaults them
|
|
33
|
+
// only when a non-Codex client omits them.
|
|
34
|
+
export const CODEX_VERSION = "0.144.4";
|
|
35
|
+
export const CODEX_USER_AGENT = `codex_cli_rs/${CODEX_VERSION} (external; neurolink-proxy)`;
|
|
36
|
+
export const CODEX_ORIGINATOR = "codex_cli_rs";
|
|
37
|
+
const CODEX_AUTH_FILE = join(homedir(), ".codex", "auth.json");
|
|
38
|
+
/** Base64url-decode a JWT segment into a JSON object (claims only, no verify). */
|
|
39
|
+
function decodeJwtSegment(segment) {
|
|
40
|
+
try {
|
|
41
|
+
const padded = segment + "=".repeat((4 - (segment.length % 4)) % 4);
|
|
42
|
+
const json = Buffer.from(padded.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf8");
|
|
43
|
+
const parsed = JSON.parse(json);
|
|
44
|
+
return parsed && typeof parsed === "object"
|
|
45
|
+
? parsed
|
|
46
|
+
: null;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function getOpenAiAuthClaim(claims) {
|
|
53
|
+
if (!claims) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const auth = claims["https://api.openai.com/auth"];
|
|
57
|
+
return auth && typeof auth === "object"
|
|
58
|
+
? auth
|
|
59
|
+
: null;
|
|
60
|
+
}
|
|
61
|
+
/** Decode the ChatGPT account id, plan type, and expiry from an access token. */
|
|
62
|
+
export function decodeCodexAccessToken(accessToken) {
|
|
63
|
+
const parts = accessToken.split(".");
|
|
64
|
+
if (parts.length !== 3) {
|
|
65
|
+
return {};
|
|
66
|
+
}
|
|
67
|
+
const claims = decodeJwtSegment(parts[1]);
|
|
68
|
+
const authClaim = getOpenAiAuthClaim(claims);
|
|
69
|
+
const accountId = typeof authClaim?.chatgpt_account_id === "string"
|
|
70
|
+
? authClaim.chatgpt_account_id
|
|
71
|
+
: undefined;
|
|
72
|
+
const planType = typeof authClaim?.chatgpt_plan_type === "string"
|
|
73
|
+
? authClaim.chatgpt_plan_type
|
|
74
|
+
: undefined;
|
|
75
|
+
const exp = typeof claims?.exp === "number" ? claims.exp * 1000 : undefined;
|
|
76
|
+
return { accountId, planType, expiresAt: exp };
|
|
77
|
+
}
|
|
78
|
+
/** Decode the account email from an id token (best-effort, for the label). */
|
|
79
|
+
export function decodeCodexEmail(idToken) {
|
|
80
|
+
if (!idToken) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
const parts = idToken.split(".");
|
|
84
|
+
if (parts.length !== 3) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
const claims = decodeJwtSegment(parts[1]);
|
|
88
|
+
return typeof claims?.email === "string" ? claims.email : undefined;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The chatgpt-account-id the backend requires for a given access token.
|
|
92
|
+
* Prefer the value stored on the account; fall back to decoding the JWT so a
|
|
93
|
+
* rotated token always carries a coherent account id.
|
|
94
|
+
*/
|
|
95
|
+
export function resolveCodexAccountId(accessToken, storedAccountId) {
|
|
96
|
+
if (storedAccountId && storedAccountId.length > 0) {
|
|
97
|
+
return storedAccountId;
|
|
98
|
+
}
|
|
99
|
+
return decodeCodexAccessToken(accessToken).accountId;
|
|
100
|
+
}
|
|
101
|
+
/** Build a CodexImportedCredential from raw token material. */
|
|
102
|
+
function buildImportedCredential(tokens) {
|
|
103
|
+
const decoded = decodeCodexAccessToken(tokens.access_token);
|
|
104
|
+
return {
|
|
105
|
+
accessToken: tokens.access_token,
|
|
106
|
+
refreshToken: tokens.refresh_token,
|
|
107
|
+
idToken: tokens.id_token,
|
|
108
|
+
accountId: tokens.account_id ?? decoded.accountId,
|
|
109
|
+
expiresAt: decoded.expiresAt,
|
|
110
|
+
planType: decoded.planType,
|
|
111
|
+
email: decodeCodexEmail(tokens.id_token),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Import an existing Codex credential from `~/.codex/auth.json` (or a custom
|
|
116
|
+
* path). This is the verified, robust login path: the user logs into Codex
|
|
117
|
+
* normally, then imports the current account into the proxy pool.
|
|
118
|
+
*/
|
|
119
|
+
export async function importCodexAuthFile(authFilePath = CODEX_AUTH_FILE) {
|
|
120
|
+
const raw = await readFile(authFilePath, "utf8");
|
|
121
|
+
const parsed = JSON.parse(raw);
|
|
122
|
+
const tokens = parsed.tokens;
|
|
123
|
+
if (!tokens || typeof tokens.access_token !== "string") {
|
|
124
|
+
throw new Error(`No ChatGPT OAuth tokens found in ${authFilePath}. Run \`codex login\` first.`);
|
|
125
|
+
}
|
|
126
|
+
if (parsed.auth_mode && parsed.auth_mode !== "chatgpt") {
|
|
127
|
+
throw new Error(`Codex auth mode is "${parsed.auth_mode}", expected "chatgpt". Only ChatGPT subscription login can be pooled.`);
|
|
128
|
+
}
|
|
129
|
+
return buildImportedCredential({
|
|
130
|
+
access_token: tokens.access_token,
|
|
131
|
+
refresh_token: tokens.refresh_token,
|
|
132
|
+
id_token: tokens.id_token,
|
|
133
|
+
account_id: tokens.account_id,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Refresh a Codex access token. Verified: POST form-urlencoded to
|
|
138
|
+
* auth.openai.com/oauth/token with grant_type=refresh_token and the Codex
|
|
139
|
+
* client id. Returns a fresh credential (access + rotated refresh token).
|
|
140
|
+
*/
|
|
141
|
+
export async function refreshCodexToken(refreshToken, options = {}) {
|
|
142
|
+
const body = new URLSearchParams({
|
|
143
|
+
grant_type: "refresh_token",
|
|
144
|
+
refresh_token: refreshToken,
|
|
145
|
+
client_id: CODEX_CLIENT_ID,
|
|
146
|
+
scope: CODEX_DEFAULT_SCOPES.join(" "),
|
|
147
|
+
});
|
|
148
|
+
const response = await fetch(CODEX_TOKEN_URL, {
|
|
149
|
+
method: "POST",
|
|
150
|
+
headers: {
|
|
151
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
152
|
+
Accept: "application/json",
|
|
153
|
+
"User-Agent": CODEX_USER_AGENT,
|
|
154
|
+
},
|
|
155
|
+
body: body.toString(),
|
|
156
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? 10_000),
|
|
157
|
+
});
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
const text = await response.text().catch(() => "");
|
|
160
|
+
throw Object.assign(new Error(`Codex token refresh failed: ${response.status} ${text.slice(0, 200)}`), { status: response.status });
|
|
161
|
+
}
|
|
162
|
+
const data = (await response.json());
|
|
163
|
+
if (!data.access_token) {
|
|
164
|
+
throw new Error("Codex token refresh returned no access_token");
|
|
165
|
+
}
|
|
166
|
+
logger.debug("Codex token refreshed");
|
|
167
|
+
return buildImportedCredential({
|
|
168
|
+
access_token: data.access_token,
|
|
169
|
+
// OpenAI rotates the refresh token; fall back to the old one if omitted.
|
|
170
|
+
refresh_token: data.refresh_token ?? refreshToken,
|
|
171
|
+
id_token: data.id_token,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Whether a {@link refreshCodexToken} failure means the refresh token itself is
|
|
176
|
+
* no longer usable, as opposed to the request never getting a verdict.
|
|
177
|
+
*
|
|
178
|
+
* Only the authorization server rejecting the grant is permanent. A 5xx, a
|
|
179
|
+
* timeout, or a DNS failure says nothing about the credential, and treating
|
|
180
|
+
* those as permanent disables a working account — which `auth cleanup` then
|
|
181
|
+
* deletes.
|
|
182
|
+
*/
|
|
183
|
+
export function isPermanentCodexRefreshFailure(error) {
|
|
184
|
+
const status = error?.status;
|
|
185
|
+
if (typeof status !== "number" || status < 400 || status >= 500) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
// 408 and 429 are a timeout and a throttle. Neither is the grant being
|
|
189
|
+
// rejected, and treating them as permanent disables the account — which
|
|
190
|
+
// `auth cleanup` then deletes.
|
|
191
|
+
return status !== 408 && status !== 429;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Whether an imported credential's access token is expired (or within the
|
|
195
|
+
* buffer window) and should be refreshed before use.
|
|
196
|
+
*/
|
|
197
|
+
export function codexTokenNeedsRefresh(expiresAt, bufferMs = 5 * 60 * 1000) {
|
|
198
|
+
if (!expiresAt) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
return expiresAt - bufferMs <= Date.now();
|
|
202
|
+
}
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export { ANTHROPIC_OAUTH_BASE_URL, DEFAULT_SCOPES, DEFAULT_REDIRECT_URI, DEFAULT
|
|
|
14
14
|
export { AnthropicOAuth } from "./anthropicOAuth.js";
|
|
15
15
|
export { OAuthError, OAuthConfigurationError, OAuthTokenExchangeError, OAuthTokenRefreshError, OAuthTokenValidationError, OAuthTokenRevocationError, OAuthCallbackServerError, } from "../types/index.js";
|
|
16
16
|
export { createAnthropicOAuth, createAnthropicOAuthConfig, hasAnthropicOAuthCredentials, startCallbackServer, stopCallbackServer, performOAuthFlow, } from "./anthropicOAuth.js";
|
|
17
|
+
export { CODEX_CLIENT_ID, CODEX_AUTH_URL, CODEX_TOKEN_URL, CODEX_REDIRECT_URI, CODEX_DEFAULT_SCOPES, CODEX_BACKEND_BASE_URL, CODEX_RESPONSES_URL, CODEX_USAGE_URL, CODEX_USER_AGENT, CODEX_ORIGINATOR, decodeCodexAccessToken, decodeCodexEmail, resolveCodexAccountId, importCodexAuthFile, refreshCodexToken, codexTokenNeedsRefresh, } from "./codexOAuth.js";
|
|
17
18
|
export { TokenStore, tokenStore, defaultTokenStore } from "./tokenStore.js";
|
|
18
19
|
export { TokenStoreError } from "../types/index.js";
|
|
19
20
|
export { AccountPool } from "./accountPool.js";
|
package/dist/auth/index.js
CHANGED
|
@@ -23,6 +23,10 @@ export { OAuthError, OAuthConfigurationError, OAuthTokenExchangeError, OAuthToke
|
|
|
23
23
|
export { createAnthropicOAuth, createAnthropicOAuthConfig, hasAnthropicOAuthCredentials, startCallbackServer, stopCallbackServer, performOAuthFlow, } from "./anthropicOAuth.js";
|
|
24
24
|
// OAuth types (canonical definitions in types/subscriptionTypes.ts)
|
|
25
25
|
// =============================================================================
|
|
26
|
+
// CODEX (ChatGPT) OAUTH - subscription pool support
|
|
27
|
+
// =============================================================================
|
|
28
|
+
export { CODEX_CLIENT_ID, CODEX_AUTH_URL, CODEX_TOKEN_URL, CODEX_REDIRECT_URI, CODEX_DEFAULT_SCOPES, CODEX_BACKEND_BASE_URL, CODEX_RESPONSES_URL, CODEX_USAGE_URL, CODEX_USER_AGENT, CODEX_ORIGINATOR, decodeCodexAccessToken, decodeCodexEmail, resolveCodexAccountId, importCodexAuthFile, refreshCodexToken, codexTokenNeedsRefresh, } from "./codexOAuth.js";
|
|
29
|
+
// =============================================================================
|
|
26
30
|
// TOKEN STORE - Secure Token Storage
|
|
27
31
|
// =============================================================================
|
|
28
32
|
// Main TokenStore class and instances
|