@moxxy/plugin-provider-openai-codex 0.21.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/LICENSE +21 -0
- package/dist/codex/headers.d.ts +5 -0
- package/dist/codex/headers.d.ts.map +1 -0
- package/dist/codex/headers.js +39 -0
- package/dist/codex/headers.js.map +1 -0
- package/dist/codex/sse-event-handler.d.ts +16 -0
- package/dist/codex/sse-event-handler.d.ts.map +1 -0
- package/dist/codex/sse-event-handler.js +155 -0
- package/dist/codex/sse-event-handler.js.map +1 -0
- package/dist/codex/stream-consumer.d.ts +4 -0
- package/dist/codex/stream-consumer.d.ts.map +1 -0
- package/dist/codex/stream-consumer.js +176 -0
- package/dist/codex/stream-consumer.js.map +1 -0
- package/dist/codex/stream-types.d.ts +61 -0
- package/dist/codex/stream-types.d.ts.map +1 -0
- package/dist/codex/stream-types.js +19 -0
- package/dist/codex/stream-types.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/login.d.ts +33 -0
- package/dist/login.d.ts.map +1 -0
- package/dist/login.js +94 -0
- package/dist/login.js.map +1 -0
- package/dist/models.d.ts +10 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +19 -0
- package/dist/models.js.map +1 -0
- package/dist/oauth.d.ts +74 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +170 -0
- package/dist/oauth.js.map +1 -0
- package/dist/profile.d.ts +13 -0
- package/dist/profile.d.ts.map +1 -0
- package/dist/profile.js +39 -0
- package/dist/profile.js.map +1 -0
- package/dist/provider.d.ts +79 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +306 -0
- package/dist/provider.js.map +1 -0
- package/dist/translate.d.ts +77 -0
- package/dist/translate.d.ts.map +1 -0
- package/dist/translate.js +172 -0
- package/dist/translate.js.map +1 -0
- package/dist/types.d.ts +27 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +66 -0
- package/src/codex/headers.ts +41 -0
- package/src/codex/sse-event-handler.test.ts +85 -0
- package/src/codex/sse-event-handler.ts +173 -0
- package/src/codex/stream-consumer.test.ts +280 -0
- package/src/codex/stream-consumer.ts +181 -0
- package/src/codex/stream-types.ts +61 -0
- package/src/index.ts +65 -0
- package/src/login.ts +121 -0
- package/src/models.ts +21 -0
- package/src/oauth.test.ts +223 -0
- package/src/oauth.ts +200 -0
- package/src/profile.ts +52 -0
- package/src/provider.test.ts +507 -0
- package/src/provider.ts +360 -0
- package/src/translate.test.ts +95 -0
- package/src/translate.ts +224 -0
- package/src/types.ts +28 -0
package/dist/login.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex OAuth login — thin wrapper over `@moxxy/plugin-oauth`'s provider
|
|
3
|
+
* framework. Everything generic (loopback callback server, browser opener,
|
|
4
|
+
* device-flow polling, vault persistence, refresh) lives in plugin-oauth;
|
|
5
|
+
* this file only carries the codex-shaped mapping into `CodexTokens` that
|
|
6
|
+
* the provider class consumes.
|
|
7
|
+
*/
|
|
8
|
+
import { type OAuthVault } from '@moxxy/plugin-oauth';
|
|
9
|
+
import type { ProviderAuthContext, ProviderOAuthResult, ProviderOAuthStatus } from '@moxxy/sdk';
|
|
10
|
+
import type { CodexTokens } from './types.js';
|
|
11
|
+
export declare function codexLogin(ctx: ProviderAuthContext): Promise<ProviderOAuthResult>;
|
|
12
|
+
export declare function codexLogout(ctx: ProviderAuthContext): Promise<boolean>;
|
|
13
|
+
export declare function codexStatus(ctx: ProviderAuthContext): Promise<ProviderOAuthStatus | null>;
|
|
14
|
+
/**
|
|
15
|
+
* Read the stored token set in the legacy `CodexTokens` shape the provider
|
|
16
|
+
* class expects. Returns `null` if nothing is stored — the provider then
|
|
17
|
+
* reports the "run `moxxy login openai-codex`" error to the caller.
|
|
18
|
+
*/
|
|
19
|
+
export declare function readStoredTokens(vault: OAuthVault): Promise<CodexTokens | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Pre-request freshness gate consumed by `CodexProvider.ensureFresh`.
|
|
22
|
+
* Reads the stored creds, refreshes if near expiry, persists the rotated
|
|
23
|
+
* tokens BEFORE returning so a crash here can't strand a single-use
|
|
24
|
+
* refresh_token in memory.
|
|
25
|
+
*/
|
|
26
|
+
export declare function ensureFreshCodexTokens(vault: OAuthVault): Promise<CodexTokens>;
|
|
27
|
+
/**
|
|
28
|
+
* Persist the provider's in-memory `CodexTokens` back into the vault after
|
|
29
|
+
* an in-flight refresh. Wires the `CodexProvider.onTokensRefreshed`
|
|
30
|
+
* callback to the same storage layout `runOauthLogin` writes.
|
|
31
|
+
*/
|
|
32
|
+
export declare function persistCodexTokens(vault: OAuthVault, tokens: CodexTokens): Promise<void>;
|
|
33
|
+
//# sourceMappingURL=login.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAML,KAAK,UAAU,EAEhB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,wBAAsB,UAAU,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CASvF;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAO5E;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAQ/F;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAGrF;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAWpF;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,IAAI,CAAC,CAgBf"}
|
package/dist/login.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex OAuth login — thin wrapper over `@moxxy/plugin-oauth`'s provider
|
|
3
|
+
* framework. Everything generic (loopback callback server, browser opener,
|
|
4
|
+
* device-flow polling, vault persistence, refresh) lives in plugin-oauth;
|
|
5
|
+
* this file only carries the codex-shaped mapping into `CodexTokens` that
|
|
6
|
+
* the provider class consumes.
|
|
7
|
+
*/
|
|
8
|
+
import { clearStoredCreds, ensureFreshTokens, readStoredCreds, runOauthLogin, storeTokenSet, } from '@moxxy/plugin-oauth';
|
|
9
|
+
import { CODEX_PROVIDER_ID, codexOauthProfile } from './profile.js';
|
|
10
|
+
export async function codexLogin(ctx) {
|
|
11
|
+
const result = await runOauthLogin(codexOauthProfile, {
|
|
12
|
+
vault: ctx.vault,
|
|
13
|
+
headless: ctx.headless,
|
|
14
|
+
write: ctx.write,
|
|
15
|
+
});
|
|
16
|
+
return result.accountId
|
|
17
|
+
? { accountId: result.accountId, expiresAt: result.tokens.expiresAt ?? 0 }
|
|
18
|
+
: { expiresAt: result.tokens.expiresAt ?? 0 };
|
|
19
|
+
}
|
|
20
|
+
export async function codexLogout(ctx) {
|
|
21
|
+
try {
|
|
22
|
+
const removed = await clearStoredCreds(ctx.vault, CODEX_PROVIDER_ID);
|
|
23
|
+
return removed > 0;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function codexStatus(ctx) {
|
|
30
|
+
const stored = await readStoredCreds(ctx.vault, CODEX_PROVIDER_ID);
|
|
31
|
+
if (!stored)
|
|
32
|
+
return null;
|
|
33
|
+
return {
|
|
34
|
+
accountId: stored.extras.account_id ?? null,
|
|
35
|
+
expiresAt: stored.tokenSet.expiresAt ?? 0,
|
|
36
|
+
vaultKey: `oauth/${CODEX_PROVIDER_ID}/*`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Read the stored token set in the legacy `CodexTokens` shape the provider
|
|
41
|
+
* class expects. Returns `null` if nothing is stored — the provider then
|
|
42
|
+
* reports the "run `moxxy login openai-codex`" error to the caller.
|
|
43
|
+
*/
|
|
44
|
+
export async function readStoredTokens(vault) {
|
|
45
|
+
const stored = await readStoredCreds(vault, CODEX_PROVIDER_ID);
|
|
46
|
+
return stored ? toCodexTokens(stored) : null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Pre-request freshness gate consumed by `CodexProvider.ensureFresh`.
|
|
50
|
+
* Reads the stored creds, refreshes if near expiry, persists the rotated
|
|
51
|
+
* tokens BEFORE returning so a crash here can't strand a single-use
|
|
52
|
+
* refresh_token in memory.
|
|
53
|
+
*/
|
|
54
|
+
export async function ensureFreshCodexTokens(vault) {
|
|
55
|
+
const { tokens, extras } = await ensureFreshTokens(codexOauthProfile, vault);
|
|
56
|
+
if (!tokens.refreshToken) {
|
|
57
|
+
throw new Error('refreshed token set missing refresh_token');
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
access: tokens.accessToken,
|
|
61
|
+
refresh: tokens.refreshToken,
|
|
62
|
+
expires: tokens.expiresAt ?? 0,
|
|
63
|
+
...(extras.account_id ? { accountId: extras.account_id } : {}),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Persist the provider's in-memory `CodexTokens` back into the vault after
|
|
68
|
+
* an in-flight refresh. Wires the `CodexProvider.onTokensRefreshed`
|
|
69
|
+
* callback to the same storage layout `runOauthLogin` writes.
|
|
70
|
+
*/
|
|
71
|
+
export async function persistCodexTokens(vault, tokens) {
|
|
72
|
+
await storeTokenSet(vault, CODEX_PROVIDER_ID, {
|
|
73
|
+
accessToken: tokens.access,
|
|
74
|
+
refreshToken: tokens.refresh,
|
|
75
|
+
expiresAt: tokens.expires,
|
|
76
|
+
tokenType: 'Bearer',
|
|
77
|
+
}, {
|
|
78
|
+
clientId: codexOauthProfile.clientId,
|
|
79
|
+
tokenUrl: codexOauthProfile.tokenUrl,
|
|
80
|
+
extras: tokens.accountId ? { account_id: tokens.accountId } : {},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function toCodexTokens(stored) {
|
|
84
|
+
if (!stored.tokenSet.refreshToken) {
|
|
85
|
+
throw new Error(`Stored codex creds missing refresh_token under oauth/${CODEX_PROVIDER_ID}/`);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
access: stored.tokenSet.accessToken,
|
|
89
|
+
refresh: stored.tokenSet.refreshToken,
|
|
90
|
+
expires: stored.tokenSet.expiresAt ?? 0,
|
|
91
|
+
...(stored.extras.account_id ? { accountId: stored.extras.account_id } : {}),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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"}
|
package/dist/models.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ModelDescriptor } from '@moxxy/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Models the ChatGPT-plan backend will serve. Mirrors opencode's ALLOWED_MODELS
|
|
4
|
+
* set (`packages/opencode/src/plugin/codex.ts`). The API-key OpenAI provider
|
|
5
|
+
* still exposes the full catalog; this list is the subset the Codex backend
|
|
6
|
+
* routes to ChatGPT-Pro/Plus subscribers without per-token billing.
|
|
7
|
+
*/
|
|
8
|
+
export declare const codexModels: ReadonlyArray<ModelDescriptor>;
|
|
9
|
+
export declare const DEFAULT_CODEX_MODEL = "gpt-5.3-codex";
|
|
10
|
+
//# sourceMappingURL=models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;GAKG;AAIH,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,eAAe,CAOtD,CAAC;AAEF,eAAO,MAAM,mBAAmB,kBAAkB,CAAC"}
|
package/dist/models.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Models the ChatGPT-plan backend will serve. Mirrors opencode's ALLOWED_MODELS
|
|
3
|
+
* set (`packages/opencode/src/plugin/codex.ts`). The API-key OpenAI provider
|
|
4
|
+
* still exposes the full catalog; this list is the subset the Codex backend
|
|
5
|
+
* routes to ChatGPT-Pro/Plus subscribers without per-token billing.
|
|
6
|
+
*/
|
|
7
|
+
// Every Codex-served model is a gpt-5-family reasoning model, so all advertise
|
|
8
|
+
// `supportsReasoning` — the request already sends `reasoning.summary: 'auto'`;
|
|
9
|
+
// the per-provider toggle decides whether the summary is surfaced.
|
|
10
|
+
export const codexModels = [
|
|
11
|
+
{ id: 'gpt-5.5', contextWindow: 1_000_000, maxOutputTokens: 128_000, supportsTools: true, supportsStreaming: true, supportsImages: true, supportsDocuments: true, supportsReasoning: true },
|
|
12
|
+
{ id: 'gpt-5.4', contextWindow: 1_000_000, maxOutputTokens: 128_000, supportsTools: true, supportsStreaming: true, supportsImages: true, supportsDocuments: true, supportsReasoning: true },
|
|
13
|
+
{ id: 'gpt-5.4-mini', contextWindow: 400_000, maxOutputTokens: 128_000, supportsTools: true, supportsStreaming: true, supportsImages: true, supportsDocuments: true, supportsReasoning: true },
|
|
14
|
+
{ id: 'gpt-5.3-codex', contextWindow: 400_000, maxOutputTokens: 128_000, supportsTools: true, supportsStreaming: true, supportsImages: true, supportsDocuments: true, supportsReasoning: true },
|
|
15
|
+
{ id: 'gpt-5.3-codex-spark', contextWindow: 400_000, maxOutputTokens: 128_000, supportsTools: true, supportsStreaming: true, supportsImages: true, supportsDocuments: true, supportsReasoning: true },
|
|
16
|
+
{ id: 'gpt-5.2', contextWindow: 400_000, maxOutputTokens: 128_000, supportsTools: true, supportsStreaming: true, supportsImages: true, supportsDocuments: true, supportsReasoning: true },
|
|
17
|
+
];
|
|
18
|
+
export const DEFAULT_CODEX_MODEL = 'gpt-5.3-codex';
|
|
19
|
+
//# sourceMappingURL=models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,+EAA+E;AAC/E,+EAA+E;AAC/E,mEAAmE;AACnE,MAAM,CAAC,MAAM,WAAW,GAAmC;IACzD,EAAE,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE;IAC3L,EAAE,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE;IAC3L,EAAE,EAAE,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE;IAC9L,EAAE,EAAE,EAAE,eAAe,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE;IAC/L,EAAE,EAAE,EAAE,qBAAqB,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE;IACrM,EAAE,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE;CAC1L,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,eAAe,CAAC"}
|
package/dist/oauth.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex-specific OAuth pieces: constants, JWT-claim extraction, and thin
|
|
3
|
+
* wrappers that adapt `@moxxy/plugin-oauth`'s generic helpers into the
|
|
4
|
+
* `CodexTokens` shape the provider class consumes. The actual flow
|
|
5
|
+
* orchestration lives in `profile.ts` + `login.ts`.
|
|
6
|
+
*/
|
|
7
|
+
import { generateState as oauthGenerateState } from '@moxxy/plugin-oauth';
|
|
8
|
+
import type { CodexTokens, PkceCodes } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Public OAuth client id baked into the first-party Codex / OpenCode clients.
|
|
11
|
+
* Same value used by codex-rs (`codex-rs/login/src/auth/manager.rs`) and
|
|
12
|
+
* opencode (`packages/opencode/src/plugin/codex.ts`) — using it lets a moxxy
|
|
13
|
+
* login interoperate with credentials produced by either tool.
|
|
14
|
+
*/
|
|
15
|
+
export declare const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
16
|
+
export declare const ISSUER = "https://auth.openai.com";
|
|
17
|
+
export declare const AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
|
|
18
|
+
export declare const TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
19
|
+
export declare const CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
20
|
+
export declare const DEFAULT_CALLBACK_PORT = 1455;
|
|
21
|
+
export declare const DEFAULT_REDIRECT_PATH = "/auth/callback";
|
|
22
|
+
export declare const DEFAULT_REDIRECT_URI = "http://localhost:1455/auth/callback";
|
|
23
|
+
export declare const SCOPES = "openid profile email offline_access";
|
|
24
|
+
export declare const ORIGINATOR = "moxxy";
|
|
25
|
+
/**
|
|
26
|
+
* PKCE pair for the Codex flow. Delegates to `@moxxy/plugin-oauth`'s shared
|
|
27
|
+
* primitives (S256 verifier -> challenge) instead of hand-rolling crypto;
|
|
28
|
+
* kept as a thin wrapper so it returns the `PkceCodes` shape and stays a
|
|
29
|
+
* stable public export.
|
|
30
|
+
*/
|
|
31
|
+
export declare function generatePKCE(): Promise<PkceCodes>;
|
|
32
|
+
export declare const generateState: typeof oauthGenerateState;
|
|
33
|
+
/**
|
|
34
|
+
* Thin wrapper over `@moxxy/plugin-oauth`'s generic `buildAuthUrl` that
|
|
35
|
+
* stamps in the Codex-specific extras. Exported because tests + downstream
|
|
36
|
+
* consumers may want to build URLs without running the full flow.
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildAuthorizeUrl(redirectUri: string, pkce: PkceCodes, state: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* JWT-claim extraction — header.payload.signature with base64url-encoded
|
|
41
|
+
* segments. We never verify the signature: the access_token is only ever
|
|
42
|
+
* sent back to the issuer (or its API gateway), so trust is rooted in the
|
|
43
|
+
* fact that we received the token over TLS from the token endpoint. The
|
|
44
|
+
* only thing we use these claims for is plucking the chatgpt_account_id
|
|
45
|
+
* for the per-request header.
|
|
46
|
+
*/
|
|
47
|
+
export declare function parseJwtClaims(jwt: string): Record<string, unknown> | undefined;
|
|
48
|
+
interface AccountIdSource {
|
|
49
|
+
readonly access_token?: string;
|
|
50
|
+
readonly id_token?: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Account-id priority order matches opencode's `extractAccountIdFromClaims`:
|
|
54
|
+
* the explicit top-level claim → the namespaced auth-bag claim → first
|
|
55
|
+
* organization id. Returning undefined is fine — the API just won't
|
|
56
|
+
* receive the optional ChatGPT-Account-Id header.
|
|
57
|
+
*/
|
|
58
|
+
export declare function extractAccountId(tokens: AccountIdSource): string | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Wrapper around plugin-oauth's `exchangeCodeForToken` that returns the
|
|
61
|
+
* `CodexTokens` shape (with `accountId` plucked from the id_token JWT).
|
|
62
|
+
* Tests + downstream callers use it directly; the live login path goes
|
|
63
|
+
* through `runOauthLogin(codexOauthProfile, ...)`.
|
|
64
|
+
*/
|
|
65
|
+
export declare function exchangeCodeForTokens(code: string, redirectUri: string, pkce: PkceCodes, fetchImpl?: typeof fetch): Promise<CodexTokens>;
|
|
66
|
+
/**
|
|
67
|
+
* Refresh both the access AND refresh tokens. The OAuth server issues a
|
|
68
|
+
* fresh refresh_token on every refresh and INVALIDATES the previous one —
|
|
69
|
+
* callers must persist the returned tokens BEFORE issuing any API call
|
|
70
|
+
* that might fail mid-flight, otherwise a crash will lock the user out.
|
|
71
|
+
*/
|
|
72
|
+
export declare function refreshTokens(refreshToken: string, fetchImpl?: typeof fetch): Promise<CodexTokens>;
|
|
73
|
+
export {};
|
|
74
|
+
//# sourceMappingURL=oauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAKL,aAAa,IAAI,kBAAkB,EAGpC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,SAAS,iCAAiC,CAAC;AACxD,eAAO,MAAM,MAAM,4BAA4B,CAAC;AAChD,eAAO,MAAM,aAAa,4CAA8B,CAAC;AACzD,eAAO,MAAM,SAAS,wCAA0B,CAAC;AACjD,eAAO,MAAM,mBAAmB,oDAAoD,CAAC;AACrF,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAC1C,eAAO,MAAM,qBAAqB,mBAAmB,CAAC;AACtD,eAAO,MAAM,oBAAoB,wCAAsE,CAAC;AACxG,eAAO,MAAM,MAAM,wCAAwC,CAAC;AAC5D,eAAO,MAAM,UAAU,UAAU,CAAC;AAElC;;;;;GAKG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,SAAS,CAAC,CAGvD;AAED,eAAO,MAAM,aAAa,2BAAqB,CAAC;AAEhD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAc7F;AAcD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAiB/E;AAED,UAAU,eAAe;IACvB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAmB5E;AAkBD;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,SAAS,EACf,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,WAAW,CAAC,CAYtB;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,WAAW,CAAC,CAQtB"}
|
package/dist/oauth.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex-specific OAuth pieces: constants, JWT-claim extraction, and thin
|
|
3
|
+
* wrappers that adapt `@moxxy/plugin-oauth`'s generic helpers into the
|
|
4
|
+
* `CodexTokens` shape the provider class consumes. The actual flow
|
|
5
|
+
* orchestration lives in `profile.ts` + `login.ts`.
|
|
6
|
+
*/
|
|
7
|
+
import { Buffer } from 'node:buffer';
|
|
8
|
+
import { buildAuthUrl, computeCodeChallenge, exchangeCodeForToken as oauthExchangeCodeForToken, generateCodeVerifier, generateState as oauthGenerateState, refreshAccessToken, } from '@moxxy/plugin-oauth';
|
|
9
|
+
/**
|
|
10
|
+
* Public OAuth client id baked into the first-party Codex / OpenCode clients.
|
|
11
|
+
* Same value used by codex-rs (`codex-rs/login/src/auth/manager.rs`) and
|
|
12
|
+
* opencode (`packages/opencode/src/plugin/codex.ts`) — using it lets a moxxy
|
|
13
|
+
* login interoperate with credentials produced by either tool.
|
|
14
|
+
*/
|
|
15
|
+
export const CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';
|
|
16
|
+
export const ISSUER = 'https://auth.openai.com';
|
|
17
|
+
export const AUTHORIZE_URL = `${ISSUER}/oauth/authorize`;
|
|
18
|
+
export const TOKEN_URL = `${ISSUER}/oauth/token`;
|
|
19
|
+
export const CODEX_RESPONSES_URL = 'https://chatgpt.com/backend-api/codex/responses';
|
|
20
|
+
export const DEFAULT_CALLBACK_PORT = 1455;
|
|
21
|
+
export const DEFAULT_REDIRECT_PATH = '/auth/callback';
|
|
22
|
+
export const DEFAULT_REDIRECT_URI = `http://localhost:${DEFAULT_CALLBACK_PORT}${DEFAULT_REDIRECT_PATH}`;
|
|
23
|
+
export const SCOPES = 'openid profile email offline_access';
|
|
24
|
+
export const ORIGINATOR = 'moxxy';
|
|
25
|
+
/**
|
|
26
|
+
* PKCE pair for the Codex flow. Delegates to `@moxxy/plugin-oauth`'s shared
|
|
27
|
+
* primitives (S256 verifier -> challenge) instead of hand-rolling crypto;
|
|
28
|
+
* kept as a thin wrapper so it returns the `PkceCodes` shape and stays a
|
|
29
|
+
* stable public export.
|
|
30
|
+
*/
|
|
31
|
+
export async function generatePKCE() {
|
|
32
|
+
const verifier = generateCodeVerifier();
|
|
33
|
+
return { verifier, challenge: computeCodeChallenge(verifier) };
|
|
34
|
+
}
|
|
35
|
+
export const generateState = oauthGenerateState;
|
|
36
|
+
/**
|
|
37
|
+
* Thin wrapper over `@moxxy/plugin-oauth`'s generic `buildAuthUrl` that
|
|
38
|
+
* stamps in the Codex-specific extras. Exported because tests + downstream
|
|
39
|
+
* consumers may want to build URLs without running the full flow.
|
|
40
|
+
*/
|
|
41
|
+
export function buildAuthorizeUrl(redirectUri, pkce, state) {
|
|
42
|
+
return buildAuthUrl({
|
|
43
|
+
authUrl: AUTHORIZE_URL,
|
|
44
|
+
clientId: CLIENT_ID,
|
|
45
|
+
redirectUri,
|
|
46
|
+
scopes: SCOPES.split(' '),
|
|
47
|
+
codeChallenge: pkce.challenge,
|
|
48
|
+
state,
|
|
49
|
+
extraAuthParams: {
|
|
50
|
+
id_token_add_organizations: 'true',
|
|
51
|
+
codex_cli_simplified_flow: 'true',
|
|
52
|
+
originator: ORIGINATOR,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Hard cap on the base64url payload segment we'll decode + JSON.parse. A
|
|
58
|
+
* legitimate OAuth id_token/access_token JWT payload is a few hundred bytes to
|
|
59
|
+
* a couple KiB; 64 KiB is far beyond any real claim set. The cap is defense in
|
|
60
|
+
* depth: although the token reaches us over TLS from the issuer, a hostile or
|
|
61
|
+
* MITM'd token endpoint — or a tampered persisted vault entry replayed back in
|
|
62
|
+
* — could otherwise hand us a multi-megabyte/gigabyte segment that we'd
|
|
63
|
+
* base64-decode and `JSON.parse` in full. Over the cap we degrade to
|
|
64
|
+
* `undefined` (callers already treat a missing account-id claim as fine).
|
|
65
|
+
*/
|
|
66
|
+
const MAX_JWT_SEGMENT_CHARS = 64 * 1024;
|
|
67
|
+
/**
|
|
68
|
+
* JWT-claim extraction — header.payload.signature with base64url-encoded
|
|
69
|
+
* segments. We never verify the signature: the access_token is only ever
|
|
70
|
+
* sent back to the issuer (or its API gateway), so trust is rooted in the
|
|
71
|
+
* fact that we received the token over TLS from the token endpoint. The
|
|
72
|
+
* only thing we use these claims for is plucking the chatgpt_account_id
|
|
73
|
+
* for the per-request header.
|
|
74
|
+
*/
|
|
75
|
+
export function parseJwtClaims(jwt) {
|
|
76
|
+
if (typeof jwt !== 'string')
|
|
77
|
+
return undefined;
|
|
78
|
+
const parts = jwt.split('.');
|
|
79
|
+
if (parts.length !== 3)
|
|
80
|
+
return undefined;
|
|
81
|
+
const payload = parts[1];
|
|
82
|
+
// Bound the decode: never allocate/parse an oversized payload segment.
|
|
83
|
+
if (payload.length > MAX_JWT_SEGMENT_CHARS)
|
|
84
|
+
return undefined;
|
|
85
|
+
try {
|
|
86
|
+
const parsed = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
|
|
87
|
+
// Guard against a payload that parses to a non-object (e.g. `"5"`, `[]`,
|
|
88
|
+
// `null`): downstream callers index it as a claim bag, so reject anything
|
|
89
|
+
// that isn't a plain object rather than returning a surprising value.
|
|
90
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
91
|
+
return undefined;
|
|
92
|
+
return parsed;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Account-id priority order matches opencode's `extractAccountIdFromClaims`:
|
|
100
|
+
* the explicit top-level claim → the namespaced auth-bag claim → first
|
|
101
|
+
* organization id. Returning undefined is fine — the API just won't
|
|
102
|
+
* receive the optional ChatGPT-Account-Id header.
|
|
103
|
+
*/
|
|
104
|
+
export function extractAccountId(tokens) {
|
|
105
|
+
for (const candidate of [tokens.id_token, tokens.access_token]) {
|
|
106
|
+
if (!candidate)
|
|
107
|
+
continue;
|
|
108
|
+
const claims = parseJwtClaims(candidate);
|
|
109
|
+
if (!claims)
|
|
110
|
+
continue;
|
|
111
|
+
const direct = claims['chatgpt_account_id'];
|
|
112
|
+
if (typeof direct === 'string' && direct)
|
|
113
|
+
return direct;
|
|
114
|
+
const authBag = claims['https://api.openai.com/auth'];
|
|
115
|
+
if (authBag && typeof authBag === 'object') {
|
|
116
|
+
const fromBag = authBag['chatgpt_account_id'];
|
|
117
|
+
if (typeof fromBag === 'string' && fromBag)
|
|
118
|
+
return fromBag;
|
|
119
|
+
}
|
|
120
|
+
const orgs = claims['organizations'];
|
|
121
|
+
if (Array.isArray(orgs) && orgs.length > 0) {
|
|
122
|
+
const first = orgs[0];
|
|
123
|
+
if (first && typeof first.id === 'string' && first.id)
|
|
124
|
+
return first.id;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
function toCodexTokens(set) {
|
|
130
|
+
if (!set.refreshToken) {
|
|
131
|
+
throw new Error('OAuth token response missing refresh_token');
|
|
132
|
+
}
|
|
133
|
+
const accountId = extractAccountId({
|
|
134
|
+
...(set.idToken ? { id_token: set.idToken } : {}),
|
|
135
|
+
access_token: set.accessToken,
|
|
136
|
+
});
|
|
137
|
+
return {
|
|
138
|
+
access: set.accessToken,
|
|
139
|
+
refresh: set.refreshToken,
|
|
140
|
+
expires: set.expiresAt ?? Date.now() + 3600_000,
|
|
141
|
+
...(accountId ? { accountId } : {}),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Wrapper around plugin-oauth's `exchangeCodeForToken` that returns the
|
|
146
|
+
* `CodexTokens` shape (with `accountId` plucked from the id_token JWT).
|
|
147
|
+
* Tests + downstream callers use it directly; the live login path goes
|
|
148
|
+
* through `runOauthLogin(codexOauthProfile, ...)`.
|
|
149
|
+
*/
|
|
150
|
+
export async function exchangeCodeForTokens(code, redirectUri, pkce, fetchImpl = fetch) {
|
|
151
|
+
const set = await oauthExchangeCodeForToken({
|
|
152
|
+
tokenUrl: TOKEN_URL,
|
|
153
|
+
code,
|
|
154
|
+
redirectUri,
|
|
155
|
+
clientId: CLIENT_ID,
|
|
156
|
+
codeVerifier: pkce.verifier,
|
|
157
|
+
}, fetchImpl);
|
|
158
|
+
return toCodexTokens(set);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Refresh both the access AND refresh tokens. The OAuth server issues a
|
|
162
|
+
* fresh refresh_token on every refresh and INVALIDATES the previous one —
|
|
163
|
+
* callers must persist the returned tokens BEFORE issuing any API call
|
|
164
|
+
* that might fail mid-flight, otherwise a crash will lock the user out.
|
|
165
|
+
*/
|
|
166
|
+
export async function refreshTokens(refreshToken, fetchImpl = fetch) {
|
|
167
|
+
const set = await refreshAccessToken({ tokenUrl: TOKEN_URL, clientId: CLIENT_ID, refreshToken }, fetchImpl);
|
|
168
|
+
return toCodexTokens(set.refreshToken ? set : { ...set, refreshToken });
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `OAuthProviderProfile` for the OpenAI Codex (ChatGPT Pro/Plus) provider.
|
|
3
|
+
* Drives both `runOauthLogin` (interactive) and `ensureFreshTokens`
|
|
4
|
+
* (pre-request refresh) from `@moxxy/plugin-oauth`. Codex-specific
|
|
5
|
+
* concerns:
|
|
6
|
+
* - the non-standard OpenAI device-flow dialect (via `openaiDeviceFlow`)
|
|
7
|
+
* - `chatgpt_account_id` extraction from id_token / access_token JWTs
|
|
8
|
+
* - the bundled-CLI auth flags (`id_token_add_organizations`, etc.)
|
|
9
|
+
*/
|
|
10
|
+
import { type OAuthProviderProfile } from '@moxxy/plugin-oauth';
|
|
11
|
+
export declare const CODEX_PROVIDER_ID = "openai-codex";
|
|
12
|
+
export declare const codexOauthProfile: OAuthProviderProfile;
|
|
13
|
+
//# sourceMappingURL=profile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAoB,KAAK,oBAAoB,EAAiB,MAAM,qBAAqB,CAAC;AAajG,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAEhD,eAAO,MAAM,iBAAiB,EAAE,oBA0B/B,CAAC"}
|
package/dist/profile.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `OAuthProviderProfile` for the OpenAI Codex (ChatGPT Pro/Plus) provider.
|
|
3
|
+
* Drives both `runOauthLogin` (interactive) and `ensureFreshTokens`
|
|
4
|
+
* (pre-request refresh) from `@moxxy/plugin-oauth`. Codex-specific
|
|
5
|
+
* concerns:
|
|
6
|
+
* - the non-standard OpenAI device-flow dialect (via `openaiDeviceFlow`)
|
|
7
|
+
* - `chatgpt_account_id` extraction from id_token / access_token JWTs
|
|
8
|
+
* - the bundled-CLI auth flags (`id_token_add_organizations`, etc.)
|
|
9
|
+
*/
|
|
10
|
+
import { openaiDeviceFlow } from '@moxxy/plugin-oauth';
|
|
11
|
+
import { AUTHORIZE_URL, CLIENT_ID, DEFAULT_CALLBACK_PORT, DEFAULT_REDIRECT_PATH, ISSUER, ORIGINATOR, SCOPES, TOKEN_URL, extractAccountId, } from './oauth.js';
|
|
12
|
+
export const CODEX_PROVIDER_ID = 'openai-codex';
|
|
13
|
+
export const codexOauthProfile = {
|
|
14
|
+
id: CODEX_PROVIDER_ID,
|
|
15
|
+
displayName: 'ChatGPT Pro/Plus',
|
|
16
|
+
authUrl: AUTHORIZE_URL,
|
|
17
|
+
tokenUrl: TOKEN_URL,
|
|
18
|
+
clientId: CLIENT_ID,
|
|
19
|
+
scopes: SCOPES.split(' '),
|
|
20
|
+
// These flags are what codex-rs / opencode pass; without them the
|
|
21
|
+
// returned id_token won't carry the chatgpt_account_id / organizations
|
|
22
|
+
// claims we need to populate the ChatGPT-Account-Id header.
|
|
23
|
+
extraAuthParams: {
|
|
24
|
+
id_token_add_organizations: 'true',
|
|
25
|
+
codex_cli_simplified_flow: 'true',
|
|
26
|
+
originator: ORIGINATOR,
|
|
27
|
+
},
|
|
28
|
+
redirect: { port: DEFAULT_CALLBACK_PORT, path: DEFAULT_REDIRECT_PATH },
|
|
29
|
+
deviceFlow: openaiDeviceFlow({
|
|
30
|
+
issuer: ISSUER,
|
|
31
|
+
tokenUrl: TOKEN_URL,
|
|
32
|
+
verificationUri: `${ISSUER}/codex/device`,
|
|
33
|
+
}),
|
|
34
|
+
extractAccountId: (tokens) => extractAccountId({
|
|
35
|
+
...(tokens.idToken ? { id_token: tokens.idToken } : {}),
|
|
36
|
+
access_token: tokens.accessToken,
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,gBAAgB,EAA4C,MAAM,qBAAqB,CAAC;AACjG,OAAO,EACL,aAAa,EACb,SAAS,EACT,qBAAqB,EACrB,qBAAqB,EACrB,MAAM,EACN,UAAU,EACV,MAAM,EACN,SAAS,EACT,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAEhD,MAAM,CAAC,MAAM,iBAAiB,GAAyB;IACrD,EAAE,EAAE,iBAAiB;IACrB,WAAW,EAAE,kBAAkB;IAC/B,OAAO,EAAE,aAAa;IACtB,QAAQ,EAAE,SAAS;IACnB,QAAQ,EAAE,SAAS;IACnB,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;IACzB,kEAAkE;IAClE,uEAAuE;IACvE,4DAA4D;IAC5D,eAAe,EAAE;QACf,0BAA0B,EAAE,MAAM;QAClC,yBAAyB,EAAE,MAAM;QACjC,UAAU,EAAE,UAAU;KACvB;IACD,QAAQ,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,qBAAqB,EAAE;IACtE,UAAU,EAAE,gBAAgB,CAAC;QAC3B,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,SAAS;QACnB,eAAe,EAAE,GAAG,MAAM,eAAe;KAC1C,CAAC;IACF,gBAAgB,EAAE,CAAC,MAAgB,EAAE,EAAE,CACrC,gBAAgB,CAAC;QACf,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,YAAY,EAAE,MAAM,CAAC,WAAW;KACjC,CAAC;CACL,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { LLMProvider, ProviderEvent, ProviderRequest } from '@moxxy/sdk';
|
|
2
|
+
import type { CodexTokens } from './types.js';
|
|
3
|
+
export interface CodexProviderConfig {
|
|
4
|
+
readonly tokens?: CodexTokens;
|
|
5
|
+
/**
|
|
6
|
+
* Called with the new token bundle whenever an in-process refresh happens.
|
|
7
|
+
* The CLI's setup wires this to a vault writeback so the refreshed
|
|
8
|
+
* refresh_token (single-use, rotates on every refresh) is persisted
|
|
9
|
+
* before the next API call goes out.
|
|
10
|
+
*/
|
|
11
|
+
readonly onTokensRefreshed?: (next: CodexTokens) => void | Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Re-reads the persisted token bundle (the vault) and returns it, or null
|
|
14
|
+
* when nothing is stored. The refresh token is SINGLE-USE and rotates on
|
|
15
|
+
* every refresh, so when another consumer/process refreshes first, this is
|
|
16
|
+
* how the provider picks up the rotated bundle instead of burning a dead
|
|
17
|
+
* token: it's consulted under the per-credential lock before refreshing,
|
|
18
|
+
* and once more to retry after an invalid_grant-style rejection.
|
|
19
|
+
*/
|
|
20
|
+
readonly reloadTokens?: () => Promise<CodexTokens | null>;
|
|
21
|
+
readonly defaultModel?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Reasoning effort sent with every request (`reasoning.effort`).
|
|
24
|
+
* Defaults to `'medium'`. Reaches here from `provider.config` in
|
|
25
|
+
* moxxy.config.ts (e.g. `{ reasoningEffort: 'high' }`); the CLI's
|
|
26
|
+
* credential resolution merges that config through to `createClient`.
|
|
27
|
+
*/
|
|
28
|
+
readonly reasoningEffort?: 'low' | 'medium' | 'high';
|
|
29
|
+
/** Test seam — when omitted we use the global `fetch`. */
|
|
30
|
+
readonly fetch?: typeof fetch;
|
|
31
|
+
/** Test seam — when omitted we use crypto.randomUUID for the per-request session id. */
|
|
32
|
+
readonly sessionIdProvider?: () => string;
|
|
33
|
+
/**
|
|
34
|
+
* Idle timeout (ms) for the streaming request: the watchdog aborts the call if
|
|
35
|
+
* the backend goes silent for this long (stalled handshake or mid-stream
|
|
36
|
+
* silence). Reset on every received byte, so slow-but-alive streams survive.
|
|
37
|
+
* Defaults to {@link CODEX_IDLE_TIMEOUT_MS}; mainly a test seam.
|
|
38
|
+
*/
|
|
39
|
+
readonly idleTimeoutMs?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* LLMProvider implementation against the ChatGPT-plan Codex backend. Auth is
|
|
43
|
+
* an OAuth bearer plus the optional ChatGPT-Account-Id header; the rest of
|
|
44
|
+
* the request body is the OpenAI Responses-API shape.
|
|
45
|
+
*
|
|
46
|
+
* Request-param support: `req.maxTokens` maps to the Responses
|
|
47
|
+
* `max_output_tokens` field; `req.temperature` is NOT forwarded — the Codex
|
|
48
|
+
* backend only serves gpt-5-family reasoning models, which reject sampling
|
|
49
|
+
* params with a 400, so it is dropped (with a one-shot MOXXY_DEBUG note)
|
|
50
|
+
* instead of breaking the request. Reasoning effort is configurable via
|
|
51
|
+
* `CodexProviderConfig.reasoningEffort` (default `'medium'`).
|
|
52
|
+
*/
|
|
53
|
+
export declare class CodexProvider implements LLMProvider {
|
|
54
|
+
readonly name = "openai-codex";
|
|
55
|
+
readonly models: readonly import("@moxxy/sdk").ModelDescriptor[];
|
|
56
|
+
private tokens;
|
|
57
|
+
private readonly onTokensRefreshed?;
|
|
58
|
+
private readonly reloadTokens?;
|
|
59
|
+
private readonly defaultModel;
|
|
60
|
+
private readonly reasoningEffort?;
|
|
61
|
+
private readonly fetchImpl;
|
|
62
|
+
private readonly sessionIdProvider;
|
|
63
|
+
private readonly idleTimeoutMs;
|
|
64
|
+
constructor(config?: CodexProviderConfig);
|
|
65
|
+
stream(req: ProviderRequest): AsyncIterable<ProviderEvent>;
|
|
66
|
+
countTokens(req: Pick<ProviderRequest, 'model' | 'messages' | 'system' | 'tools'>): Promise<number>;
|
|
67
|
+
private postCodex;
|
|
68
|
+
private ensureFresh;
|
|
69
|
+
/**
|
|
70
|
+
* Refresh + persist under the per-credential lock. The refresh token is
|
|
71
|
+
* SINGLE-USE (rotated + invalidated on every refresh), so concurrent
|
|
72
|
+
* refreshers — a second stream in this process, the whisper-stt
|
|
73
|
+
* transcriber sharing this credential, or another moxxy process — must
|
|
74
|
+
* serialize and coalesce: whoever holds the lock refreshes once, everyone
|
|
75
|
+
* queued behind it adopts the rotated bundle instead of burning it.
|
|
76
|
+
*/
|
|
77
|
+
private refreshNow;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,aAAa,EACb,eAAe,EAChB,MAAM,YAAY,CAAC;AAQpB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA0B9C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE;;;;;;;OAOG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC1D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrD,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,wFAAwF;IACxF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAC;IAC1C;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,aAAc,YAAW,WAAW;IAC/C,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,MAAM,kDAAe;IAE9B,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAA8C;IACjF,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAoC;IAClE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAA4B;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAe;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,MAAM,GAAE,mBAAwB;IAmBrC,MAAM,CAAC,GAAG,EAAE,eAAe,GAAG,aAAa,CAAC,aAAa,CAAC;IAiH3D,WAAW,CACf,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAC,GACpE,OAAO,CAAC,MAAM,CAAC;IAiBlB,OAAO,CAAC,SAAS;YAUH,WAAW;IAWzB;;;;;;;OAOG;YACW,UAAU;CA4CzB"}
|