Core primitives
ChatGPTConfig, exported defaults, and the device-flow, token, and transport primitives in @opencoredev/loginwithchatgpt-core.
@opencoredev/loginwithchatgpt-core contains the primitives the other packages are built
on. Reach for it when building headless logins, custom
handlers, or tests. Typical apps never import it directly.
It has no dependencies and runs anywhere with Web-standard fetch and
crypto.
ChatGPTConfig
Every package accepts these fields and resolves them with resolveConfig():
| Option | Default | Notes |
|---|---|---|
clientId | the public Codex client id | Override only if you control a compatible client. |
issuer | "https://auth.openai.com" | Derives the OAuth, device-code, and token URLs. |
scope | "openid profile email offline_access" | offline_access is required for refresh tokens. |
codexBaseUrl | "https://chatgpt.com/backend-api/codex" | Base URL for models and responses. |
realtimeBaseUrl | "https://chatgpt.com" | ChatGPT web origin for the unsupported private voice transport. |
realtimeClientVersion | current observed ChatGPT web id | Sent to the private voice edge; can drift. |
realtimeClientBuild | current observed ChatGPT web build | Sent to the private voice edge; can drift. |
originator | "codex_cli_rs" | Sent as a header to upstream endpoints. |
clientVersion | "0.142.5" | Gates model availability; see client version drift. |
fetch | global fetch | Inject for testing, proxying, or exotic runtimes. |
The concrete defaults are exported as constants (DEFAULT_CLIENT_ID,
DEFAULT_ISSUER, DEFAULT_SCOPE, DEFAULT_CODEX_BASE_URL,
DEFAULT_ORIGINATOR, DEFAULT_CLIENT_VERSION, DEFAULT_MODEL). Treat them
as operational defaults, not guarantees. The upstream endpoints can change
independently of this package.
Device flow
import {
requestDeviceCode,
pollDeviceCode,
exchangeDeviceAuthorization,
waitForDeviceTokens,
resolveConfig,
} from "@opencoredev/loginwithchatgpt-core";
const config = resolveConfig();
const device = await requestDeviceCode(config);
// { deviceAuthId, userCode, verificationUrl, interval, expiresAt }pollDeviceCode(config, device)runs one poll and returns{ status: "pending" }or{ status: "authorized", … }.exchangeDeviceAuthorization(config, poll)trades an authorized poll result forChatGPTTokens.waitForDeviceTokens(config, device, options?)is the loop in one call, withsignal,onPoll, andintervalMsoptions.
Device codes live for 15 minutes (DEVICE_CODE_TTL_MS).
Tokens
interface ChatGPTTokens {
accessToken: string;
refreshToken?: string;
idToken?: string;
accountId?: string; // chatgpt_account_id claim
expiresAt?: number; // epoch ms
}refreshTokens(config, refreshToken)performs one refresh and throwsrefresh_token_invalidwhen the token is dead.ensureFreshTokens(config, tokens, { onRefresh?, force? })refreshes only when needed (60-second early margin), with a persistence callback.isAccessTokenExpired(tokens)is the same margin check, standalone.isRefreshTokenInvalid(error)is the type guard for the dead-refresh-token case.
JWT and PKCE helpers
parseUser(idToken)buildsChatGPTUser(accountId,email,name,plan) from id-token claims.decodeJwt(token),getTokenExpiry(token), andderiveAccountId(token)decode payloads without verifying signatures, so treat their output as claims, not proof.generatePkce(),createState(), andrandomToken(length?)support the loopback redirect flow (createAuthorizationUrl()plusexchangeAuthorizationCode()), which exists alongside the device flow for environments that can listen on localhost.
Codex transport
This layer makes AI SDK traffic acceptable to the ChatGPT-backed backend:
createCodexFetch({ config, getAuth, ...CodexResponsesOptions })returns a fetch-compatible function that rewrites URLs ontocodexBaseUrl, injects the bearer token andchatgpt-account-idheader, addsclient_version, and normalizes/responsesbodies.normalizeResponsesBody(body, options?)is the body rewrite by itself (rules).listCodexModels({ config, getAuth })andextractCodexModelSlugs(value)handle model listing and tolerant slug extraction.
Experimental private voice transport
These helpers target undocumented ChatGPT web routes, not the supported public
Realtime API. The /wm path rejects arbitrary application client_tools;
direct browser sessions do not receive the desktop app-server's handoff items.
Video and screen-sharing media are not exposed by these SDK helpers.
connectChatGPTRealtime(options?)owns browser microphone, WebRTC, remote audio, direct/wmevent decoding, controls, and optional local barge-in.connectChatGPTRealtimeAppServer(options?)uses the handler's authenticated app-server routes, subscribes to tool lifecycle events, resolves explicit confirmations, and closes the server process with the call.parseChatGPTRealtimeAppServerEvent(value)validates the app-server event catalog for custom clients.createChatGPTRealtimeCall({ config, getAuth, sdp, session })is the server-only SDP exchange primitive.buildChatGPTRealtimeSession(options?)maps typed camel-case options to the upstream session wire object.parseChatGPTRealtimeSessionOptions(value)validates the complete public session contract at JSON and untyped JavaScript boundaries.parseChatGPTRealtimeTranscript(event)extracts user speech and assistant captions;connectChatGPTRealtime()also exposes this asonTranscript.createChatGPTRealtimeAction(),encodeChatGPTRealtimeEvent(), andparseChatGPTRealtimeEvent()support custom WebRTC clients;getChatGPTRealtimePayload()normalizes nested payload envelopes.parseChatGPTRealtimeToolInvocation(),createChatGPTRealtimeToolUpdate(), andcreateChatGPTRealtimeToolResult()expose only the reserved direct/wmclient-tool lifecycle.CHATGPT_REALTIME_PATHS,CHATGPT_REALTIME_EVENT_TYPES, andCHATGPT_REALTIME_VOICESexpose the known routes, event catalog, and current documented voice names without closing the wire API to future values.
The full types, schema, modes, and lifecycle are in Experimental ChatGPT voice transport.
Store
KeyValueStore<T> and MemoryStore<T> are the
session-store contract used by
the server package, exported here so custom stores need no extra dependency.
Everything these functions can throw is listed in Error codes.