@lumifai/harness-model-codex-oauth 0.1.0
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 +124 -0
- package/dist/auth.d.ts +92 -0
- package/dist/auth.js +348 -0
- package/dist/auth.js.map +1 -0
- package/dist/gateway.d.ts +60 -0
- package/dist/gateway.js +91 -0
- package/dist/gateway.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/models.d.ts +16 -0
- package/dist/models.js +36 -0
- package/dist/models.js.map +1 -0
- package/dist/oauth-fetch.d.ts +33 -0
- package/dist/oauth-fetch.js +134 -0
- package/dist/oauth-fetch.js.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# @lumifai/harness-model-codex-oauth
|
|
2
|
+
|
|
3
|
+
Mastra model gateway that bills OpenAI requests to a ChatGPT / Codex subscription
|
|
4
|
+
(Plus, Pro, Team, Business, or Enterprise) instead of a Platform API key.
|
|
5
|
+
Registering it lets any harness model string of the form
|
|
6
|
+
`codex-oauth/openai/<model-id>` run against `chatgpt.com/backend-api/codex` with
|
|
7
|
+
a subscription OAuth token, so mode models, subagents, and the model picker all
|
|
8
|
+
work unchanged.
|
|
9
|
+
|
|
10
|
+
The gateway is a thin credential swap over the standard `@ai-sdk/openai`
|
|
11
|
+
Responses provider: streaming, tool calls, usage accounting, and reasoning are
|
|
12
|
+
handled by that provider. The harness agent loop, tool permissions, and
|
|
13
|
+
human-in-the-loop flow are untouched.
|
|
14
|
+
|
|
15
|
+
## Before you enable this
|
|
16
|
+
|
|
17
|
+
ChatGPT OAuth tokens are scoped to Codex / ChatGPT product surfaces. The
|
|
18
|
+
Platform API (`api.openai.com`) rejects them, and the Codex backend rejects
|
|
19
|
+
Platform API keys. This gateway talks to the subscription endpoint with the
|
|
20
|
+
credential written by `codex login`, which means **it presents your application
|
|
21
|
+
as a local Codex client**. Consequences you are accepting:
|
|
22
|
+
|
|
23
|
+
- It is a deliberate reuse of a subscription credential outside the Codex CLI,
|
|
24
|
+
not a supported multi-tenant configuration. The subscription account is the
|
|
25
|
+
one at risk if OpenAI objects.
|
|
26
|
+
- OpenAI can change the Codex backend check at any time and requests will start
|
|
27
|
+
failing with no deprecation path.
|
|
28
|
+
- Use this only for local or self-hosted development where each developer
|
|
29
|
+
supplies their own credential. Do not ship it as a customer-facing auth path,
|
|
30
|
+
and do not use it for production or multi-tenant traffic.
|
|
31
|
+
|
|
32
|
+
Because of that, the acknowledgement is a required constructor argument rather
|
|
33
|
+
than an option with a default:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
createCodexOAuthGateway({ acknowledgeCodexSubscription: true });
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
For anything customer-facing, use a normal `OPENAI_API_KEY` with the built-in
|
|
40
|
+
`openai/*` models instead.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pnpm add @lumifai/harness-model-codex-oauth
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Get credentials
|
|
49
|
+
|
|
50
|
+
Requires a ChatGPT plan that includes Codex. Sign in once with the Codex CLI:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
codex login
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
That writes `~/.codex/auth.json` (access token, refresh token, account id). The
|
|
57
|
+
gateway reads that file by default and refreshes the access token in place when
|
|
58
|
+
it is near expiry. No env var export is required for the common local path.
|
|
59
|
+
|
|
60
|
+
Fallback when no auth file is present (typical in CI). To use a non-default
|
|
61
|
+
auth file path, set `CODEX_HOME` instead — env tokens do not override an
|
|
62
|
+
existing `auth.json`:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
export CODEX_OAUTH_ACCESS_TOKEN='…'
|
|
66
|
+
export CODEX_OAUTH_ACCOUNT_ID='…' # or embed the chatgpt_account_id claim in the JWT
|
|
67
|
+
export CODEX_OAUTH_REFRESH_TOKEN='…' # optional; enables refresh without the auth file
|
|
68
|
+
# or point at a non-default Codex home:
|
|
69
|
+
export CODEX_HOME='/path/to/codex-home'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Treat these as secrets. The gateway resolves credentials per request, so a
|
|
73
|
+
rotated token is picked up without a restart.
|
|
74
|
+
|
|
75
|
+
## Register the gateway
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { createHarnessSessionManager } from '@lumifai/harness-server';
|
|
79
|
+
import { createCodexOAuthGateway } from '@lumifai/harness-model-codex-oauth';
|
|
80
|
+
|
|
81
|
+
const manager = createHarnessSessionManager({
|
|
82
|
+
bootConfig: {
|
|
83
|
+
gateways: [createCodexOAuthGateway({ acknowledgeCodexSubscription: true })],
|
|
84
|
+
|
|
85
|
+
planModel: 'codex-oauth/openai/gpt-5.4',
|
|
86
|
+
buildModel: 'codex-oauth/openai/gpt-5.4',
|
|
87
|
+
|
|
88
|
+
workspaceBasePath: './workspace',
|
|
89
|
+
workspaceMode: 'local',
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The gateway claims only `codex-oauth/…` model ids, so bare `openai/…` ids keep
|
|
95
|
+
routing to the API-key provider and both can coexist in one process.
|
|
96
|
+
|
|
97
|
+
The advertised catalog is static, so model resolution works offline and issues
|
|
98
|
+
no network call at boot. Pass `models` to advertise a different set:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
createCodexOAuthGateway({
|
|
102
|
+
acknowledgeCodexSubscription: true,
|
|
103
|
+
models: ['gpt-5.6-luna', 'gpt-5.4', 'gpt-5.4-mini'],
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Other options: `resolveCredentials` replaces the auth-file / env lookup (for a
|
|
108
|
+
secret manager), `baseURL` points at a proxy in front of the Codex backend, and
|
|
109
|
+
`originator` overrides the upstream identity header (defaults to `codex_cli_rs`).
|
|
110
|
+
The subscription bearer token is sent to whatever host `baseURL` resolves to —
|
|
111
|
+
only use a proxy you trust.
|
|
112
|
+
|
|
113
|
+
## Notes
|
|
114
|
+
|
|
115
|
+
- **Rate limits are your subscription's.** Quotas are far tighter than
|
|
116
|
+
pay-as-you-go API limits and are shared with your own Codex CLI usage. Expect
|
|
117
|
+
rate-limit errors under concurrent load.
|
|
118
|
+
- **Responses API only.** The Codex ChatGPT backend speaks
|
|
119
|
+
`/backend-api/codex/responses`, not Chat Completions. The gateway configures
|
|
120
|
+
`@ai-sdk/openai` accordingly.
|
|
121
|
+
- **API credentials do not interfere.** This gateway never reads
|
|
122
|
+
`OPENAI_API_KEY`, so there is no credential precedence to unset.
|
|
123
|
+
- **Not multi-tenant.** One local credential per process. Do not pool or
|
|
124
|
+
redistribute tokens across users.
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loads ChatGPT / Codex subscription OAuth credentials for local use.
|
|
3
|
+
*
|
|
4
|
+
* Default source is the auth file written by `codex login`
|
|
5
|
+
* (`~/.codex/auth.json`). Tokens expire, so callers should run them through
|
|
6
|
+
* {@link ensureFreshCodexCredentials} before each request.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CODEX_OAUTH_ACCESS_TOKEN_ENV_VAR = "CODEX_OAUTH_ACCESS_TOKEN";
|
|
9
|
+
export declare const CODEX_OAUTH_ACCOUNT_ID_ENV_VAR = "CODEX_OAUTH_ACCOUNT_ID";
|
|
10
|
+
export declare const CODEX_OAUTH_REFRESH_TOKEN_ENV_VAR = "CODEX_OAUTH_REFRESH_TOKEN";
|
|
11
|
+
/** Codex CLI's public OAuth client id (not a secret). */
|
|
12
|
+
export declare const CODEX_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
13
|
+
export declare const CODEX_OAUTH_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
14
|
+
/** Refresh when the access token is within this many ms of expiry. */
|
|
15
|
+
export declare const CODEX_OAUTH_REFRESH_MARGIN_MS: number;
|
|
16
|
+
export type CodexOAuthCredentials = {
|
|
17
|
+
accessToken: string;
|
|
18
|
+
accountId: string;
|
|
19
|
+
refreshToken?: string;
|
|
20
|
+
idToken?: string;
|
|
21
|
+
/** Absolute expiry time of the access token, in epoch milliseconds. */
|
|
22
|
+
expiresAt?: number;
|
|
23
|
+
/** Path of the auth file these credentials were loaded from, if any. */
|
|
24
|
+
authFilePath?: string;
|
|
25
|
+
};
|
|
26
|
+
export type CodexOAuthCredentialsResolver = () => CodexOAuthCredentials | undefined | Promise<CodexOAuthCredentials | undefined>;
|
|
27
|
+
/** Best-effort JWT payload decode — no signature verification. */
|
|
28
|
+
export declare function parseJwtClaims(token: string): Record<string, unknown> | undefined;
|
|
29
|
+
export declare function deriveAccountId(...tokens: Array<string | undefined>): string | undefined;
|
|
30
|
+
export declare function deriveExpiresAtMs(token: string): number | undefined;
|
|
31
|
+
/** Candidate auth.json paths, matching Codex / ChatGPT local CLI conventions. */
|
|
32
|
+
export declare function resolveCodexAuthFileCandidates(env?: NodeJS.ProcessEnv, home?: string): string[];
|
|
33
|
+
export declare function loadCodexAuthFile(path: string): (CodexOAuthCredentials & {
|
|
34
|
+
authFilePath: string;
|
|
35
|
+
}) | undefined;
|
|
36
|
+
export declare function resolveCodexCredentialsFromEnv(env?: NodeJS.ProcessEnv): CodexOAuthCredentials | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Default credential lookup: first existing Codex auth file, then env fallback
|
|
39
|
+
* when no auth file is present. (`CODEX_HOME` covers a non-default auth file path.)
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveCodexCredentials(env?: NodeJS.ProcessEnv, home?: string): CodexOAuthCredentials | undefined;
|
|
42
|
+
export declare function needsCodexTokenRefresh(credentials: CodexOAuthCredentials, now?: number, marginMs?: number): boolean;
|
|
43
|
+
export type RefreshCodexTokensOptions = {
|
|
44
|
+
refreshToken: string;
|
|
45
|
+
/** Fallback when the refreshed tokens omit `chatgpt_account_id`. */
|
|
46
|
+
accountId?: string;
|
|
47
|
+
fetchImpl?: typeof fetch;
|
|
48
|
+
clientId?: string;
|
|
49
|
+
tokenUrl?: string;
|
|
50
|
+
now?: () => number;
|
|
51
|
+
};
|
|
52
|
+
export declare function refreshCodexTokens(options: RefreshCodexTokensOptions): Promise<CodexOAuthCredentials | undefined>;
|
|
53
|
+
/** Writes refreshed tokens back to a Codex-compatible auth.json (mode 0600). */
|
|
54
|
+
export declare function persistCodexAuthFile(path: string, credentials: CodexOAuthCredentials): void;
|
|
55
|
+
export type EnsureFreshCodexCredentialsOptions = {
|
|
56
|
+
credentials: CodexOAuthCredentials;
|
|
57
|
+
fetchImpl?: typeof fetch;
|
|
58
|
+
now?: () => number;
|
|
59
|
+
/** Persist refreshed tokens. Defaults to writing `credentials.authFilePath` when set. */
|
|
60
|
+
persist?: (credentials: CodexOAuthCredentials) => void | Promise<void>;
|
|
61
|
+
refreshMarginMs?: number;
|
|
62
|
+
/** Ignore expiry margin and refresh whenever a refresh token is available. */
|
|
63
|
+
forceRefresh?: boolean;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Returns credentials that are not near expiry, refreshing via the Codex OAuth
|
|
67
|
+
* client when a refresh token is available.
|
|
68
|
+
*/
|
|
69
|
+
export declare function ensureFreshCodexCredentials(options: EnsureFreshCodexCredentialsOptions): Promise<CodexOAuthCredentials>;
|
|
70
|
+
/**
|
|
71
|
+
* Prefer an in-memory refreshed snapshot over a stale env/file reload of the
|
|
72
|
+
* same account so env-only credentials do not re-refresh on every request.
|
|
73
|
+
*/
|
|
74
|
+
export declare function mergeCredentialSnapshot(cached: CodexOAuthCredentials | undefined, resolved: CodexOAuthCredentials): CodexOAuthCredentials;
|
|
75
|
+
export type LiveCodexCredentialsResolver = {
|
|
76
|
+
resolve: (options?: {
|
|
77
|
+
forceRefresh?: boolean;
|
|
78
|
+
}) => Promise<CodexOAuthCredentials>;
|
|
79
|
+
};
|
|
80
|
+
export type CreateLiveCodexCredentialsResolverOptions = {
|
|
81
|
+
resolveCredentials?: CodexOAuthCredentialsResolver;
|
|
82
|
+
fetchImpl?: typeof fetch;
|
|
83
|
+
now?: () => number;
|
|
84
|
+
persist?: EnsureFreshCodexCredentialsOptions['persist'];
|
|
85
|
+
refreshMarginMs?: number;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Per-gateway credential cache with in-flight refresh dedup. Concurrent callers
|
|
89
|
+
* share one refresh; sequential callers reuse the cached access token until it
|
|
90
|
+
* nears expiry (including the env-var path with no auth file to persist to).
|
|
91
|
+
*/
|
|
92
|
+
export declare function createLiveCodexCredentialsResolver(options?: CreateLiveCodexCredentialsResolverOptions): LiveCodexCredentialsResolver;
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Loads ChatGPT / Codex subscription OAuth credentials for local use.
|
|
4
|
+
*
|
|
5
|
+
* Default source is the auth file written by `codex login`
|
|
6
|
+
* (`~/.codex/auth.json`). Tokens expire, so callers should run them through
|
|
7
|
+
* {@link ensureFreshCodexCredentials} before each request.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.CODEX_OAUTH_REFRESH_MARGIN_MS = exports.CODEX_OAUTH_TOKEN_URL = exports.CODEX_OAUTH_CLIENT_ID = exports.CODEX_OAUTH_REFRESH_TOKEN_ENV_VAR = exports.CODEX_OAUTH_ACCOUNT_ID_ENV_VAR = exports.CODEX_OAUTH_ACCESS_TOKEN_ENV_VAR = void 0;
|
|
11
|
+
exports.parseJwtClaims = parseJwtClaims;
|
|
12
|
+
exports.deriveAccountId = deriveAccountId;
|
|
13
|
+
exports.deriveExpiresAtMs = deriveExpiresAtMs;
|
|
14
|
+
exports.resolveCodexAuthFileCandidates = resolveCodexAuthFileCandidates;
|
|
15
|
+
exports.loadCodexAuthFile = loadCodexAuthFile;
|
|
16
|
+
exports.resolveCodexCredentialsFromEnv = resolveCodexCredentialsFromEnv;
|
|
17
|
+
exports.resolveCodexCredentials = resolveCodexCredentials;
|
|
18
|
+
exports.needsCodexTokenRefresh = needsCodexTokenRefresh;
|
|
19
|
+
exports.refreshCodexTokens = refreshCodexTokens;
|
|
20
|
+
exports.persistCodexAuthFile = persistCodexAuthFile;
|
|
21
|
+
exports.ensureFreshCodexCredentials = ensureFreshCodexCredentials;
|
|
22
|
+
exports.mergeCredentialSnapshot = mergeCredentialSnapshot;
|
|
23
|
+
exports.createLiveCodexCredentialsResolver = createLiveCodexCredentialsResolver;
|
|
24
|
+
const node_os_1 = require("node:os");
|
|
25
|
+
const node_path_1 = require("node:path");
|
|
26
|
+
const node_fs_1 = require("node:fs");
|
|
27
|
+
exports.CODEX_OAUTH_ACCESS_TOKEN_ENV_VAR = 'CODEX_OAUTH_ACCESS_TOKEN';
|
|
28
|
+
exports.CODEX_OAUTH_ACCOUNT_ID_ENV_VAR = 'CODEX_OAUTH_ACCOUNT_ID';
|
|
29
|
+
exports.CODEX_OAUTH_REFRESH_TOKEN_ENV_VAR = 'CODEX_OAUTH_REFRESH_TOKEN';
|
|
30
|
+
/** Codex CLI's public OAuth client id (not a secret). */
|
|
31
|
+
exports.CODEX_OAUTH_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';
|
|
32
|
+
exports.CODEX_OAUTH_TOKEN_URL = 'https://auth.openai.com/oauth/token';
|
|
33
|
+
/** Refresh when the access token is within this many ms of expiry. */
|
|
34
|
+
exports.CODEX_OAUTH_REFRESH_MARGIN_MS = 5 * 60 * 1000;
|
|
35
|
+
function isRecord(value) {
|
|
36
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
37
|
+
}
|
|
38
|
+
/** Best-effort JWT payload decode — no signature verification. */
|
|
39
|
+
function parseJwtClaims(token) {
|
|
40
|
+
const parts = token.split('.');
|
|
41
|
+
if (parts.length !== 3 || !parts[1])
|
|
42
|
+
return undefined;
|
|
43
|
+
try {
|
|
44
|
+
const padded = parts[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
45
|
+
const json = Buffer.from(padded + '='.repeat((4 - (padded.length % 4)) % 4), 'base64').toString('utf8');
|
|
46
|
+
const parsed = JSON.parse(json);
|
|
47
|
+
return isRecord(parsed) ? parsed : undefined;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function deriveAccountId(...tokens) {
|
|
54
|
+
for (const token of tokens) {
|
|
55
|
+
if (!token)
|
|
56
|
+
continue;
|
|
57
|
+
const claims = parseJwtClaims(token);
|
|
58
|
+
const auth = claims?.['https://api.openai.com/auth'];
|
|
59
|
+
if (isRecord(auth) && typeof auth.chatgpt_account_id === 'string' && auth.chatgpt_account_id) {
|
|
60
|
+
return auth.chatgpt_account_id;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
function deriveExpiresAtMs(token) {
|
|
66
|
+
const claims = parseJwtClaims(token);
|
|
67
|
+
return typeof claims?.exp === 'number' ? claims.exp * 1000 : undefined;
|
|
68
|
+
}
|
|
69
|
+
/** Candidate auth.json paths, matching Codex / ChatGPT local CLI conventions. */
|
|
70
|
+
function resolveCodexAuthFileCandidates(env = process.env, home = (0, node_os_1.homedir)()) {
|
|
71
|
+
const paths = [];
|
|
72
|
+
if (env.CODEX_HOME)
|
|
73
|
+
paths.push((0, node_path_1.join)(env.CODEX_HOME, 'auth.json'));
|
|
74
|
+
if (env.CHATGPT_LOCAL_HOME)
|
|
75
|
+
paths.push((0, node_path_1.join)(env.CHATGPT_LOCAL_HOME, 'auth.json'));
|
|
76
|
+
paths.push((0, node_path_1.join)(home, '.codex', 'auth.json'));
|
|
77
|
+
paths.push((0, node_path_1.join)(home, '.chatgpt-local', 'auth.json'));
|
|
78
|
+
return paths;
|
|
79
|
+
}
|
|
80
|
+
function loadCodexAuthFile(path) {
|
|
81
|
+
if (!(0, node_fs_1.existsSync)(path))
|
|
82
|
+
return undefined;
|
|
83
|
+
let raw;
|
|
84
|
+
try {
|
|
85
|
+
raw = JSON.parse((0, node_fs_1.readFileSync)(path, 'utf8'));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
if (!isRecord(raw) || !isRecord(raw.tokens))
|
|
91
|
+
return undefined;
|
|
92
|
+
const tokens = raw.tokens;
|
|
93
|
+
const accessToken = tokens?.access_token?.trim();
|
|
94
|
+
if (!accessToken)
|
|
95
|
+
return undefined;
|
|
96
|
+
const accountId = tokens?.account_id?.trim() || deriveAccountId(tokens?.id_token, accessToken);
|
|
97
|
+
if (!accountId)
|
|
98
|
+
return undefined;
|
|
99
|
+
return {
|
|
100
|
+
accessToken,
|
|
101
|
+
accountId,
|
|
102
|
+
refreshToken: tokens?.refresh_token?.trim() || undefined,
|
|
103
|
+
idToken: tokens?.id_token?.trim() || undefined,
|
|
104
|
+
expiresAt: deriveExpiresAtMs(accessToken),
|
|
105
|
+
authFilePath: path,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function resolveCodexCredentialsFromEnv(env = process.env) {
|
|
109
|
+
const accessToken = env[exports.CODEX_OAUTH_ACCESS_TOKEN_ENV_VAR]?.trim();
|
|
110
|
+
if (!accessToken)
|
|
111
|
+
return undefined;
|
|
112
|
+
const accountId = env[exports.CODEX_OAUTH_ACCOUNT_ID_ENV_VAR]?.trim() || deriveAccountId(accessToken);
|
|
113
|
+
if (!accountId)
|
|
114
|
+
return undefined;
|
|
115
|
+
return {
|
|
116
|
+
accessToken,
|
|
117
|
+
accountId,
|
|
118
|
+
refreshToken: env[exports.CODEX_OAUTH_REFRESH_TOKEN_ENV_VAR]?.trim() || undefined,
|
|
119
|
+
expiresAt: deriveExpiresAtMs(accessToken),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Default credential lookup: first existing Codex auth file, then env fallback
|
|
124
|
+
* when no auth file is present. (`CODEX_HOME` covers a non-default auth file path.)
|
|
125
|
+
*/
|
|
126
|
+
function resolveCodexCredentials(env = process.env, home = (0, node_os_1.homedir)()) {
|
|
127
|
+
for (const path of resolveCodexAuthFileCandidates(env, home)) {
|
|
128
|
+
const fromFile = loadCodexAuthFile(path);
|
|
129
|
+
if (fromFile)
|
|
130
|
+
return fromFile;
|
|
131
|
+
}
|
|
132
|
+
return resolveCodexCredentialsFromEnv(env);
|
|
133
|
+
}
|
|
134
|
+
function needsCodexTokenRefresh(credentials, now = Date.now(), marginMs = exports.CODEX_OAUTH_REFRESH_MARGIN_MS) {
|
|
135
|
+
const expiresAt = credentials.expiresAt ?? deriveExpiresAtMs(credentials.accessToken);
|
|
136
|
+
if (expiresAt == null || expiresAt <= 0)
|
|
137
|
+
return false;
|
|
138
|
+
return expiresAt <= now + marginMs;
|
|
139
|
+
}
|
|
140
|
+
async function refreshCodexTokens(options) {
|
|
141
|
+
const doFetch = options.fetchImpl ?? globalThis.fetch;
|
|
142
|
+
const response = await doFetch(options.tokenUrl ?? exports.CODEX_OAUTH_TOKEN_URL, {
|
|
143
|
+
method: 'POST',
|
|
144
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
145
|
+
body: new URLSearchParams({
|
|
146
|
+
grant_type: 'refresh_token',
|
|
147
|
+
refresh_token: options.refreshToken,
|
|
148
|
+
client_id: options.clientId ?? exports.CODEX_OAUTH_CLIENT_ID,
|
|
149
|
+
scope: 'openid profile email offline_access',
|
|
150
|
+
}).toString(),
|
|
151
|
+
});
|
|
152
|
+
if (!response.ok)
|
|
153
|
+
return undefined;
|
|
154
|
+
const payload = (await response.json());
|
|
155
|
+
if (!payload.access_token)
|
|
156
|
+
return undefined;
|
|
157
|
+
const now = options.now?.() ?? Date.now();
|
|
158
|
+
const accountId = deriveAccountId(payload.id_token, payload.access_token) ?? options.accountId;
|
|
159
|
+
if (!accountId)
|
|
160
|
+
return undefined;
|
|
161
|
+
return {
|
|
162
|
+
accessToken: payload.access_token,
|
|
163
|
+
refreshToken: payload.refresh_token ?? options.refreshToken,
|
|
164
|
+
idToken: payload.id_token,
|
|
165
|
+
accountId,
|
|
166
|
+
expiresAt: now + (payload.expires_in ?? 3600) * 1000,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/** Writes refreshed tokens back to a Codex-compatible auth.json (mode 0600). */
|
|
170
|
+
function persistCodexAuthFile(path, credentials) {
|
|
171
|
+
let existing = {};
|
|
172
|
+
if ((0, node_fs_1.existsSync)(path)) {
|
|
173
|
+
try {
|
|
174
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)(path, 'utf8'));
|
|
175
|
+
if (isRecord(parsed))
|
|
176
|
+
existing = parsed;
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
existing = {};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
|
|
184
|
+
}
|
|
185
|
+
const next = {
|
|
186
|
+
...existing,
|
|
187
|
+
auth_mode: existing.auth_mode ?? 'chatgpt',
|
|
188
|
+
tokens: {
|
|
189
|
+
...(isRecord(existing.tokens) ? existing.tokens : {}),
|
|
190
|
+
access_token: credentials.accessToken,
|
|
191
|
+
account_id: credentials.accountId,
|
|
192
|
+
// Only overwrite optional fields when present — a refresh_token grant often
|
|
193
|
+
// omits id_token, and JSON.stringify would otherwise delete the existing key.
|
|
194
|
+
...(credentials.refreshToken ? { refresh_token: credentials.refreshToken } : {}),
|
|
195
|
+
...(credentials.idToken ? { id_token: credentials.idToken } : {}),
|
|
196
|
+
},
|
|
197
|
+
last_refresh: new Date().toISOString(),
|
|
198
|
+
};
|
|
199
|
+
const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
200
|
+
try {
|
|
201
|
+
(0, node_fs_1.writeFileSync)(tmpPath, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
|
|
202
|
+
try {
|
|
203
|
+
(0, node_fs_1.chmodSync)(tmpPath, 0o600);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// Best-effort on platforms that ignore mode.
|
|
207
|
+
}
|
|
208
|
+
(0, node_fs_1.renameSync)(tmpPath, path);
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
try {
|
|
212
|
+
(0, node_fs_1.unlinkSync)(tmpPath);
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Ignore cleanup failures.
|
|
216
|
+
}
|
|
217
|
+
throw error;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Returns credentials that are not near expiry, refreshing via the Codex OAuth
|
|
222
|
+
* client when a refresh token is available.
|
|
223
|
+
*/
|
|
224
|
+
async function ensureFreshCodexCredentials(options) {
|
|
225
|
+
const { credentials } = options;
|
|
226
|
+
const now = options.now?.() ?? Date.now();
|
|
227
|
+
const shouldRefresh = options.forceRefresh || needsCodexTokenRefresh(credentials, now, options.refreshMarginMs);
|
|
228
|
+
if (!shouldRefresh) {
|
|
229
|
+
return credentials;
|
|
230
|
+
}
|
|
231
|
+
if (!credentials.refreshToken) {
|
|
232
|
+
if (options.forceRefresh || needsCodexTokenRefresh(credentials, now, 0)) {
|
|
233
|
+
throw new Error('Codex subscription OAuth token expired and no refresh token is available. Run `codex login`.');
|
|
234
|
+
}
|
|
235
|
+
return credentials;
|
|
236
|
+
}
|
|
237
|
+
const refreshed = await refreshCodexTokens({
|
|
238
|
+
refreshToken: credentials.refreshToken,
|
|
239
|
+
accountId: credentials.accountId,
|
|
240
|
+
fetchImpl: options.fetchImpl,
|
|
241
|
+
now: options.now,
|
|
242
|
+
});
|
|
243
|
+
if (!refreshed) {
|
|
244
|
+
if (options.forceRefresh || needsCodexTokenRefresh(credentials, now, 0)) {
|
|
245
|
+
throw new Error('Codex subscription OAuth token expired and refresh failed. Run `codex login`.');
|
|
246
|
+
}
|
|
247
|
+
return credentials;
|
|
248
|
+
}
|
|
249
|
+
const next = {
|
|
250
|
+
...refreshed,
|
|
251
|
+
// Preserve an existing id_token when the refresh response omitted one.
|
|
252
|
+
idToken: refreshed.idToken ?? credentials.idToken,
|
|
253
|
+
authFilePath: credentials.authFilePath,
|
|
254
|
+
};
|
|
255
|
+
if (options.persist) {
|
|
256
|
+
await options.persist(next);
|
|
257
|
+
}
|
|
258
|
+
else if (credentials.authFilePath) {
|
|
259
|
+
persistCodexAuthFile(credentials.authFilePath, next);
|
|
260
|
+
}
|
|
261
|
+
return next;
|
|
262
|
+
}
|
|
263
|
+
function credentialExpiryMs(credentials) {
|
|
264
|
+
return credentials.expiresAt ?? deriveExpiresAtMs(credentials.accessToken) ?? 0;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Prefer an in-memory refreshed snapshot over a stale env/file reload of the
|
|
268
|
+
* same account so env-only credentials do not re-refresh on every request.
|
|
269
|
+
*/
|
|
270
|
+
function mergeCredentialSnapshot(cached, resolved) {
|
|
271
|
+
if (!cached)
|
|
272
|
+
return resolved;
|
|
273
|
+
if (cached.accountId !== resolved.accountId)
|
|
274
|
+
return resolved;
|
|
275
|
+
if (cached.authFilePath &&
|
|
276
|
+
resolved.authFilePath &&
|
|
277
|
+
cached.authFilePath !== resolved.authFilePath) {
|
|
278
|
+
return resolved;
|
|
279
|
+
}
|
|
280
|
+
if (credentialExpiryMs(cached) < credentialExpiryMs(resolved)) {
|
|
281
|
+
return {
|
|
282
|
+
...resolved,
|
|
283
|
+
idToken: resolved.idToken ?? cached.idToken,
|
|
284
|
+
refreshToken: resolved.refreshToken ?? cached.refreshToken,
|
|
285
|
+
authFilePath: resolved.authFilePath ?? cached.authFilePath,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
...resolved,
|
|
290
|
+
accessToken: cached.accessToken,
|
|
291
|
+
refreshToken: cached.refreshToken ?? resolved.refreshToken,
|
|
292
|
+
idToken: cached.idToken ?? resolved.idToken,
|
|
293
|
+
expiresAt: cached.expiresAt ?? resolved.expiresAt,
|
|
294
|
+
authFilePath: resolved.authFilePath ?? cached.authFilePath,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Per-gateway credential cache with in-flight refresh dedup. Concurrent callers
|
|
299
|
+
* share one refresh; sequential callers reuse the cached access token until it
|
|
300
|
+
* nears expiry (including the env-var path with no auth file to persist to).
|
|
301
|
+
*/
|
|
302
|
+
function createLiveCodexCredentialsResolver(options = {}) {
|
|
303
|
+
let cached;
|
|
304
|
+
let inflight;
|
|
305
|
+
let inflightForce = false;
|
|
306
|
+
return {
|
|
307
|
+
async resolve({ forceRefresh = false } = {}) {
|
|
308
|
+
if (inflight && (!forceRefresh || inflightForce)) {
|
|
309
|
+
return inflight;
|
|
310
|
+
}
|
|
311
|
+
const previous = inflight;
|
|
312
|
+
const run = (async () => {
|
|
313
|
+
if (previous) {
|
|
314
|
+
try {
|
|
315
|
+
await previous;
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
// Prior attempt failed; continue with a fresh resolve.
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
const resolved = await (options.resolveCredentials ?? resolveCodexCredentials)();
|
|
322
|
+
if (!resolved) {
|
|
323
|
+
throw new Error('Codex subscription OAuth credentials missing. Run `codex login` (writes ~/.codex/auth.json) or export CODEX_OAUTH_ACCESS_TOKEN and CODEX_OAUTH_ACCOUNT_ID.');
|
|
324
|
+
}
|
|
325
|
+
const candidate = mergeCredentialSnapshot(cached, resolved);
|
|
326
|
+
const fresh = await ensureFreshCodexCredentials({
|
|
327
|
+
credentials: candidate,
|
|
328
|
+
fetchImpl: options.fetchImpl,
|
|
329
|
+
now: options.now,
|
|
330
|
+
persist: options.persist,
|
|
331
|
+
refreshMarginMs: options.refreshMarginMs,
|
|
332
|
+
forceRefresh,
|
|
333
|
+
});
|
|
334
|
+
cached = fresh;
|
|
335
|
+
return fresh;
|
|
336
|
+
})().finally(() => {
|
|
337
|
+
if (inflight === run) {
|
|
338
|
+
inflight = undefined;
|
|
339
|
+
inflightForce = false;
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
inflight = run;
|
|
343
|
+
inflightForce = forceRefresh;
|
|
344
|
+
return run;
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { MastraModelGateway } from '@mastra/core/llm';
|
|
2
|
+
import type { GatewayLanguageModel, ProviderConfig } from '@mastra/core/llm';
|
|
3
|
+
import { type CodexOAuthCredentialsResolver } from './auth';
|
|
4
|
+
import { type CodexOAuthModelId } from './models';
|
|
5
|
+
import { type CreateCodexOAuthFetchOptions } from './oauth-fetch';
|
|
6
|
+
export type CodexOAuthGatewayOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* Required, explicit opt-in. This gateway bills ChatGPT / Codex subscription
|
|
9
|
+
* quotas via OAuth credentials intended for the Codex CLI. Only enable it for
|
|
10
|
+
* local/self-hosted use where each developer supplies their own credential;
|
|
11
|
+
* never as a customer-facing or multi-tenant auth path. See README
|
|
12
|
+
* "Before you enable this".
|
|
13
|
+
*/
|
|
14
|
+
acknowledgeCodexSubscription: true;
|
|
15
|
+
/** Model ids to advertise under `codex-oauth/openai/<id>`. */
|
|
16
|
+
models?: ReadonlyArray<CodexOAuthModelId>;
|
|
17
|
+
/**
|
|
18
|
+
* Overrides the default `~/.codex/auth.json` / env lookup. Invoked on every
|
|
19
|
+
* outbound Codex request so a rotated token is picked up without resolving a
|
|
20
|
+
* new language model.
|
|
21
|
+
*/
|
|
22
|
+
resolveCredentials?: CodexOAuthCredentialsResolver;
|
|
23
|
+
/**
|
|
24
|
+
* Overrides the Codex Responses base URL (proxy in front of it). The
|
|
25
|
+
* subscription OAuth bearer token is sent to whatever host this resolves to —
|
|
26
|
+
* only point it at a proxy you trust.
|
|
27
|
+
*/
|
|
28
|
+
baseURL?: string;
|
|
29
|
+
/** Upstream `originator` header. Pass `false` to omit. */
|
|
30
|
+
originator?: CreateCodexOAuthFetchOptions['originator'];
|
|
31
|
+
/** Injected in tests. */
|
|
32
|
+
fetchImpl?: typeof fetch;
|
|
33
|
+
};
|
|
34
|
+
export declare function createCodexOAuthGateway(options: CodexOAuthGatewayOptions): CodexOAuthGateway;
|
|
35
|
+
export declare class CodexOAuthGateway extends MastraModelGateway {
|
|
36
|
+
readonly id: "codex-oauth";
|
|
37
|
+
readonly name = "Codex subscription (OAuth)";
|
|
38
|
+
private readonly models;
|
|
39
|
+
private readonly resolveCredentials;
|
|
40
|
+
private readonly liveCredentials;
|
|
41
|
+
private readonly oauthFetch;
|
|
42
|
+
private readonly baseURL;
|
|
43
|
+
private readonly originator;
|
|
44
|
+
private readonly fetchImpl?;
|
|
45
|
+
constructor(options: CodexOAuthGatewayOptions);
|
|
46
|
+
/**
|
|
47
|
+
* Claims only ids carrying this gateway's own prefix. Bare `openai/*` ids
|
|
48
|
+
* keep routing to the API-key provider so both can coexist in one process.
|
|
49
|
+
*/
|
|
50
|
+
handlesModel(modelId: string): boolean;
|
|
51
|
+
fetchProviders(): Promise<Record<string, ProviderConfig>>;
|
|
52
|
+
buildUrl(): Promise<string | undefined>;
|
|
53
|
+
getApiKey(): Promise<string>;
|
|
54
|
+
resolveLanguageModel(args: {
|
|
55
|
+
modelId: string;
|
|
56
|
+
providerId: string;
|
|
57
|
+
apiKey: string;
|
|
58
|
+
headers?: Record<string, string>;
|
|
59
|
+
}): Promise<GatewayLanguageModel>;
|
|
60
|
+
}
|
package/dist/gateway.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CodexOAuthGateway = void 0;
|
|
4
|
+
exports.createCodexOAuthGateway = createCodexOAuthGateway;
|
|
5
|
+
const openai_1 = require("@ai-sdk/openai");
|
|
6
|
+
const llm_1 = require("@mastra/core/llm");
|
|
7
|
+
const auth_1 = require("./auth");
|
|
8
|
+
const models_1 = require("./models");
|
|
9
|
+
const oauth_fetch_1 = require("./oauth-fetch");
|
|
10
|
+
function createCodexOAuthGateway(options) {
|
|
11
|
+
return new CodexOAuthGateway(options);
|
|
12
|
+
}
|
|
13
|
+
class CodexOAuthGateway extends llm_1.MastraModelGateway {
|
|
14
|
+
id = models_1.CODEX_OAUTH_GATEWAY_ID;
|
|
15
|
+
name = 'Codex subscription (OAuth)';
|
|
16
|
+
models;
|
|
17
|
+
resolveCredentials;
|
|
18
|
+
liveCredentials;
|
|
19
|
+
oauthFetch;
|
|
20
|
+
baseURL;
|
|
21
|
+
originator;
|
|
22
|
+
fetchImpl;
|
|
23
|
+
constructor(options) {
|
|
24
|
+
super();
|
|
25
|
+
if (options?.acknowledgeCodexSubscription !== true) {
|
|
26
|
+
throw new Error('CodexOAuthGateway requires `acknowledgeCodexSubscription: true`. Subscription OAuth tokens are only for local Codex use; see the package README before enabling this.');
|
|
27
|
+
}
|
|
28
|
+
this.models = options.models?.length ? [...options.models] : models_1.DEFAULT_CODEX_OAUTH_MODELS;
|
|
29
|
+
this.resolveCredentials = options.resolveCredentials ?? auth_1.resolveCodexCredentials;
|
|
30
|
+
this.baseURL = options.baseURL ?? oauth_fetch_1.CODEX_RESPONSES_BASE_URL;
|
|
31
|
+
this.originator = options.originator;
|
|
32
|
+
this.fetchImpl = options.fetchImpl;
|
|
33
|
+
this.liveCredentials = (0, auth_1.createLiveCodexCredentialsResolver)({
|
|
34
|
+
resolveCredentials: () => this.resolveCredentials(),
|
|
35
|
+
fetchImpl: this.fetchImpl,
|
|
36
|
+
});
|
|
37
|
+
this.oauthFetch = (0, oauth_fetch_1.createCodexOAuthFetch)({
|
|
38
|
+
liveCredentials: this.liveCredentials,
|
|
39
|
+
originator: this.originator,
|
|
40
|
+
fetchImpl: this.fetchImpl,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Claims only ids carrying this gateway's own prefix. Bare `openai/*` ids
|
|
45
|
+
* keep routing to the API-key provider so both can coexist in one process.
|
|
46
|
+
*/
|
|
47
|
+
handlesModel(modelId) {
|
|
48
|
+
return modelId.startsWith(`${this.id}/`);
|
|
49
|
+
}
|
|
50
|
+
async fetchProviders() {
|
|
51
|
+
return {
|
|
52
|
+
[models_1.CODEX_OAUTH_PROVIDER_ID]: {
|
|
53
|
+
name: 'OpenAI (Codex subscription)',
|
|
54
|
+
apiKeyEnvVar: auth_1.CODEX_OAUTH_ACCESS_TOKEN_ENV_VAR,
|
|
55
|
+
gateway: this.id,
|
|
56
|
+
models: [...this.models],
|
|
57
|
+
docUrl: 'https://developers.openai.com/codex/',
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async buildUrl() {
|
|
62
|
+
// The OpenAI provider owns its own URL from `baseURL`; nothing to build here.
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
async getApiKey() {
|
|
66
|
+
const fresh = await this.liveCredentials.resolve();
|
|
67
|
+
return fresh.accessToken;
|
|
68
|
+
}
|
|
69
|
+
async resolveLanguageModel(args) {
|
|
70
|
+
if (args.providerId !== models_1.CODEX_OAUTH_PROVIDER_ID) {
|
|
71
|
+
throw new Error(`CodexOAuthGateway only supports providerId="${models_1.CODEX_OAUTH_PROVIDER_ID}" (got ${args.providerId})`);
|
|
72
|
+
}
|
|
73
|
+
const openai = (0, openai_1.createOpenAI)({
|
|
74
|
+
// The bearer token is applied by the fetch wrapper. This placeholder only
|
|
75
|
+
// satisfies the provider's required apiKey setting.
|
|
76
|
+
apiKey: 'codex-oauth',
|
|
77
|
+
baseURL: this.baseURL,
|
|
78
|
+
headers: args.headers,
|
|
79
|
+
// Prefer the live resolver over Mastra's captured `args.apiKey` so a
|
|
80
|
+
// rotated / refreshed token is used on the next request without
|
|
81
|
+
// re-resolving the model. The fetch instance is shared so concurrent
|
|
82
|
+
// requests dedupe token refresh.
|
|
83
|
+
fetch: this.oauthFetch,
|
|
84
|
+
});
|
|
85
|
+
// Default OpenAI factory uses the Responses API, which is what the Codex
|
|
86
|
+
// ChatGPT backend speaks (`…/codex/responses`).
|
|
87
|
+
return openai((0, models_1.stripCodexOAuthPrefix)(args.modelId));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.CodexOAuthGateway = CodexOAuthGateway;
|
|
91
|
+
//# sourceMappingURL=gateway.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { CodexOAuthGateway, createCodexOAuthGateway } from './gateway';
|
|
2
|
+
export type { CodexOAuthGatewayOptions } from './gateway';
|
|
3
|
+
export { CODEX_OAUTH_GATEWAY_ID, CODEX_OAUTH_PROVIDER_ID, DEFAULT_CODEX_OAUTH_MODELS, } from './models';
|
|
4
|
+
export type { CodexOAuthModelId, CodexOAuthProviderId } from './models';
|
|
5
|
+
export { CODEX_OAUTH_ACCESS_TOKEN_ENV_VAR, CODEX_OAUTH_ACCOUNT_ID_ENV_VAR, CODEX_OAUTH_REFRESH_TOKEN_ENV_VAR, resolveCodexCredentials, } from './auth';
|
|
6
|
+
export type { CodexOAuthCredentials, CodexOAuthCredentialsResolver } from './auth';
|
|
7
|
+
export { CODEX_RESPONSES_BASE_URL } from './oauth-fetch';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CODEX_RESPONSES_BASE_URL = exports.resolveCodexCredentials = exports.CODEX_OAUTH_REFRESH_TOKEN_ENV_VAR = exports.CODEX_OAUTH_ACCOUNT_ID_ENV_VAR = exports.CODEX_OAUTH_ACCESS_TOKEN_ENV_VAR = exports.DEFAULT_CODEX_OAUTH_MODELS = exports.CODEX_OAUTH_PROVIDER_ID = exports.CODEX_OAUTH_GATEWAY_ID = exports.createCodexOAuthGateway = exports.CodexOAuthGateway = void 0;
|
|
4
|
+
var gateway_1 = require("./gateway");
|
|
5
|
+
Object.defineProperty(exports, "CodexOAuthGateway", { enumerable: true, get: function () { return gateway_1.CodexOAuthGateway; } });
|
|
6
|
+
Object.defineProperty(exports, "createCodexOAuthGateway", { enumerable: true, get: function () { return gateway_1.createCodexOAuthGateway; } });
|
|
7
|
+
var models_1 = require("./models");
|
|
8
|
+
Object.defineProperty(exports, "CODEX_OAUTH_GATEWAY_ID", { enumerable: true, get: function () { return models_1.CODEX_OAUTH_GATEWAY_ID; } });
|
|
9
|
+
Object.defineProperty(exports, "CODEX_OAUTH_PROVIDER_ID", { enumerable: true, get: function () { return models_1.CODEX_OAUTH_PROVIDER_ID; } });
|
|
10
|
+
Object.defineProperty(exports, "DEFAULT_CODEX_OAUTH_MODELS", { enumerable: true, get: function () { return models_1.DEFAULT_CODEX_OAUTH_MODELS; } });
|
|
11
|
+
var auth_1 = require("./auth");
|
|
12
|
+
Object.defineProperty(exports, "CODEX_OAUTH_ACCESS_TOKEN_ENV_VAR", { enumerable: true, get: function () { return auth_1.CODEX_OAUTH_ACCESS_TOKEN_ENV_VAR; } });
|
|
13
|
+
Object.defineProperty(exports, "CODEX_OAUTH_ACCOUNT_ID_ENV_VAR", { enumerable: true, get: function () { return auth_1.CODEX_OAUTH_ACCOUNT_ID_ENV_VAR; } });
|
|
14
|
+
Object.defineProperty(exports, "CODEX_OAUTH_REFRESH_TOKEN_ENV_VAR", { enumerable: true, get: function () { return auth_1.CODEX_OAUTH_REFRESH_TOKEN_ENV_VAR; } });
|
|
15
|
+
Object.defineProperty(exports, "resolveCodexCredentials", { enumerable: true, get: function () { return auth_1.resolveCodexCredentials; } });
|
|
16
|
+
var oauth_fetch_1 = require("./oauth-fetch");
|
|
17
|
+
Object.defineProperty(exports, "CODEX_RESPONSES_BASE_URL", { enumerable: true, get: function () { return oauth_fetch_1.CODEX_RESPONSES_BASE_URL; } });
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qCAAuE;AAA9D,4GAAA,iBAAiB,OAAA;AAAE,kHAAA,uBAAuB,OAAA;AAEnD,mCAIkB;AAHhB,gHAAA,sBAAsB,OAAA;AACtB,iHAAA,uBAAuB,OAAA;AACvB,oHAAA,0BAA0B,OAAA;AAG5B,+BAKgB;AAJd,wHAAA,gCAAgC,OAAA;AAChC,sHAAA,8BAA8B,OAAA;AAC9B,yHAAA,iCAAiC,OAAA;AACjC,+GAAA,uBAAuB,OAAA;AAGzB,6CAAyD;AAAhD,uHAAA,wBAAwB,OAAA"}
|
package/dist/models.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const CODEX_OAUTH_GATEWAY_ID: "codex-oauth";
|
|
2
|
+
export declare const CODEX_OAUTH_PROVIDER_ID: "openai";
|
|
3
|
+
export type CodexOAuthProviderId = typeof CODEX_OAUTH_PROVIDER_ID;
|
|
4
|
+
export type CodexOAuthModelId = string;
|
|
5
|
+
/**
|
|
6
|
+
* Advertised catalog. Static so model resolution needs no network call at boot.
|
|
7
|
+
* Override with `createCodexOAuthGateway({ models })` to track new releases
|
|
8
|
+
* without waiting for a package bump.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_CODEX_OAUTH_MODELS: ReadonlyArray<CodexOAuthModelId>;
|
|
11
|
+
/**
|
|
12
|
+
* Strips a `codex-oauth/` or `codex-oauth/openai/` prefix from a router id.
|
|
13
|
+
* Mastra hands `resolveLanguageModel` the full router id, while the OpenAI
|
|
14
|
+
* provider expects the bare model id.
|
|
15
|
+
*/
|
|
16
|
+
export declare function stripCodexOAuthPrefix(modelId: string): CodexOAuthModelId;
|
package/dist/models.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_CODEX_OAUTH_MODELS = exports.CODEX_OAUTH_PROVIDER_ID = exports.CODEX_OAUTH_GATEWAY_ID = void 0;
|
|
4
|
+
exports.stripCodexOAuthPrefix = stripCodexOAuthPrefix;
|
|
5
|
+
exports.CODEX_OAUTH_GATEWAY_ID = 'codex-oauth';
|
|
6
|
+
exports.CODEX_OAUTH_PROVIDER_ID = 'openai';
|
|
7
|
+
/**
|
|
8
|
+
* Advertised catalog. Static so model resolution needs no network call at boot.
|
|
9
|
+
* Override with `createCodexOAuthGateway({ models })` to track new releases
|
|
10
|
+
* without waiting for a package bump.
|
|
11
|
+
*/
|
|
12
|
+
exports.DEFAULT_CODEX_OAUTH_MODELS = [
|
|
13
|
+
'gpt-5.6-sol',
|
|
14
|
+
'gpt-5.6-terra',
|
|
15
|
+
'gpt-5.6-luna',
|
|
16
|
+
'gpt-5.5',
|
|
17
|
+
'gpt-5.4',
|
|
18
|
+
'gpt-5.4-mini',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Strips a `codex-oauth/` or `codex-oauth/openai/` prefix from a router id.
|
|
22
|
+
* Mastra hands `resolveLanguageModel` the full router id, while the OpenAI
|
|
23
|
+
* provider expects the bare model id.
|
|
24
|
+
*/
|
|
25
|
+
function stripCodexOAuthPrefix(modelId) {
|
|
26
|
+
const trimmed = modelId.trim();
|
|
27
|
+
const gatewayPrefix = `${exports.CODEX_OAUTH_GATEWAY_ID}/`;
|
|
28
|
+
const withoutGateway = trimmed.startsWith(gatewayPrefix)
|
|
29
|
+
? trimmed.slice(gatewayPrefix.length)
|
|
30
|
+
: trimmed;
|
|
31
|
+
const providerPrefix = `${exports.CODEX_OAUTH_PROVIDER_ID}/`;
|
|
32
|
+
return withoutGateway.startsWith(providerPrefix)
|
|
33
|
+
? withoutGateway.slice(providerPrefix.length)
|
|
34
|
+
: withoutGateway;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":";;;AA2BA,sDAWC;AAtCY,QAAA,sBAAsB,GAAG,aAAsB,CAAC;AAEhD,QAAA,uBAAuB,GAAG,QAAiB,CAAC;AAMzD;;;;GAIG;AACU,QAAA,0BAA0B,GAAqC;IAC1E,aAAa;IACb,eAAe;IACf,cAAc;IACd,SAAS;IACT,SAAS;IACT,cAAc;CACf,CAAC;AAEF;;;;GAIG;AACH,SAAgB,qBAAqB,CAAC,OAAe;IACnD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC/B,MAAM,aAAa,GAAG,GAAG,8BAAsB,GAAG,CAAC;IACnD,MAAM,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC;QACtD,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC;QACrC,CAAC,CAAC,OAAO,CAAC;IAEZ,MAAM,cAAc,GAAG,GAAG,+BAAuB,GAAG,CAAC;IACrD,OAAO,cAAc,CAAC,UAAU,CAAC,cAAc,CAAC;QAC9C,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC;QAC7C,CAAC,CAAC,cAAc,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rewrites OpenAI Responses API requests so they authenticate against the
|
|
3
|
+
* ChatGPT Codex subscription backend instead of the metered Platform API.
|
|
4
|
+
*
|
|
5
|
+
* Deltas versus a normal API-key request:
|
|
6
|
+
* 1. `Authorization: Bearer <oauth>` plus `ChatGPT-Account-Id`.
|
|
7
|
+
* 2. `OpenAI-Beta: responses=experimental` and a Codex `originator`.
|
|
8
|
+
* 3. Body normalization: `store` defaults to false, `max_output_tokens` is
|
|
9
|
+
* dropped (Codex rejects it), and `system` roles become `developer`.
|
|
10
|
+
*/
|
|
11
|
+
import { type CodexOAuthCredentialsResolver, type LiveCodexCredentialsResolver } from './auth';
|
|
12
|
+
export declare const CODEX_RESPONSES_BASE_URL = "https://chatgpt.com/backend-api/codex";
|
|
13
|
+
export declare const CODEX_OAUTH_ORIGINATOR = "codex_cli_rs";
|
|
14
|
+
export declare const CODEX_OAUTH_BETA = "responses=experimental";
|
|
15
|
+
export type CreateCodexOAuthFetchOptions = {
|
|
16
|
+
/** Resolved per request so a rotated / refreshed token is picked up live. */
|
|
17
|
+
resolveCredentials?: CodexOAuthCredentialsResolver;
|
|
18
|
+
/**
|
|
19
|
+
* Shared live credential cache (refresh dedup + in-memory reuse). When set,
|
|
20
|
+
* takes precedence over `resolveCredentials`.
|
|
21
|
+
*/
|
|
22
|
+
liveCredentials?: LiveCodexCredentialsResolver;
|
|
23
|
+
/** Upstream `originator` header. Pass `false` to omit. */
|
|
24
|
+
originator?: string | false;
|
|
25
|
+
/** Defaults to global `fetch`. Injected in tests. */
|
|
26
|
+
fetchImpl?: typeof fetch;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Codex Responses body tweaks required for subscription auth compatibility.
|
|
30
|
+
* Exported for tests.
|
|
31
|
+
*/
|
|
32
|
+
export declare function normalizeCodexResponsesBody(body: Record<string, unknown>): Record<string, unknown>;
|
|
33
|
+
export declare function createCodexOAuthFetch(options?: CreateCodexOAuthFetchOptions): typeof fetch;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Rewrites OpenAI Responses API requests so they authenticate against the
|
|
4
|
+
* ChatGPT Codex subscription backend instead of the metered Platform API.
|
|
5
|
+
*
|
|
6
|
+
* Deltas versus a normal API-key request:
|
|
7
|
+
* 1. `Authorization: Bearer <oauth>` plus `ChatGPT-Account-Id`.
|
|
8
|
+
* 2. `OpenAI-Beta: responses=experimental` and a Codex `originator`.
|
|
9
|
+
* 3. Body normalization: `store` defaults to false, `max_output_tokens` is
|
|
10
|
+
* dropped (Codex rejects it), and `system` roles become `developer`.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.CODEX_OAUTH_BETA = exports.CODEX_OAUTH_ORIGINATOR = exports.CODEX_RESPONSES_BASE_URL = void 0;
|
|
14
|
+
exports.normalizeCodexResponsesBody = normalizeCodexResponsesBody;
|
|
15
|
+
exports.createCodexOAuthFetch = createCodexOAuthFetch;
|
|
16
|
+
const auth_1 = require("./auth");
|
|
17
|
+
exports.CODEX_RESPONSES_BASE_URL = 'https://chatgpt.com/backend-api/codex';
|
|
18
|
+
exports.CODEX_OAUTH_ORIGINATOR = 'codex_cli_rs';
|
|
19
|
+
exports.CODEX_OAUTH_BETA = 'responses=experimental';
|
|
20
|
+
function normalizeHeaders(headers) {
|
|
21
|
+
const normalized = {};
|
|
22
|
+
if (!headers)
|
|
23
|
+
return normalized;
|
|
24
|
+
if (typeof Headers !== 'undefined' && headers instanceof Headers) {
|
|
25
|
+
headers.forEach((value, key) => {
|
|
26
|
+
normalized[key] = value;
|
|
27
|
+
});
|
|
28
|
+
return normalized;
|
|
29
|
+
}
|
|
30
|
+
if (Array.isArray(headers)) {
|
|
31
|
+
for (const [key, value] of headers) {
|
|
32
|
+
if (value !== undefined)
|
|
33
|
+
normalized[key] = value;
|
|
34
|
+
}
|
|
35
|
+
return normalized;
|
|
36
|
+
}
|
|
37
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
38
|
+
if (value !== undefined)
|
|
39
|
+
normalized[key] = value;
|
|
40
|
+
}
|
|
41
|
+
return normalized;
|
|
42
|
+
}
|
|
43
|
+
function deleteHeader(headers, name) {
|
|
44
|
+
const lower = name.toLowerCase();
|
|
45
|
+
for (const key of Object.keys(headers)) {
|
|
46
|
+
if (key.toLowerCase() === lower)
|
|
47
|
+
delete headers[key];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function isRecord(value) {
|
|
51
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Codex Responses body tweaks required for subscription auth compatibility.
|
|
55
|
+
* Exported for tests.
|
|
56
|
+
*/
|
|
57
|
+
function normalizeCodexResponsesBody(body) {
|
|
58
|
+
const normalized = { ...body };
|
|
59
|
+
if (normalized.store === undefined) {
|
|
60
|
+
normalized.store = false;
|
|
61
|
+
}
|
|
62
|
+
delete normalized.max_output_tokens;
|
|
63
|
+
if (Array.isArray(normalized.input)) {
|
|
64
|
+
normalized.input = normalized.input.map((item) => {
|
|
65
|
+
if (!isRecord(item) || item.role !== 'system')
|
|
66
|
+
return item;
|
|
67
|
+
return { ...item, role: 'developer' };
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return normalized;
|
|
71
|
+
}
|
|
72
|
+
function applyCredentialHeaders(headers, credentials, originator) {
|
|
73
|
+
deleteHeader(headers, 'authorization');
|
|
74
|
+
deleteHeader(headers, 'chatgpt-account-id');
|
|
75
|
+
deleteHeader(headers, 'openai-beta');
|
|
76
|
+
deleteHeader(headers, 'originator');
|
|
77
|
+
headers.authorization = `Bearer ${credentials.accessToken}`;
|
|
78
|
+
headers['ChatGPT-Account-Id'] = credentials.accountId;
|
|
79
|
+
headers['OpenAI-Beta'] = exports.CODEX_OAUTH_BETA;
|
|
80
|
+
if (originator)
|
|
81
|
+
headers.originator = originator;
|
|
82
|
+
}
|
|
83
|
+
function createCodexOAuthFetch(options = {}) {
|
|
84
|
+
const doFetch = options.fetchImpl ?? globalThis.fetch;
|
|
85
|
+
const originator = options.originator === false ? undefined : (options.originator ?? exports.CODEX_OAUTH_ORIGINATOR);
|
|
86
|
+
const liveCredentials = options.liveCredentials ??
|
|
87
|
+
(0, auth_1.createLiveCodexCredentialsResolver)({
|
|
88
|
+
resolveCredentials: options.resolveCredentials,
|
|
89
|
+
fetchImpl: doFetch,
|
|
90
|
+
});
|
|
91
|
+
return async function codexOAuthFetch(input, init) {
|
|
92
|
+
const credentials = await liveCredentials.resolve();
|
|
93
|
+
const rawBody = init?.body;
|
|
94
|
+
let body = rawBody;
|
|
95
|
+
if (typeof rawBody === 'string') {
|
|
96
|
+
try {
|
|
97
|
+
const parsed = JSON.parse(rawBody);
|
|
98
|
+
if (isRecord(parsed)) {
|
|
99
|
+
body = JSON.stringify(normalizeCodexResponsesBody(parsed));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Non-JSON body: forward unchanged.
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else if (rawBody != null) {
|
|
107
|
+
// The OpenAI Responses provider always sends a JSON string body. Anything
|
|
108
|
+
// else means the provider changed shape and body normalization would
|
|
109
|
+
// silently go missing.
|
|
110
|
+
throw new Error('Unexpected OpenAI request body: expected a JSON string. Codex OAuth requests cannot be rewritten.');
|
|
111
|
+
}
|
|
112
|
+
const headers = normalizeHeaders(init?.headers);
|
|
113
|
+
applyCredentialHeaders(headers, credentials, originator);
|
|
114
|
+
const response = await doFetch(input, { ...init, headers, body });
|
|
115
|
+
if (response.status !== 401)
|
|
116
|
+
return response;
|
|
117
|
+
// Opaque / unexpectedly expired access tokens never trip the proactive
|
|
118
|
+
// margin check — force one refresh and retry once.
|
|
119
|
+
let refreshed;
|
|
120
|
+
try {
|
|
121
|
+
refreshed = await liveCredentials.resolve({ forceRefresh: true });
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return response;
|
|
125
|
+
}
|
|
126
|
+
if (refreshed.accessToken === credentials.accessToken) {
|
|
127
|
+
return response;
|
|
128
|
+
}
|
|
129
|
+
const retryHeaders = normalizeHeaders(init?.headers);
|
|
130
|
+
applyCredentialHeaders(retryHeaders, refreshed, originator);
|
|
131
|
+
return doFetch(input, { ...init, headers: retryHeaders, body });
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=oauth-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-fetch.js","sourceRoot":"","sources":["../src/oauth-fetch.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAoEH,kEAkBC;AAiBD,sDAyDC;AA9JD,iCAKgB;AAEH,QAAA,wBAAwB,GAAG,uCAAuC,CAAC;AAEnE,QAAA,sBAAsB,GAAG,cAAc,CAAC;AAExC,QAAA,gBAAgB,GAAG,wBAAwB,CAAC;AAgBzD,SAAS,gBAAgB,CAAC,OAAgC;IACxD,MAAM,UAAU,GAA2B,EAAE,CAAC;IAC9C,IAAI,CAAC,OAAO;QAAE,OAAO,UAAU,CAAC;IAEhC,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,YAAY,OAAO,EAAE,CAAC;QACjE,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YAC7B,UAAU,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC1B,CAAC,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;YACnC,IAAI,KAAK,KAAK,SAAS;gBAAE,UAAU,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACnD,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAA6C,CAAC,EAAE,CAAC;QACzF,IAAI,KAAK,KAAK,SAAS;YAAE,UAAU,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACnD,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,YAAY,CAAC,OAA+B,EAAE,IAAY;IACjE,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,KAAK;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;IACvD,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED;;;GAGG;AACH,SAAgB,2BAA2B,CACzC,IAA6B;IAE7B,MAAM,UAAU,GAA4B,EAAE,GAAG,IAAI,EAAE,CAAC;IAExD,IAAI,UAAU,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACnC,UAAU,CAAC,KAAK,GAAG,KAAK,CAAC;IAC3B,CAAC;IACD,OAAO,UAAU,CAAC,iBAAiB,CAAC;IAEpC,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,UAAU,CAAC,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YAC/C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC;YAC3D,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,sBAAsB,CAC7B,OAA+B,EAC/B,WAAkC,EAClC,UAA8B;IAE9B,YAAY,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;IACvC,YAAY,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;IAC5C,YAAY,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACrC,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACpC,OAAO,CAAC,aAAa,GAAG,UAAU,WAAW,CAAC,WAAW,EAAE,CAAC;IAC5D,OAAO,CAAC,oBAAoB,CAAC,GAAG,WAAW,CAAC,SAAS,CAAC;IACtD,OAAO,CAAC,aAAa,CAAC,GAAG,wBAAgB,CAAC;IAC1C,IAAI,UAAU;QAAE,OAAO,CAAC,UAAU,GAAG,UAAU,CAAC;AAClD,CAAC;AAED,SAAgB,qBAAqB,CAAC,UAAwC,EAAE;IAC9E,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC;IACtD,MAAM,UAAU,GACd,OAAO,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,IAAI,8BAAsB,CAAC,CAAC;IAC5F,MAAM,eAAe,GACnB,OAAO,CAAC,eAAe;QACvB,IAAA,yCAAkC,EAAC;YACjC,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;YAC9C,SAAS,EAAE,OAAO;SACnB,CAAC,CAAC;IAEL,OAAO,KAAK,UAAU,eAAe,CAAC,KAAK,EAAE,IAAI;QAC/C,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE,CAAC;QAEpD,MAAM,OAAO,GAAG,IAAI,EAAE,IAAI,CAAC;QAC3B,IAAI,IAAI,GAAG,OAAO,CAAC;QACnB,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC5C,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;oBACrB,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,2BAA2B,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC7D,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,oCAAoC;YACtC,CAAC;QACH,CAAC;aAAM,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;YAC3B,0EAA0E;YAC1E,qEAAqE;YACrE,uBAAuB;YACvB,MAAM,IAAI,KAAK,CACb,mGAAmG,CACpG,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAChD,sBAAsB,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;QAEzD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,QAAQ,CAAC;QAE7C,uEAAuE;QACvE,mDAAmD;QACnD,IAAI,SAAgC,CAAC;QACrC,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,IAAI,SAAS,CAAC,WAAW,KAAK,WAAW,CAAC,WAAW,EAAE,CAAC;YACtD,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,MAAM,YAAY,GAAG,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACrD,sBAAsB,CAAC,YAAY,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;QAC5D,OAAO,OAAO,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lumifai/harness-model-codex-oauth",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "UNLICENSED",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/Lumif-ai/lumif-harness.git",
|
|
8
|
+
"directory": "packages/model-providers/codex-oauth"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=22.13.0"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"require": "./dist/index.js",
|
|
18
|
+
"default": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./package.json": "./package.json"
|
|
21
|
+
},
|
|
22
|
+
"main": "dist/index.js",
|
|
23
|
+
"types": "dist/index.d.ts",
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"!dist/**/*.test.*",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@ai-sdk/openai": "^4.0.57",
|
|
31
|
+
"@mastra/core": "1.67.0"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public",
|
|
35
|
+
"registry": "https://registry.npmjs.org/"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc -p tsconfig.json"
|
|
39
|
+
}
|
|
40
|
+
}
|