openai-codex-oauth 1.0.0 → 1.0.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/dist/index.js +606 -25
- package/dist/index.js.map +1 -1
- package/dist/node/index.js +107 -20
- package/dist/node/index.js.map +1 -1
- package/dist/proxy.js +102 -6
- package/dist/proxy.js.map +1 -1
- package/package.json +1 -2
- package/dist/chunk-GI2K7JPK.js +0 -526
- package/dist/chunk-GI2K7JPK.js.map +0 -1
- package/dist/chunk-SCKIRN2D.js +0 -58
- package/dist/chunk-SCKIRN2D.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/provider.ts","../src/stream-to-generate.ts"],"sourcesContent":["/**\n * @file provider.ts\n *\n * Vercel AI SDK provider implementation for OpenAI Codex OAuth.\n * This module creates a provider that can be used with the AI SDK's\n * generateText, streamText, and other AI functions.\n *\n * The provider wraps the Codex OAuth fetch implementation and adapts it\n * to the AI SDK provider interface.\n */\n\nimport { createOpenAI } from '@ai-sdk/openai';\nimport type { EmbeddingModelV3, ImageModelV3, LanguageModelV3, ProviderV3 } from '@ai-sdk/provider';\nimport { NoSuchModelError } from '@ai-sdk/provider';\nimport { createCodexOAuthFetch, DEFAULT_CODEX_BASE_URL } from './codex-fetch';\nimport { StreamOnlyLanguageModel } from './stream-to-generate';\nimport type { OpenAIOAuthProviderSettings } from './types';\n\n/** Model ID type for Codex OAuth models. */\nexport type OpenAIOAuthModelId = string;\n\n/**\n * AI SDK provider interface for OpenAI Codex OAuth-backed language models.\n * Provides language model, embedding model, and image model factories.\n */\nexport interface OpenAIOAuthProvider extends ProviderV3 {\n (modelId: OpenAIOAuthModelId): LanguageModelV3;\n languageModel(modelId: OpenAIOAuthModelId): LanguageModelV3;\n responses(modelId: OpenAIOAuthModelId): LanguageModelV3;\n embeddingModel(modelId: string): EmbeddingModelV3;\n imageModel(modelId: string): ImageModelV3;\n}\n\n/**\n * Create a Vercel AI SDK provider that sends Responses API calls through the\n * OpenAI Codex OAuth backend.\n */\nexport function createOpenAIOAuth(settings: OpenAIOAuthProviderSettings = {}): OpenAIOAuthProvider {\n const providerName = settings.name ?? 'openai-oauth';\n const oauthFetch = createCodexOAuthFetch(settings);\n const openai = createOpenAI({\n apiKey: 'oauth',\n baseURL: settings.baseURL ?? DEFAULT_CODEX_BASE_URL,\n name: providerName,\n fetch: oauthFetch,\n });\n\n const createLanguageModel = (modelId: OpenAIOAuthModelId) => new StreamOnlyLanguageModel(openai.responses(modelId as never));\n const provider = (modelId: OpenAIOAuthModelId) => createLanguageModel(modelId);\n\n provider.specificationVersion = 'v3' as const;\n provider.languageModel = createLanguageModel;\n provider.responses = createLanguageModel;\n provider.embeddingModel = (modelId: string) => {\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' });\n };\n provider.imageModel = (modelId: string) => {\n throw new NoSuchModelError({ modelId, modelType: 'imageModel' });\n };\n\n return provider as OpenAIOAuthProvider;\n}\n","/**\n * @file stream-to-generate.ts\n *\n * Provides utilities for converting streaming language model results\n * into non-streaming results. This is useful when the underlying provider\n * only supports streaming but the application needs synchronous generation.\n *\n * The AI SDK v3 provider interface requires both doGenerate and doStream,\n * but some backends (like Codex) only implement streaming. This module\n * bridges that gap by consuming the stream and assembling a complete result.\n */\n\nimport type {\n LanguageModelV3,\n LanguageModelV3Content,\n LanguageModelV3FinishReason,\n LanguageModelV3GenerateResult,\n LanguageModelV3StreamResult,\n LanguageModelV3Usage,\n SharedV3ProviderMetadata,\n SharedV3Warning,\n} from '@ai-sdk/provider';\n\n/** Creates an empty usage object with all fields undefined. */\nconst emptyUsage = (): LanguageModelV3Usage => ({\n inputTokens: {\n total: undefined,\n noCache: undefined,\n cacheRead: undefined,\n cacheWrite: undefined,\n },\n outputTokens: {\n total: undefined,\n text: undefined,\n reasoning: undefined,\n },\n});\n\n/**\n * Merges two provider metadata objects, with right values taking precedence\n * over left values when both are defined.\n */\nfunction mergeProviderMetadata(\n left: SharedV3ProviderMetadata | undefined,\n right: SharedV3ProviderMetadata | undefined,\n): SharedV3ProviderMetadata | undefined {\n if (left == null) return right;\n if (right == null) return left;\n\n const merged: SharedV3ProviderMetadata = { ...left };\n for (const [provider, value] of Object.entries(right)) {\n const existing = merged[provider];\n merged[provider] = existing == null ? value : { ...existing, ...value };\n }\n\n return merged;\n}\n\n/** Collect a LanguageModel stream result into a non-streaming generate result. */\nexport async function collectStreamGenerateResult(\n streamResult: LanguageModelV3StreamResult,\n): Promise<LanguageModelV3GenerateResult> {\n const reader = streamResult.stream.getReader();\n const content: LanguageModelV3Content[] = [];\n const warnings: SharedV3Warning[] = [];\n const activeTextById = new Map<string, Extract<LanguageModelV3Content, { type: 'text' }>>();\n const activeReasoningById = new Map<string, Extract<LanguageModelV3Content, { type: 'reasoning' }>>();\n\n let finishReason: LanguageModelV3FinishReason = { unified: 'other', raw: undefined };\n let usage: LanguageModelV3Usage = emptyUsage();\n let providerMetadata: SharedV3ProviderMetadata | undefined;\n let responseMetadata: LanguageModelV3GenerateResult['response'];\n\n try {\n while (true) {\n const { value: part, done } = await reader.read();\n if (done) {\n break;\n }\n\n switch (part.type) {\n case 'stream-start':\n warnings.push(...part.warnings);\n break;\n case 'response-metadata':\n responseMetadata = {\n id: part.id,\n timestamp: part.timestamp,\n modelId: part.modelId,\n };\n break;\n case 'text-start': {\n const textPart: Extract<LanguageModelV3Content, { type: 'text' }> = {\n type: 'text',\n text: '',\n providerMetadata: part.providerMetadata,\n };\n content.push(textPart);\n activeTextById.set(part.id, textPart);\n break;\n }\n case 'text-delta': {\n const existing = activeTextById.get(part.id);\n if (existing) {\n existing.text += part.delta;\n existing.providerMetadata = mergeProviderMetadata(existing.providerMetadata, part.providerMetadata);\n } else {\n const textPart: Extract<LanguageModelV3Content, { type: 'text' }> = {\n type: 'text',\n text: part.delta,\n providerMetadata: part.providerMetadata,\n };\n content.push(textPart);\n activeTextById.set(part.id, textPart);\n }\n break;\n }\n case 'text-end': {\n const existing = activeTextById.get(part.id);\n if (existing) {\n existing.providerMetadata = mergeProviderMetadata(existing.providerMetadata, part.providerMetadata);\n activeTextById.delete(part.id);\n }\n break;\n }\n case 'reasoning-start': {\n const reasoningPart: Extract<LanguageModelV3Content, { type: 'reasoning' }> = {\n type: 'reasoning',\n text: '',\n providerMetadata: part.providerMetadata,\n };\n content.push(reasoningPart);\n activeReasoningById.set(part.id, reasoningPart);\n break;\n }\n case 'reasoning-delta': {\n const existing = activeReasoningById.get(part.id);\n if (existing) {\n existing.text += part.delta;\n existing.providerMetadata = mergeProviderMetadata(existing.providerMetadata, part.providerMetadata);\n } else {\n const reasoningPart: Extract<LanguageModelV3Content, { type: 'reasoning' }> = {\n type: 'reasoning',\n text: part.delta,\n providerMetadata: part.providerMetadata,\n };\n content.push(reasoningPart);\n activeReasoningById.set(part.id, reasoningPart);\n }\n break;\n }\n case 'reasoning-end': {\n const existing = activeReasoningById.get(part.id);\n if (existing) {\n existing.providerMetadata = mergeProviderMetadata(existing.providerMetadata, part.providerMetadata);\n activeReasoningById.delete(part.id);\n }\n break;\n }\n case 'tool-call':\n case 'tool-result':\n case 'tool-approval-request':\n case 'file':\n case 'source':\n content.push(part);\n break;\n case 'finish':\n finishReason = part.finishReason;\n usage = part.usage;\n providerMetadata = part.providerMetadata;\n break;\n case 'error':\n throw part.error instanceof Error ? part.error : new Error('Streaming request failed.', { cause: part.error });\n case 'tool-input-start':\n case 'tool-input-delta':\n case 'tool-input-end':\n case 'raw':\n break;\n default:\n part satisfies never;\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n return {\n content,\n finishReason,\n usage,\n providerMetadata,\n request: streamResult.request,\n response: responseMetadata == null && streamResult.response?.headers == null\n ? undefined\n : {\n ...(responseMetadata ?? {}),\n ...(streamResult.response?.headers == null ? {} : { headers: streamResult.response.headers }),\n },\n warnings,\n };\n}\n\n/** Language model wrapper that implements `doGenerate` by consuming `doStream`. */\nexport class StreamOnlyLanguageModel implements LanguageModelV3 {\n readonly specificationVersion = 'v3';\n readonly provider: string;\n readonly modelId: string;\n readonly supportedUrls: LanguageModelV3['supportedUrls'];\n\n constructor(private readonly inner: LanguageModelV3) {\n this.provider = inner.provider;\n this.modelId = inner.modelId;\n this.supportedUrls = inner.supportedUrls;\n }\n\n async doGenerate(options: Parameters<LanguageModelV3['doGenerate']>[0]): Promise<LanguageModelV3GenerateResult> {\n return collectStreamGenerateResult(await this.inner.doStream(options));\n }\n\n doStream(options: Parameters<LanguageModelV3['doStream']>[0]) {\n return this.inner.doStream(options);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAWA,SAAS,oBAAoB;AAE7B,SAAS,wBAAwB;;;ACWjC,IAAM,aAAa,OAA6B;AAAA,EAC9C,aAAa;AAAA,IACX,OAAO;AAAA,IACP,SAAS;AAAA,IACT,WAAW;AAAA,IACX,YAAY;AAAA,EACd;AAAA,EACA,cAAc;AAAA,IACZ,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AACF;AAMA,SAAS,sBACP,MACA,OACsC;AACtC,MAAI,QAAQ,KAAM,QAAO;AACzB,MAAI,SAAS,KAAM,QAAO;AAE1B,QAAM,SAAmC,EAAE,GAAG,KAAK;AACnD,aAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AACrD,UAAM,WAAW,OAAO,QAAQ;AAChC,WAAO,QAAQ,IAAI,YAAY,OAAO,QAAQ,EAAE,GAAG,UAAU,GAAG,MAAM;AAAA,EACxE;AAEA,SAAO;AACT;AAGA,eAAsB,4BACpB,cACwC;AACxC,QAAM,SAAS,aAAa,OAAO,UAAU;AAC7C,QAAM,UAAoC,CAAC;AAC3C,QAAM,WAA8B,CAAC;AACrC,QAAM,iBAAiB,oBAAI,IAA+D;AAC1F,QAAM,sBAAsB,oBAAI,IAAoE;AAEpG,MAAI,eAA4C,EAAE,SAAS,SAAS,KAAK,OAAU;AACnF,MAAI,QAA8B,WAAW;AAC7C,MAAI;AACJ,MAAI;AAEJ,MAAI;AACF,WAAO,MAAM;AACX,YAAM,EAAE,OAAO,MAAM,KAAK,IAAI,MAAM,OAAO,KAAK;AAChD,UAAI,MAAM;AACR;AAAA,MACF;AAEA,cAAQ,KAAK,MAAM;AAAA,QACjB,KAAK;AACH,mBAAS,KAAK,GAAG,KAAK,QAAQ;AAC9B;AAAA,QACF,KAAK;AACH,6BAAmB;AAAA,YACjB,IAAI,KAAK;AAAA,YACT,WAAW,KAAK;AAAA,YAChB,SAAS,KAAK;AAAA,UAChB;AACA;AAAA,QACF,KAAK,cAAc;AACjB,gBAAM,WAA8D;AAAA,YAClE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,kBAAkB,KAAK;AAAA,UACzB;AACA,kBAAQ,KAAK,QAAQ;AACrB,yBAAe,IAAI,KAAK,IAAI,QAAQ;AACpC;AAAA,QACF;AAAA,QACA,KAAK,cAAc;AACjB,gBAAM,WAAW,eAAe,IAAI,KAAK,EAAE;AAC3C,cAAI,UAAU;AACZ,qBAAS,QAAQ,KAAK;AACtB,qBAAS,mBAAmB,sBAAsB,SAAS,kBAAkB,KAAK,gBAAgB;AAAA,UACpG,OAAO;AACL,kBAAM,WAA8D;AAAA,cAClE,MAAM;AAAA,cACN,MAAM,KAAK;AAAA,cACX,kBAAkB,KAAK;AAAA,YACzB;AACA,oBAAQ,KAAK,QAAQ;AACrB,2BAAe,IAAI,KAAK,IAAI,QAAQ;AAAA,UACtC;AACA;AAAA,QACF;AAAA,QACA,KAAK,YAAY;AACf,gBAAM,WAAW,eAAe,IAAI,KAAK,EAAE;AAC3C,cAAI,UAAU;AACZ,qBAAS,mBAAmB,sBAAsB,SAAS,kBAAkB,KAAK,gBAAgB;AAClG,2BAAe,OAAO,KAAK,EAAE;AAAA,UAC/B;AACA;AAAA,QACF;AAAA,QACA,KAAK,mBAAmB;AACtB,gBAAM,gBAAwE;AAAA,YAC5E,MAAM;AAAA,YACN,MAAM;AAAA,YACN,kBAAkB,KAAK;AAAA,UACzB;AACA,kBAAQ,KAAK,aAAa;AAC1B,8BAAoB,IAAI,KAAK,IAAI,aAAa;AAC9C;AAAA,QACF;AAAA,QACA,KAAK,mBAAmB;AACtB,gBAAM,WAAW,oBAAoB,IAAI,KAAK,EAAE;AAChD,cAAI,UAAU;AACZ,qBAAS,QAAQ,KAAK;AACtB,qBAAS,mBAAmB,sBAAsB,SAAS,kBAAkB,KAAK,gBAAgB;AAAA,UACpG,OAAO;AACL,kBAAM,gBAAwE;AAAA,cAC5E,MAAM;AAAA,cACN,MAAM,KAAK;AAAA,cACX,kBAAkB,KAAK;AAAA,YACzB;AACA,oBAAQ,KAAK,aAAa;AAC1B,gCAAoB,IAAI,KAAK,IAAI,aAAa;AAAA,UAChD;AACA;AAAA,QACF;AAAA,QACA,KAAK,iBAAiB;AACpB,gBAAM,WAAW,oBAAoB,IAAI,KAAK,EAAE;AAChD,cAAI,UAAU;AACZ,qBAAS,mBAAmB,sBAAsB,SAAS,kBAAkB,KAAK,gBAAgB;AAClG,gCAAoB,OAAO,KAAK,EAAE;AAAA,UACpC;AACA;AAAA,QACF;AAAA,QACA,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AACH,kBAAQ,KAAK,IAAI;AACjB;AAAA,QACF,KAAK;AACH,yBAAe,KAAK;AACpB,kBAAQ,KAAK;AACb,6BAAmB,KAAK;AACxB;AAAA,QACF,KAAK;AACH,gBAAM,KAAK,iBAAiB,QAAQ,KAAK,QAAQ,IAAI,MAAM,6BAA6B,EAAE,OAAO,KAAK,MAAM,CAAC;AAAA,QAC/G,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AACH;AAAA,QACF;AACE;AAAA,MACJ;AAAA,IACF;AAAA,EACF,UAAE;AACA,WAAO,YAAY;AAAA,EACrB;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,aAAa;AAAA,IACtB,UAAU,oBAAoB,QAAQ,aAAa,UAAU,WAAW,OACpE,SACA;AAAA,MACE,GAAI,oBAAoB,CAAC;AAAA,MACzB,GAAI,aAAa,UAAU,WAAW,OAAO,CAAC,IAAI,EAAE,SAAS,aAAa,SAAS,QAAQ;AAAA,IAC7F;AAAA,IACJ;AAAA,EACF;AACF;AAGO,IAAM,0BAAN,MAAyD;AAAA,EAM9D,YAA6B,OAAwB;AAAxB;AAC3B,SAAK,WAAW,MAAM;AACtB,SAAK,UAAU,MAAM;AACrB,SAAK,gBAAgB,MAAM;AAAA,EAC7B;AAAA,EAJ6B;AAAA,EALpB,uBAAuB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EAQT,MAAM,WAAW,SAA+F;AAC9G,WAAO,4BAA4B,MAAM,KAAK,MAAM,SAAS,OAAO,CAAC;AAAA,EACvE;AAAA,EAEA,SAAS,SAAqD;AAC5D,WAAO,KAAK,MAAM,SAAS,OAAO;AAAA,EACpC;AACF;;;ADzLO,SAAS,kBAAkB,WAAwC,CAAC,GAAwB;AACjG,QAAM,eAAe,SAAS,QAAQ;AACtC,QAAM,aAAa,sBAAsB,QAAQ;AACjD,QAAM,SAAS,aAAa;AAAA,IAC1B,QAAQ;AAAA,IACR,SAAS,SAAS,WAAW;AAAA,IAC7B,MAAM;AAAA,IACN,OAAO;AAAA,EACT,CAAC;AAED,QAAM,sBAAsB,CAAC,YAAgC,IAAI,wBAAwB,OAAO,UAAU,OAAgB,CAAC;AAC3H,QAAM,WAAW,CAAC,YAAgC,oBAAoB,OAAO;AAE7E,WAAS,uBAAuB;AAChC,WAAS,gBAAgB;AACzB,WAAS,YAAY;AACrB,WAAS,iBAAiB,CAAC,YAAoB;AAC7C,UAAM,IAAI,iBAAiB,EAAE,SAAS,WAAW,iBAAiB,CAAC;AAAA,EACrE;AACA,WAAS,aAAa,CAAC,YAAoB;AACzC,UAAM,IAAI,iBAAiB,EAAE,SAAS,WAAW,aAAa,CAAC;AAAA,EACjE;AAEA,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/jwt.ts","../src/types.ts","../src/device-flow.ts","../src/memory-token-store.ts","../src/codex-fetch.ts","../src/proxy.ts","../src/provider.ts","../src/stream-to-generate.ts"],"sourcesContent":["export { createCodexOAuthClient, createCodexOAuthFetch, DEFAULT_CODEX_BASE_URL, normalizeCodexResponsesBody } from './codex-fetch';\nexport { createOpenAIOAuthProxy } from './proxy';\nexport type { OpenAIOAuthProxyOptions } from './proxy';\nexport { DEFAULT_CLIENT_ID, DEFAULT_ISSUER, refreshOpenAITokens, startOpenAIDeviceFlow } from './device-flow';\nexport { deriveAccountId, deriveExpiresAt, parseJwtClaims } from './jwt';\nexport { createMemoryTokenStore } from './memory-token-store';\nexport { createOpenAIOAuth } from './provider';\nexport type { OpenAIOAuthModelId, OpenAIOAuthProvider } from './provider';\nexport type {\n FetchLike,\n OpenAIDeviceFlow,\n OpenAIDeviceFlowOptions,\n OpenAIOAuthProviderSettings,\n OpenAIOAuthSettings,\n OpenAIOAuthTokens,\n TokenStore,\n} from './types';\nexport { OpenAIOAuthError } from './types';\n","/**\n * @file jwt.ts\n *\n * JWT (JSON Web Token) parsing utilities for OpenAI OAuth.\n * These functions decode and extract claims from JWTs without\n * verifying signatures (since we trust the OAuth issuer).\n */\n\nimport type { OpenAIOAuthTokens } from './types';\n\n/**\n * Decodes a Base64URL-encoded string to UTF-8 text.\n * Handles both browser (atob) and Node.js (Buffer) environments.\n */\nfunction decodeBase64Url(value: string): string | undefined {\n try {\n const base64 = value.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);\n\n if (typeof globalThis.atob === 'function') {\n const binary = globalThis.atob(padded);\n const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));\n return new TextDecoder().decode(bytes);\n }\n\n return Buffer.from(padded, 'base64').toString('utf8');\n } catch {\n return undefined;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Parse the JSON payload from a JWT without verifying the signature. */\nexport function parseJwtClaims(token: string | undefined): Record<string, unknown> | undefined {\n if (!token?.includes('.')) {\n return undefined;\n }\n\n const parts = token.split('.');\n if (parts.length !== 3 || !parts[1]) {\n return undefined;\n }\n\n const payload = decodeBase64Url(parts[1]);\n if (!payload) {\n return undefined;\n }\n\n try {\n const parsed = JSON.parse(payload) as unknown;\n return isRecord(parsed) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Derive the ChatGPT account id from OpenAI JWT claims when present. */\nexport function deriveAccountId(...tokens: Array<string | undefined>): string | undefined {\n for (const token of tokens) {\n const claims = parseJwtClaims(token);\n const auth = claims?.['https://api.openai.com/auth'];\n\n if (isRecord(auth) && typeof auth.chatgpt_account_id === 'string' && auth.chatgpt_account_id.length > 0) {\n return auth.chatgpt_account_id;\n }\n }\n\n return undefined;\n}\n\n/** Derive JWT expiry as epoch milliseconds from the `exp` claim. */\nexport function deriveExpiresAt(token: string | undefined): number | undefined {\n const claims = parseJwtClaims(token);\n return typeof claims?.exp === 'number' ? claims.exp * 1000 : undefined;\n}\n","/**\n * @file types.ts\n *\n * Core type definitions for the OpenAI Codex OAuth library.\n * These types define the contract for authentication, token management,\n * and configuration across all modules.\n */\n\n/**\n * Type alias for the global fetch function signature.\n * Used to allow custom fetch implementations in various environments.\n */\nexport type FetchLike = typeof globalThis.fetch;\n\n/**\n * OAuth credentials used to call the OpenAI Codex backend.\n * Treat these as password-equivalent secrets.\n */\nexport type OpenAIOAuthTokens = {\n /** Bearer token sent to the Codex backend. */\n accessToken: string;\n /** Refresh token used to obtain a new access token before expiry. */\n refreshToken?: string;\n /** Access-token expiry as epoch milliseconds. */\n expiresAt?: number;\n /** Optional OpenID token. Used to derive `accountId` when available. */\n idToken?: string;\n /** ChatGPT account id required by the Codex backend. */\n accountId?: string;\n};\n\n/** Async storage interface for loading and persisting OAuth credentials. */\nexport type TokenStore = {\n /** Load the latest known credentials. Return `undefined` when the user is not signed in. */\n load(): Promise<OpenAIOAuthTokens | undefined>;\n /** Persist new credentials after sign-in or refresh. */\n save(tokens: OpenAIOAuthTokens): Promise<void>;\n};\n\n/** In-progress OpenAI Codex device authorization flow. */\nexport type OpenAIDeviceFlow = {\n providerId: 'openai';\n /** URL the user should open to authorize the device flow. */\n url: string;\n /** User code to enter on the authorization page. */\n code: string;\n /** Human-readable instruction string for command-line or app UI. */\n instructions: string;\n /** Poll until authorization completes, exchange the grant, and return OAuth tokens. */\n complete(): Promise<OpenAIOAuthTokens>;\n};\n\n/** Options for starting OpenAI's Codex device OAuth flow. */\nexport type OpenAIDeviceFlowOptions = {\n /** Custom fetch implementation, useful for tests and non-standard runtimes. */\n fetch?: FetchLike;\n /** Clock override returning epoch milliseconds. */\n now?: () => number;\n /** Sleep override used between polling attempts. */\n sleep?: (ms: number) => Promise<void>;\n /** OAuth client id. Defaults to the Codex client id. */\n clientId?: string;\n /** OAuth issuer. Defaults to `https://auth.openai.com`. */\n issuer?: string;\n /** Optional store that receives tokens after successful authorization. */\n tokenStore?: TokenStore;\n};\n\n/** Shared settings for Codex OAuth fetch, client, and AI SDK provider creation. */\nexport type OpenAIOAuthSettings = {\n /** Custom fetch implementation for both token refresh and Codex requests. */\n fetch?: FetchLike;\n /** Secure token storage used to load and save refreshed credentials. */\n tokenStore?: TokenStore;\n /** Inline tokens for scripts/tests. Prefer `tokenStore` for production apps. */\n tokens?: OpenAIOAuthTokens;\n /** OAuth client id. Defaults to the Codex client id. */\n clientId?: string;\n /** OAuth issuer. Defaults to `https://auth.openai.com`. */\n issuer?: string;\n /** Override token endpoint for refresh. */\n tokenUrl?: string;\n /** Codex backend base URL. Defaults to `https://chatgpt.com/backend-api/codex`. */\n baseURL?: string;\n /** Browser proxy base URL for Codex requests. Defaults to `/api/proxy/openai/codex` in browsers. Pass `false` to disable. */\n browserProxyBaseUrl?: string | false;\n /** Additional headers sent to the Codex backend before OAuth headers are applied. */\n headers?: Record<string, string>;\n /** Default `instructions` value for Responses requests that omit it. */\n instructions?: string;\n /** Default OpenAI Responses `store` value. Defaults to `false`. */\n store?: boolean;\n /** Upstream `originator` header. Pass `false` to omit it. */\n originator?: string | false;\n /** Refresh access tokens this many milliseconds before expiry. */\n refreshMarginMs?: number;\n /** Clock override returning epoch milliseconds. */\n now?: () => number;\n /** Called after a successful token refresh. */\n onTokens?: (tokens: OpenAIOAuthTokens) => void | Promise<void>;\n};\n\n/** Settings for the AI SDK provider factory. */\nexport type OpenAIOAuthProviderSettings = OpenAIOAuthSettings & {\n /** Provider name exposed to AI SDK telemetry and metadata. */\n name?: string;\n};\n\n/** Error class used for OAuth, token, and credential setup failures. */\nexport class OpenAIOAuthError extends Error {\n readonly code: string;\n\n constructor(code: string, message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'OpenAIOAuthError';\n this.code = code;\n }\n}\n","/**\n * @file device-flow.ts\n *\n * Implements the OAuth 2.0 Device Authorization Grant flow for OpenAI Codex.\n * This flow is designed for CLI tools and headless applications where\n * the user authorizes on a separate device with a browser.\n *\n * The flow:\n * 1. Client requests a device code from the authorization server\n * 2. User visits the authorization URL and enters the displayed code\n * 3. Client polls for authorization completion\n * 4. On success, client exchanges the authorization grant for tokens\n */\n\nimport { deriveAccountId, deriveExpiresAt } from './jwt';\nimport type { FetchLike, OpenAIDeviceFlow, OpenAIDeviceFlowOptions, OpenAIOAuthTokens } from './types';\nimport { OpenAIOAuthError } from './types';\n\n/** Default OAuth client ID for Codex/ChatGPT. */\nconst DEFAULT_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';\n/** Default OAuth issuer/authorization server URL. */\nconst DEFAULT_ISSUER = 'https://auth.openai.com';\n/** Additional wait time added to the poll interval to avoid race conditions. */\nconst POLL_BUFFER_MS = 3000;\n\n/**\n * Resolves the fetch function to use for OAuth requests.\n */\nfunction pickFetch(customFetch?: FetchLike): FetchLike {\n if (typeof customFetch === 'function') {\n return customFetch;\n }\n\n if (typeof globalThis.fetch === 'function') {\n return globalThis.fetch.bind(globalThis);\n }\n\n throw new OpenAIOAuthError('fetch_required', 'A fetch implementation is required for OpenAI OAuth.');\n}\n\n/**\n * Start OpenAI's Codex device OAuth flow.\n *\n * The returned flow contains a URL and user code for authorization. Call\n * `flow.complete()` after showing those values to poll for authorization and\n * exchange the grant for OAuth tokens.\n */\nexport async function startOpenAIDeviceFlow(options: OpenAIDeviceFlowOptions = {}): Promise<OpenAIDeviceFlow> {\n const fetch = pickFetch(options.fetch);\n const issuer = (options.issuer ?? DEFAULT_ISSUER).replace(/\\/$/, '');\n const clientId = options.clientId ?? DEFAULT_CLIENT_ID;\n const now = options.now ?? (() => Date.now());\n const sleep = options.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n\n const codeResponse = await fetch(`${issuer}/api/accounts/deviceauth/usercode`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ client_id: clientId }),\n });\n\n if (!codeResponse.ok) {\n throw new OpenAIOAuthError('auth_failed', 'Failed to initiate OpenAI authorization.');\n }\n\n const device = (await codeResponse.json()) as {\n device_auth_id?: string;\n user_code?: string;\n interval?: string | number;\n };\n\n if (!device.device_auth_id || !device.user_code) {\n throw new OpenAIOAuthError('auth_failed', 'OpenAI authorization response did not include a device code.');\n }\n\n const intervalSeconds = typeof device.interval === 'number' ? device.interval : parseInt(device.interval ?? '5', 10);\n const intervalMs = Math.max(Number.isFinite(intervalSeconds) ? intervalSeconds : 5, 1) * 1000;\n\n return {\n providerId: 'openai',\n url: `${issuer}/codex/device`,\n code: device.user_code,\n instructions: `Enter code: ${device.user_code}`,\n async complete() {\n while (true) {\n const poll = await fetch(`${issuer}/api/accounts/deviceauth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n device_auth_id: device.device_auth_id,\n user_code: device.user_code,\n }),\n });\n\n if (poll.ok) {\n const grant = (await poll.json()) as {\n authorization_code?: string;\n code_verifier?: string;\n };\n\n if (!grant.authorization_code || !grant.code_verifier) {\n throw new OpenAIOAuthError('auth_failed', 'OpenAI authorization grant was incomplete.');\n }\n\n const tokens = await exchangeAuthorizationCode({\n fetch,\n issuer,\n clientId,\n authorizationCode: grant.authorization_code,\n codeVerifier: grant.code_verifier,\n now,\n });\n\n await options.tokenStore?.save(tokens);\n return tokens;\n }\n\n if (poll.status !== 403 && poll.status !== 404) {\n throw new OpenAIOAuthError('auth_failed', 'OpenAI OAuth authorization failed.');\n }\n\n await sleep(intervalMs + POLL_BUFFER_MS);\n }\n },\n };\n}\n\nasync function exchangeAuthorizationCode({\n fetch,\n issuer,\n clientId,\n authorizationCode,\n codeVerifier,\n now,\n}: {\n fetch: FetchLike;\n issuer: string;\n clientId: string;\n authorizationCode: string;\n codeVerifier: string;\n now: () => number;\n}): Promise<OpenAIOAuthTokens> {\n const response = await fetch(`${issuer}/oauth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: authorizationCode,\n redirect_uri: `${issuer}/deviceauth/callback`,\n client_id: clientId,\n code_verifier: codeVerifier,\n }).toString(),\n });\n\n if (!response.ok) {\n throw new OpenAIOAuthError('auth_failed', 'OpenAI token exchange failed.');\n }\n\n const token = (await response.json()) as {\n refresh_token?: string;\n access_token?: string;\n id_token?: string;\n expires_in?: number;\n };\n\n if (!token.access_token) {\n throw new OpenAIOAuthError('auth_failed', 'OpenAI token exchange did not return an access token.');\n }\n\n return {\n refreshToken: token.refresh_token,\n accessToken: token.access_token,\n idToken: token.id_token,\n expiresAt: now() + (token.expires_in ?? 3600) * 1000,\n accountId: deriveAccountId(token.id_token, token.access_token),\n };\n}\n\n/**\n * Refresh OpenAI OAuth credentials with a refresh token.\n *\n * Returns `undefined` when refresh is not possible or the server rejects the\n * refresh request. Callers should treat expired credentials as unusable when\n * this returns `undefined`.\n */\nexport async function refreshOpenAITokens({\n tokens,\n fetch,\n clientId = DEFAULT_CLIENT_ID,\n issuer = DEFAULT_ISSUER,\n tokenUrl,\n now = () => Date.now(),\n}: {\n tokens: OpenAIOAuthTokens;\n fetch: FetchLike;\n clientId?: string;\n issuer?: string;\n tokenUrl?: string;\n now?: () => number;\n}): Promise<OpenAIOAuthTokens | undefined> {\n if (!tokens.refreshToken) {\n return undefined;\n }\n\n const resolvedIssuer = issuer.replace(/\\/$/, '');\n const response = await fetch(tokenUrl ?? `${resolvedIssuer}/oauth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: tokens.refreshToken,\n client_id: clientId,\n scope: 'openid profile email offline_access',\n }).toString(),\n });\n\n if (!response.ok) {\n return undefined;\n }\n\n const payload = (await response.json()) as {\n access_token?: string;\n refresh_token?: string;\n id_token?: string;\n expires_in?: number;\n };\n\n if (!payload.access_token) {\n return undefined;\n }\n\n const next: OpenAIOAuthTokens = {\n accessToken: payload.access_token,\n refreshToken: payload.refresh_token ?? tokens.refreshToken,\n idToken: payload.id_token ?? tokens.idToken,\n expiresAt: now() + (payload.expires_in ?? 3600) * 1000,\n };\n next.accountId = deriveAccountId(next.idToken, next.accessToken) ?? tokens.accountId;\n\n return next;\n}\n\nexport { DEFAULT_CLIENT_ID, DEFAULT_ISSUER };\n","/**\n * @file memory-token-store.ts\n *\n * In-memory token storage implementation.\n * Useful for testing and short-lived processes that don't need\n * persistent token storage.\n */\n\nimport type { OpenAIOAuthTokens, TokenStore } from './types';\n\n/**\n * Create an in-memory token store.\n *\n * This is useful for tests and short-lived scripts. It does not persist tokens\n * across process restarts.\n */\nexport function createMemoryTokenStore(initial?: OpenAIOAuthTokens): TokenStore {\n let current = initial;\n\n return {\n async load() {\n return current;\n },\n async save(tokens) {\n current = tokens;\n },\n };\n}\n","/**\n * @file codex-fetch.ts\n *\n * Core module that provides the fetch wrapper for authenticated Codex requests.\n * This is the main entry point for making OAuth-authenticated requests to\n * the OpenAI Codex backend.\n *\n * Key features:\n * - Automatic token management and refresh before expiry\n * - Request/response normalization for Codex API compatibility\n * - Browser proxy URL resolution for cross-origin requests\n * - Account ID derivation from JWT claims\n */\n\nimport { refreshOpenAITokens } from './device-flow';\nimport { deriveAccountId, deriveExpiresAt } from './jwt';\nimport { createMemoryTokenStore } from './memory-token-store';\nimport type { FetchLike, OpenAIOAuthSettings, OpenAIOAuthTokens, TokenStore } from './types';\nimport { OpenAIOAuthError } from './types';\n\n/** Default OpenAI Codex backend base URL. */\nexport const DEFAULT_CODEX_BASE_URL = 'https://chatgpt.com/backend-api/codex';\n/** Default proxy base path when running in a browser environment. */\nconst DEFAULT_BROWSER_PROXY_BASE_URL = '/api/proxy/openai/codex';\n/** Default time (5 minutes) before expiry to trigger token refresh. */\nconst DEFAULT_REFRESH_MARGIN_MS = 5 * 60 * 1000;\n/** Default empty instructions value for Responses API requests. */\nconst DEFAULT_INSTRUCTIONS = '';\n/** Default originator header value identifying this library. */\nconst DEFAULT_ORIGINATOR = 'openai-codex-oauth';\n\n/**\n * Parsed request components used internally for request transformation.\n */\ntype RequestParts = {\n url: string;\n method?: string;\n headers: Headers;\n body?: BodyInit | null;\n signal?: AbortSignal | null;\n};\n\n/**\n * Resolves the fetch function to use for OAuth and Codex requests.\n * Prefers a custom fetch if provided, falls back to globalThis.fetch.\n */\nfunction pickFetch(customFetch?: FetchLike): FetchLike {\n if (typeof customFetch === 'function') {\n return customFetch;\n }\n\n if (typeof globalThis.fetch === 'function') {\n return globalThis.fetch.bind(globalThis);\n }\n\n throw new OpenAIOAuthError('fetch_required', 'A fetch implementation is required for OpenAI OAuth.');\n}\n\n/** Removes trailing slash from a URL string for consistent handling. */\nfunction withoutTrailingSlash(value: string): string {\n return value.replace(/\\/$/, '');\n}\n\n/** Resolves the base URL for Codex requests, defaulting to the official endpoint. */\nfunction resolveBaseURL(baseURL?: string): string {\n return withoutTrailingSlash(baseURL ?? DEFAULT_CODEX_BASE_URL);\n}\n\n/**\n * Creates a token store based on provided settings.\n * Prefers explicit tokenStore, then inline tokens, then throws an error.\n */\nfunction createStore(settings: OpenAIOAuthSettings): TokenStore {\n if (settings.tokenStore) {\n return settings.tokenStore;\n }\n\n if (settings.tokens) {\n return createMemoryTokenStore(settings.tokens);\n }\n\n throw new OpenAIOAuthError(\n 'tokens_required',\n 'OpenAI OAuth tokens are required. Pass `tokens`, `tokenStore`, or use `createCodexAuthFileStore` from `@tolksyn/openai-oauth/node`.',\n );\n}\n\n/**\n * Determines whether a token needs refresh based on expiry time.\n * Considers the marginMs parameter to refresh proactively before actual expiry.\n */\nfunction needsRefresh(tokens: OpenAIOAuthTokens, now: number, marginMs: number): boolean {\n const expiresAt = tokens.expiresAt ?? deriveExpiresAt(tokens.accessToken);\n if (!tokens.accessToken) {\n return true;\n }\n\n if (expiresAt == null || expiresAt <= 0) {\n return false;\n }\n\n return expiresAt <= now + marginMs;\n}\n\n/**\n * Checks if a token has already expired based on current time.\n */\nfunction hasExpired(tokens: OpenAIOAuthTokens, now: number): boolean {\n const expiresAt = tokens.expiresAt ?? deriveExpiresAt(tokens.accessToken);\n return expiresAt != null && expiresAt > 0 && expiresAt <= now;\n}\n\n/**\n * Manages OAuth token lifecycle including loading, caching, refresh, and validation.\n * Uses a promise coalescing pattern to handle concurrent token requests efficiently.\n */\nclass TokenManager {\n /** Promise for an in-flight token refresh to coalesce concurrent requests. */\n private inflight?: Promise<OpenAIOAuthTokens>;\n /** Cached current tokens to avoid redundant store loads. */\n private current?: OpenAIOAuthTokens;\n\n constructor(\n private readonly settings: OpenAIOAuthSettings,\n private readonly store: TokenStore,\n private readonly fetch: FetchLike,\n ) {}\n\n /**\n * Gets valid OAuth tokens, triggering refresh if needed.\n * Coalesces concurrent requests to avoid duplicate refresh calls.\n */\n async get(): Promise<OpenAIOAuthTokens> {\n if (this.inflight) {\n return this.inflight;\n }\n\n this.inflight = this.loadFresh()\n .then((tokens) => {\n this.current = tokens;\n this.inflight = undefined;\n return tokens;\n })\n .catch((error) => {\n this.inflight = undefined;\n throw error;\n });\n\n return this.inflight;\n }\n\n /**\n * Loads fresh tokens, optionally refreshing if expired or near expiry.\n * Derives account ID from JWT claims if not explicitly provided.\n */\n private async loadFresh(): Promise<OpenAIOAuthTokens> {\n let tokens = this.current ?? (await this.store.load());\n if (!tokens?.accessToken) {\n throw new OpenAIOAuthError('auth_failed', 'OpenAI OAuth access token is missing.');\n }\n\n const now = this.settings.now?.() ?? Date.now();\n const refreshMarginMs = this.settings.refreshMarginMs ?? DEFAULT_REFRESH_MARGIN_MS;\n\n if (needsRefresh(tokens, now, refreshMarginMs)) {\n const refreshed = await refreshOpenAITokens({\n tokens,\n fetch: this.fetch,\n clientId: this.settings.clientId,\n issuer: this.settings.issuer,\n tokenUrl: this.settings.tokenUrl,\n now: this.settings.now,\n });\n\n if (refreshed) {\n tokens = refreshed;\n await this.store.save(tokens);\n await this.settings.onTokens?.(tokens);\n } else if (hasExpired(tokens, now)) {\n throw new OpenAIOAuthError('auth_failed', 'OpenAI OAuth access token expired and refresh failed.');\n }\n }\n\n const accountId = tokens.accountId ?? deriveAccountId(tokens.idToken, tokens.accessToken);\n if (!accountId) {\n throw new OpenAIOAuthError(\n 'account_id_missing',\n 'OpenAI OAuth account id is missing. Store `accountId` with the tokens or include an id_token with the ChatGPT account claim.',\n );\n }\n\n return {\n ...tokens,\n accountId,\n };\n }\n}\n\n/**\n * Resolves a target URL relative to the Codex base URL.\n * Handles path rewriting from `/v1/responses` to Codex backend paths.\n */\nfunction resolveTargetUrl(input: string, baseURL: string): string {\n const base = new URL(baseURL);\n const parsed = /^https?:\\/\\//.test(input) ? new URL(input) : new URL(input, 'https://codex.invalid');\n let pathname = parsed.pathname;\n const basePath = withoutTrailingSlash(base.pathname);\n\n if (pathname === basePath) {\n pathname = '/';\n } else if (basePath && pathname.startsWith(`${basePath}/`)) {\n pathname = pathname.slice(basePath.length);\n }\n\n if (pathname === '/v1') {\n pathname = '/';\n } else if (pathname.startsWith('/v1/')) {\n pathname = pathname.slice(3);\n }\n\n if (!pathname.startsWith('/')) {\n pathname = `/${pathname}`;\n }\n\n return `${base.origin}${basePath}${pathname}${parsed.search}`;\n}\n\n/**\n * Detects the browser's origin from window.location if available.\n * Returns undefined in non-browser environments.\n */\nfunction browserOrigin(): string | undefined {\n const maybeWindow = (globalThis as { window?: { location?: { origin?: string } } }).window;\n return typeof maybeWindow?.location?.origin === 'string' ? maybeWindow.location.origin.replace(/\\/$/, '') : undefined;\n}\n\n/**\n * Resolves a proxy URL for browser environments to handle cross-origin requests.\n * When running in a browser, direct Codex requests may fail CORS, so we proxy\n * through a local path that forwards to the Codex backend.\n */\nfunction resolveBrowserProxyUrl(target: URL, baseURL: string, settings: OpenAIOAuthSettings): string | undefined {\n const origin = browserOrigin();\n if (!origin || settings.browserProxyBaseUrl === false) {\n return undefined;\n }\n\n const proxyBase = settings.browserProxyBaseUrl ?? DEFAULT_BROWSER_PROXY_BASE_URL;\n const absoluteProxyBase = /^https?:\\/\\//.test(proxyBase) ? proxyBase.replace(/\\/$/, '') : `${origin}${proxyBase.startsWith('/') ? '' : '/'}${proxyBase}`.replace(/\\/$/, '');\n const upstreamBasePath = withoutTrailingSlash(new URL(baseURL).pathname);\n let pathname = target.pathname;\n\n if (upstreamBasePath && pathname.startsWith(`${upstreamBasePath}/`)) {\n pathname = pathname.slice(upstreamBasePath.length);\n }\n\n return `${absoluteProxyBase}${pathname}${target.search}`;\n}\n\n/**\n * Parses a fetch input (Request or URL string) into structured request parts.\n * Handles merging of Request defaults with optional init overrides.\n */\nasync function readRequestParts(input: Parameters<FetchLike>[0], init?: Parameters<FetchLike>[1]): Promise<RequestParts> {\n if (input instanceof Request) {\n const headers = new Headers(input.headers);\n if (init?.headers) {\n new Headers(init.headers).forEach((value, key) => headers.set(key, value));\n }\n\n return {\n url: input.url,\n method: init?.method ?? input.method,\n headers,\n body: init?.body ?? (input.body == null ? undefined : await input.clone().text()),\n signal: init?.signal ?? input.signal,\n };\n }\n\n return {\n url: String(input),\n method: init?.method,\n headers: new Headers(init?.headers),\n body: init?.body,\n signal: init?.signal,\n };\n}\n\n/**\n * Attempts to decode a request body to a string for JSON parsing.\n * Supports string, Blob, and ArrayBuffer types; returns undefined for\n * types that cannot be meaningfully decoded (FormData, ReadableStream, etc.)\n */\nasync function decodeBody(body: BodyInit | null | undefined): Promise<string | undefined> {\n if (body == null) {\n return undefined;\n }\n\n if (typeof body === 'string') {\n return body;\n }\n\n if (body instanceof URLSearchParams || body instanceof FormData || body instanceof ReadableStream) {\n return undefined;\n }\n\n if (body instanceof Blob) {\n return body.text();\n }\n\n if (body instanceof ArrayBuffer) {\n return new TextDecoder().decode(body);\n }\n\n if (ArrayBuffer.isView(body)) {\n return new TextDecoder().decode(body);\n }\n\n return undefined;\n}\n\n/**\n * Normalize an OpenAI Responses request body for the Codex backend.\n *\n * Codex expects requests to be stateless by default, so `store` defaults to\n * `false`. Unsupported `max_output_tokens` is removed.\n */\nexport function normalizeCodexResponsesBody(\n body: Record<string, unknown>,\n options: Pick<OpenAIOAuthSettings, 'instructions' | 'store'> = {},\n): Record<string, unknown> {\n const normalized = { ...body };\n\n if (typeof normalized.instructions !== 'string') {\n normalized.instructions = options.instructions ?? DEFAULT_INSTRUCTIONS;\n }\n\n if (normalized.store === undefined) {\n normalized.store = options.store ?? false;\n }\n\n delete normalized.max_output_tokens;\n\n return normalized;\n}\n\n/**\n * Prepares the request body for Codex /responses endpoints.\n * Normalizes JSON bodies to ensure Codex-compatible request format.\n */\nasync function prepareBody(pathname: string, headers: Headers, body: BodyInit | null | undefined, settings: OpenAIOAuthSettings) {\n if (!pathname.endsWith('/responses')) {\n return body;\n }\n\n const contentType = headers.get('content-type');\n if (contentType && !contentType.includes('application/json')) {\n return body;\n }\n\n const bodyText = await decodeBody(body);\n if (typeof bodyText !== 'string') {\n return body;\n }\n\n try {\n const parsed = JSON.parse(bodyText) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return body;\n }\n\n return JSON.stringify(normalizeCodexResponsesBody(parsed as Record<string, unknown>, settings));\n } catch {\n return body;\n }\n}\n\n/**\n * Create a fetch implementation that authenticates OpenAI Responses requests\n * with Codex OAuth credentials.\n *\n * The returned function rewrites `/v1/responses` requests to the Codex backend,\n * injects `Authorization` and `ChatGPT-Account-Id`, and refreshes credentials\n * before expiry when a refresh token is available.\n */\nexport function createCodexOAuthFetch(settings: OpenAIOAuthSettings = {}): FetchLike {\n const fetch = pickFetch(settings.fetch);\n const store = createStore(settings);\n const manager = new TokenManager(settings, store, fetch);\n const baseURL = resolveBaseURL(settings.baseURL);\n\n return async (input, init) => {\n const request = await readRequestParts(input, init);\n const targetUrl = resolveTargetUrl(request.url, baseURL);\n const target = new URL(targetUrl);\n const tokens = await manager.get();\n const headers = new Headers(settings.headers);\n\n request.headers.forEach((value, key) => headers.set(key, value));\n headers.delete('authorization');\n headers.delete('chatgpt-account-id');\n headers.delete('openai-beta');\n\n headers.set('Authorization', `Bearer ${tokens.accessToken}`);\n headers.set('ChatGPT-Account-Id', tokens.accountId!);\n headers.set('OpenAI-Beta', 'responses=experimental');\n\n if (settings.originator !== false && !headers.has('originator')) {\n headers.set('originator', settings.originator ?? DEFAULT_ORIGINATOR);\n }\n\n const body = await prepareBody(target.pathname, headers, request.body, settings);\n\n return fetch(resolveBrowserProxyUrl(target, baseURL, settings) ?? target.toString(), {\n method: request.method ?? init?.method,\n headers,\n body,\n signal: request.signal ?? undefined,\n });\n };\n}\n\n/** Create a small Codex client around `createCodexOAuthFetch`. */\nexport function createCodexOAuthClient(settings: OpenAIOAuthSettings = {}) {\n const baseURL = resolveBaseURL(settings.baseURL);\n const fetch = createCodexOAuthFetch(settings);\n\n return {\n baseURL,\n fetch,\n request: (path: string, init?: RequestInit) => fetch(resolveTargetUrl(path, baseURL), init),\n };\n}\n","/**\r\n * @file proxy.ts\r\n *\r\n * Server-side request handlers for proxying OpenAI Codex OAuth requests.\r\n * This module provides framework-agnostic handlers that can be used in\r\n * various server environments (Node.js, Deno, Cloudflare Workers, etc.)\r\n * to enable browser clients to make authenticated Codex requests.\r\n *\r\n * The proxy validates OAuth credentials (Authorization header and account ID),\r\n * rewrites requests to the Codex backend, and passes through responses.\r\n */\r\n\r\nimport { DEFAULT_CODEX_BASE_URL, normalizeCodexResponsesBody } from './codex-fetch';\r\nimport type { FetchLike, OpenAIOAuthSettings } from './types';\r\n\r\nexport type OpenAIOAuthProxyOptions = Pick<OpenAIOAuthSettings, 'instructions' | 'originator' | 'store'> & {\r\n fetch?: FetchLike;\r\n baseURL?: string;\r\n};\r\n\r\n/**\r\n * Resolves the fetch function to use for upstream requests.\r\n * Prefers a custom fetch if provided, falls back to globalThis.fetch,\r\n * and throws if neither is available.\r\n */\r\nfunction pickFetch(customFetch?: FetchLike): FetchLike {\r\n if (typeof customFetch === 'function') return customFetch;\r\n if (typeof globalThis.fetch === 'function') return globalThis.fetch.bind(globalThis);\r\n throw new Error('A fetch implementation is required for OpenAI Codex proxy handlers.');\r\n}\r\n\r\n/**\r\n * Creates a Response with no-store cache control header.\r\n * Used for error responses to prevent caching.\r\n */\r\nfunction noStoreText(text: string, status: number): Response {\r\n return new Response(text, {\r\n status,\r\n headers: {\r\n 'Content-Type': 'text/plain; charset=utf-8',\r\n 'Cache-Control': 'no-store',\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * Extracts and optionally normalizes the request body.\r\n * For JSON bodies, normalizes Codex-specific fields like instructions and store.\r\n */\r\nasync function requestBody(request: Request, options: OpenAIOAuthProxyOptions): Promise<string> {\r\n const text = await request.text();\r\n if (!text.trim()) return text;\r\n\r\n try {\r\n const parsed = JSON.parse(text) as unknown;\r\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return text;\r\n return JSON.stringify(normalizeCodexResponsesBody(parsed as Record<string, unknown>, options));\r\n } catch {\r\n return text;\r\n }\r\n}\r\n\r\n/** Create framework-agnostic server handlers for browser-safe Codex OAuth proxying. */\r\nexport function createOpenAIOAuthProxy(options: OpenAIOAuthProxyOptions = {}) {\r\n const fetch = pickFetch(options.fetch);\r\n const baseURL = (options.baseURL ?? DEFAULT_CODEX_BASE_URL).replace(/\\/$/, '');\r\n\r\n return {\r\n async responses(request: Request): Promise<Response> {\r\n const authorization = request.headers.get('authorization') ?? '';\r\n const accountId = request.headers.get('chatgpt-account-id') ?? '';\r\n if (!authorization.trim() || !accountId.trim()) {\r\n return noStoreText('Missing OpenAI OAuth credentials.', 401);\r\n }\r\n\r\n const upstream = await fetch(`${baseURL}/responses${new URL(request.url).search}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n Authorization: authorization,\r\n 'ChatGPT-Account-Id': accountId,\r\n 'OpenAI-Beta': 'responses=experimental',\r\n ...(options.originator === false ? {} : { originator: options.originator ?? 'openai-codex-oauth' }),\r\n },\r\n body: await requestBody(request, options),\r\n });\r\n\r\n return new Response(upstream.body, {\r\n status: upstream.status,\r\n headers: {\r\n 'Content-Type': upstream.headers.get('Content-Type') ?? 'application/json',\r\n 'Cache-Control': upstream.headers.get('Cache-Control') ?? 'no-store',\r\n },\r\n });\r\n },\r\n };\r\n}\r\n","/**\n * @file provider.ts\n *\n * Vercel AI SDK provider implementation for OpenAI Codex OAuth.\n * This module creates a provider that can be used with the AI SDK's\n * generateText, streamText, and other AI functions.\n *\n * The provider wraps the Codex OAuth fetch implementation and adapts it\n * to the AI SDK provider interface.\n */\n\nimport { createOpenAI } from '@ai-sdk/openai';\nimport type { EmbeddingModelV3, ImageModelV3, LanguageModelV3, ProviderV3 } from '@ai-sdk/provider';\nimport { NoSuchModelError } from '@ai-sdk/provider';\nimport { createCodexOAuthFetch, DEFAULT_CODEX_BASE_URL } from './codex-fetch';\nimport { StreamOnlyLanguageModel } from './stream-to-generate';\nimport type { OpenAIOAuthProviderSettings } from './types';\n\n/** Model ID type for Codex OAuth models. */\nexport type OpenAIOAuthModelId = string;\n\n/**\n * AI SDK provider interface for OpenAI Codex OAuth-backed language models.\n * Provides language model, embedding model, and image model factories.\n */\nexport interface OpenAIOAuthProvider extends ProviderV3 {\n (modelId: OpenAIOAuthModelId): LanguageModelV3;\n languageModel(modelId: OpenAIOAuthModelId): LanguageModelV3;\n responses(modelId: OpenAIOAuthModelId): LanguageModelV3;\n embeddingModel(modelId: string): EmbeddingModelV3;\n imageModel(modelId: string): ImageModelV3;\n}\n\n/**\n * Create a Vercel AI SDK provider that sends Responses API calls through the\n * OpenAI Codex OAuth backend.\n */\nexport function createOpenAIOAuth(settings: OpenAIOAuthProviderSettings = {}): OpenAIOAuthProvider {\n const providerName = settings.name ?? 'openai-oauth';\n const oauthFetch = createCodexOAuthFetch(settings);\n const openai = createOpenAI({\n apiKey: 'oauth',\n baseURL: settings.baseURL ?? DEFAULT_CODEX_BASE_URL,\n name: providerName,\n fetch: oauthFetch,\n });\n\n const createLanguageModel = (modelId: OpenAIOAuthModelId) => new StreamOnlyLanguageModel(openai.responses(modelId as never));\n const provider = (modelId: OpenAIOAuthModelId) => createLanguageModel(modelId);\n\n provider.specificationVersion = 'v3' as const;\n provider.languageModel = createLanguageModel;\n provider.responses = createLanguageModel;\n provider.embeddingModel = (modelId: string) => {\n throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' });\n };\n provider.imageModel = (modelId: string) => {\n throw new NoSuchModelError({ modelId, modelType: 'imageModel' });\n };\n\n return provider as OpenAIOAuthProvider;\n}\n","/**\n * @file stream-to-generate.ts\n *\n * Provides utilities for converting streaming language model results\n * into non-streaming results. This is useful when the underlying provider\n * only supports streaming but the application needs synchronous generation.\n *\n * The AI SDK v3 provider interface requires both doGenerate and doStream,\n * but some backends (like Codex) only implement streaming. This module\n * bridges that gap by consuming the stream and assembling a complete result.\n */\n\nimport type {\n LanguageModelV3,\n LanguageModelV3Content,\n LanguageModelV3FinishReason,\n LanguageModelV3GenerateResult,\n LanguageModelV3StreamResult,\n LanguageModelV3Usage,\n SharedV3ProviderMetadata,\n SharedV3Warning,\n} from '@ai-sdk/provider';\n\n/** Creates an empty usage object with all fields undefined. */\nconst emptyUsage = (): LanguageModelV3Usage => ({\n inputTokens: {\n total: undefined,\n noCache: undefined,\n cacheRead: undefined,\n cacheWrite: undefined,\n },\n outputTokens: {\n total: undefined,\n text: undefined,\n reasoning: undefined,\n },\n});\n\n/**\n * Merges two provider metadata objects, with right values taking precedence\n * over left values when both are defined.\n */\nfunction mergeProviderMetadata(\n left: SharedV3ProviderMetadata | undefined,\n right: SharedV3ProviderMetadata | undefined,\n): SharedV3ProviderMetadata | undefined {\n if (left == null) return right;\n if (right == null) return left;\n\n const merged: SharedV3ProviderMetadata = { ...left };\n for (const [provider, value] of Object.entries(right)) {\n const existing = merged[provider];\n merged[provider] = existing == null ? value : { ...existing, ...value };\n }\n\n return merged;\n}\n\n/** Collect a LanguageModel stream result into a non-streaming generate result. */\nexport async function collectStreamGenerateResult(\n streamResult: LanguageModelV3StreamResult,\n): Promise<LanguageModelV3GenerateResult> {\n const reader = streamResult.stream.getReader();\n const content: LanguageModelV3Content[] = [];\n const warnings: SharedV3Warning[] = [];\n const activeTextById = new Map<string, Extract<LanguageModelV3Content, { type: 'text' }>>();\n const activeReasoningById = new Map<string, Extract<LanguageModelV3Content, { type: 'reasoning' }>>();\n\n let finishReason: LanguageModelV3FinishReason = { unified: 'other', raw: undefined };\n let usage: LanguageModelV3Usage = emptyUsage();\n let providerMetadata: SharedV3ProviderMetadata | undefined;\n let responseMetadata: LanguageModelV3GenerateResult['response'];\n\n try {\n while (true) {\n const { value: part, done } = await reader.read();\n if (done) {\n break;\n }\n\n switch (part.type) {\n case 'stream-start':\n warnings.push(...part.warnings);\n break;\n case 'response-metadata':\n responseMetadata = {\n id: part.id,\n timestamp: part.timestamp,\n modelId: part.modelId,\n };\n break;\n case 'text-start': {\n const textPart: Extract<LanguageModelV3Content, { type: 'text' }> = {\n type: 'text',\n text: '',\n providerMetadata: part.providerMetadata,\n };\n content.push(textPart);\n activeTextById.set(part.id, textPart);\n break;\n }\n case 'text-delta': {\n const existing = activeTextById.get(part.id);\n if (existing) {\n existing.text += part.delta;\n existing.providerMetadata = mergeProviderMetadata(existing.providerMetadata, part.providerMetadata);\n } else {\n const textPart: Extract<LanguageModelV3Content, { type: 'text' }> = {\n type: 'text',\n text: part.delta,\n providerMetadata: part.providerMetadata,\n };\n content.push(textPart);\n activeTextById.set(part.id, textPart);\n }\n break;\n }\n case 'text-end': {\n const existing = activeTextById.get(part.id);\n if (existing) {\n existing.providerMetadata = mergeProviderMetadata(existing.providerMetadata, part.providerMetadata);\n activeTextById.delete(part.id);\n }\n break;\n }\n case 'reasoning-start': {\n const reasoningPart: Extract<LanguageModelV3Content, { type: 'reasoning' }> = {\n type: 'reasoning',\n text: '',\n providerMetadata: part.providerMetadata,\n };\n content.push(reasoningPart);\n activeReasoningById.set(part.id, reasoningPart);\n break;\n }\n case 'reasoning-delta': {\n const existing = activeReasoningById.get(part.id);\n if (existing) {\n existing.text += part.delta;\n existing.providerMetadata = mergeProviderMetadata(existing.providerMetadata, part.providerMetadata);\n } else {\n const reasoningPart: Extract<LanguageModelV3Content, { type: 'reasoning' }> = {\n type: 'reasoning',\n text: part.delta,\n providerMetadata: part.providerMetadata,\n };\n content.push(reasoningPart);\n activeReasoningById.set(part.id, reasoningPart);\n }\n break;\n }\n case 'reasoning-end': {\n const existing = activeReasoningById.get(part.id);\n if (existing) {\n existing.providerMetadata = mergeProviderMetadata(existing.providerMetadata, part.providerMetadata);\n activeReasoningById.delete(part.id);\n }\n break;\n }\n case 'tool-call':\n case 'tool-result':\n case 'tool-approval-request':\n case 'file':\n case 'source':\n content.push(part);\n break;\n case 'finish':\n finishReason = part.finishReason;\n usage = part.usage;\n providerMetadata = part.providerMetadata;\n break;\n case 'error':\n throw part.error instanceof Error ? part.error : new Error('Streaming request failed.', { cause: part.error });\n case 'tool-input-start':\n case 'tool-input-delta':\n case 'tool-input-end':\n case 'raw':\n break;\n default:\n part satisfies never;\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n return {\n content,\n finishReason,\n usage,\n providerMetadata,\n request: streamResult.request,\n response: responseMetadata == null && streamResult.response?.headers == null\n ? undefined\n : {\n ...(responseMetadata ?? {}),\n ...(streamResult.response?.headers == null ? {} : { headers: streamResult.response.headers }),\n },\n warnings,\n };\n}\n\n/** Language model wrapper that implements `doGenerate` by consuming `doStream`. */\nexport class StreamOnlyLanguageModel implements LanguageModelV3 {\n readonly specificationVersion = 'v3';\n readonly provider: string;\n readonly modelId: string;\n readonly supportedUrls: LanguageModelV3['supportedUrls'];\n\n constructor(private readonly inner: LanguageModelV3) {\n this.provider = inner.provider;\n this.modelId = inner.modelId;\n this.supportedUrls = inner.supportedUrls;\n }\n\n async doGenerate(options: Parameters<LanguageModelV3['doGenerate']>[0]): Promise<LanguageModelV3GenerateResult> {\n return collectStreamGenerateResult(await this.inner.doStream(options));\n }\n\n doStream(options: Parameters<LanguageModelV3['doStream']>[0]) {\n return this.inner.doStream(options);\n }\n}\n"],"mappings":"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","names":["pickFetch","pickFetch"]}
|
package/dist/node/index.js
CHANGED
|
@@ -1,12 +1,98 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/node/index.ts
|
|
31
|
+
var node_exports = {};
|
|
32
|
+
__export(node_exports, {
|
|
33
|
+
createCodexAuthFileStore: () => createCodexAuthFileStore,
|
|
34
|
+
resolveAuthFileCandidates: () => resolveAuthFileCandidates
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(node_exports);
|
|
37
|
+
|
|
38
|
+
// src/node/auth-file-store.ts
|
|
39
|
+
var import_node_fs = require("fs");
|
|
40
|
+
var import_node_os = __toESM(require("os"));
|
|
41
|
+
var import_node_path = __toESM(require("path"));
|
|
42
|
+
|
|
43
|
+
// src/jwt.ts
|
|
44
|
+
function decodeBase64Url(value) {
|
|
45
|
+
try {
|
|
46
|
+
const base64 = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
47
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
48
|
+
if (typeof globalThis.atob === "function") {
|
|
49
|
+
const binary = globalThis.atob(padded);
|
|
50
|
+
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
51
|
+
return new TextDecoder().decode(bytes);
|
|
52
|
+
}
|
|
53
|
+
return Buffer.from(padded, "base64").toString("utf8");
|
|
54
|
+
} catch {
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function isRecord(value) {
|
|
59
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
60
|
+
}
|
|
61
|
+
function parseJwtClaims(token) {
|
|
62
|
+
if (!token?.includes(".")) {
|
|
63
|
+
return void 0;
|
|
64
|
+
}
|
|
65
|
+
const parts = token.split(".");
|
|
66
|
+
if (parts.length !== 3 || !parts[1]) {
|
|
67
|
+
return void 0;
|
|
68
|
+
}
|
|
69
|
+
const payload = decodeBase64Url(parts[1]);
|
|
70
|
+
if (!payload) {
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const parsed = JSON.parse(payload);
|
|
75
|
+
return isRecord(parsed) ? parsed : void 0;
|
|
76
|
+
} catch {
|
|
77
|
+
return void 0;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function deriveAccountId(...tokens) {
|
|
81
|
+
for (const token of tokens) {
|
|
82
|
+
const claims = parseJwtClaims(token);
|
|
83
|
+
const auth = claims?.["https://api.openai.com/auth"];
|
|
84
|
+
if (isRecord(auth) && typeof auth.chatgpt_account_id === "string" && auth.chatgpt_account_id.length > 0) {
|
|
85
|
+
return auth.chatgpt_account_id;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return void 0;
|
|
89
|
+
}
|
|
90
|
+
function deriveExpiresAt(token) {
|
|
91
|
+
const claims = parseJwtClaims(token);
|
|
92
|
+
return typeof claims?.exp === "number" ? claims.exp * 1e3 : void 0;
|
|
93
|
+
}
|
|
5
94
|
|
|
6
95
|
// src/node/auth-file-store.ts
|
|
7
|
-
import { promises as fs } from "fs";
|
|
8
|
-
import os from "os";
|
|
9
|
-
import path from "path";
|
|
10
96
|
var AUTH_FILENAME = "auth.json";
|
|
11
97
|
function unique(values) {
|
|
12
98
|
return [...new Set(values)];
|
|
@@ -19,10 +105,10 @@ function resolveAuthFileCandidates(authFilePath) {
|
|
|
19
105
|
const codexHome = process.env.CODEX_HOME;
|
|
20
106
|
return unique(
|
|
21
107
|
[
|
|
22
|
-
chatgptHome ?
|
|
23
|
-
codexHome ?
|
|
24
|
-
|
|
25
|
-
|
|
108
|
+
chatgptHome ? import_node_path.default.join(chatgptHome, AUTH_FILENAME) : void 0,
|
|
109
|
+
codexHome ? import_node_path.default.join(codexHome, AUTH_FILENAME) : void 0,
|
|
110
|
+
import_node_path.default.join(import_node_os.default.homedir(), ".chatgpt-local", AUTH_FILENAME),
|
|
111
|
+
import_node_path.default.join(import_node_os.default.homedir(), ".codex", AUTH_FILENAME)
|
|
26
112
|
].filter((value) => Boolean(value))
|
|
27
113
|
);
|
|
28
114
|
}
|
|
@@ -31,12 +117,12 @@ function resolveWritePath(authFilePath) {
|
|
|
31
117
|
return authFilePath;
|
|
32
118
|
}
|
|
33
119
|
if (process.env.CHATGPT_LOCAL_HOME) {
|
|
34
|
-
return
|
|
120
|
+
return import_node_path.default.join(process.env.CHATGPT_LOCAL_HOME, AUTH_FILENAME);
|
|
35
121
|
}
|
|
36
122
|
if (process.env.CODEX_HOME) {
|
|
37
|
-
return
|
|
123
|
+
return import_node_path.default.join(process.env.CODEX_HOME, AUTH_FILENAME);
|
|
38
124
|
}
|
|
39
|
-
return
|
|
125
|
+
return import_node_path.default.join(import_node_os.default.homedir(), ".codex", AUTH_FILENAME);
|
|
40
126
|
}
|
|
41
127
|
function hasUsableStoredTokens(data) {
|
|
42
128
|
return Boolean(data?.tokens?.access_token);
|
|
@@ -45,7 +131,7 @@ async function readAuthFile(candidates) {
|
|
|
45
131
|
let firstParsed;
|
|
46
132
|
for (const candidate of candidates) {
|
|
47
133
|
try {
|
|
48
|
-
const raw = await
|
|
134
|
+
const raw = await import_node_fs.promises.readFile(candidate, "utf8");
|
|
49
135
|
const parsed = JSON.parse(raw);
|
|
50
136
|
if (!firstParsed) {
|
|
51
137
|
firstParsed = { filePath: candidate, data: parsed };
|
|
@@ -73,13 +159,13 @@ function toTokens(data) {
|
|
|
73
159
|
};
|
|
74
160
|
}
|
|
75
161
|
async function writeAuthFile(filePath, data) {
|
|
76
|
-
await
|
|
77
|
-
await
|
|
162
|
+
await import_node_fs.promises.mkdir(import_node_path.default.dirname(filePath), { recursive: true });
|
|
163
|
+
await import_node_fs.promises.writeFile(filePath, JSON.stringify(data, null, 2), {
|
|
78
164
|
encoding: "utf8",
|
|
79
165
|
mode: 384
|
|
80
166
|
});
|
|
81
167
|
try {
|
|
82
|
-
await
|
|
168
|
+
await import_node_fs.promises.chmod(filePath, 384);
|
|
83
169
|
} catch {
|
|
84
170
|
}
|
|
85
171
|
}
|
|
@@ -112,8 +198,9 @@ function createCodexAuthFileStore(options = {}) {
|
|
|
112
198
|
}
|
|
113
199
|
};
|
|
114
200
|
}
|
|
115
|
-
export
|
|
201
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
202
|
+
0 && (module.exports = {
|
|
116
203
|
createCodexAuthFileStore,
|
|
117
204
|
resolveAuthFileCandidates
|
|
118
|
-
};
|
|
205
|
+
});
|
|
119
206
|
//# sourceMappingURL=index.js.map
|
package/dist/node/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/node/auth-file-store.ts"],"sourcesContent":["/**\r\n * @file auth-file-store.ts\r\n *\r\n * Node.js-specific token store implementation that reads and writes\r\n * Codex-compatible auth.json files. This is the recommended storage\r\n * for desktop CLI applications using Codex.\r\n *\r\n * The store looks for auth files in multiple standard locations:\r\n * - $CHATGPT_LOCAL_HOME/auth.json\r\n * - $CODEX_HOME/auth.json\r\n * - ~/.chatgpt-local/auth.json\r\n * - ~/.codex/auth.json\r\n *\r\n * When writing, it prefers the environment variable locations or\r\n * defaults to ~/.codex/auth.json with mode 0600 for security.\r\n */\r\n\r\nimport { promises as fs } from 'node:fs';\r\nimport os from 'node:os';\r\nimport path from 'node:path';\r\nimport { deriveAccountId, deriveExpiresAt } from '../jwt';\r\nimport type { OpenAIOAuthTokens, TokenStore } from '../types';\r\n\r\n/** Standard filename for Codex/ChatGPT local auth files. */\r\nconst AUTH_FILENAME = 'auth.json';\r\n\r\n/**\r\n * Shape of the auth.json file stored by Codex/ChatGPT local.\r\n */\r\ntype StoredAuthFile = {\r\n OPENAI_API_KEY?: string;\r\n tokens?: {\r\n access_token?: string;\r\n refresh_token?: string;\r\n id_token?: string;\r\n account_id?: string;\r\n };\r\n last_refresh?: string;\r\n};\r\n\r\nexport type CodexAuthFileStoreOptions = {\r\n /** Explicit auth file path. When omitted, common Codex auth locations are searched. */\r\n authFilePath?: string;\r\n};\r\n\r\n/** Returns unique values from an array by converting to a Set and back. */\r\nfunction unique(values: string[]): string[] {\r\n return [...new Set(values)];\r\n}\r\n\r\n/**\r\n * Returns candidate paths to search for auth.json files.\r\n * If authFilePath is explicitly provided, returns just that path.\r\n * Otherwise returns all standard candidate paths based on environment variables.\r\n */\r\nexport function resolveAuthFileCandidates(authFilePath?: string): string[] {\r\n if (authFilePath) {\r\n return [authFilePath];\r\n }\r\n\r\n const chatgptHome = process.env.CHATGPT_LOCAL_HOME;\r\n const codexHome = process.env.CODEX_HOME;\r\n\r\n return unique(\r\n [\r\n chatgptHome ? path.join(chatgptHome, AUTH_FILENAME) : undefined,\r\n codexHome ? path.join(codexHome, AUTH_FILENAME) : undefined,\r\n path.join(os.homedir(), '.chatgpt-local', AUTH_FILENAME),\r\n path.join(os.homedir(), '.codex', AUTH_FILENAME),\r\n ].filter((value): value is string => Boolean(value)),\r\n );\r\n}\r\n\r\n/**\r\n * Resolves the path where auth tokens should be written.\r\n * Prefers explicit path, then environment variables, then defaults to ~/.codex/auth.json\r\n */\r\nfunction resolveWritePath(authFilePath?: string): string {\r\n if (authFilePath) {\r\n return authFilePath;\r\n }\r\n\r\n if (process.env.CHATGPT_LOCAL_HOME) {\r\n return path.join(process.env.CHATGPT_LOCAL_HOME, AUTH_FILENAME);\r\n }\r\n\r\n if (process.env.CODEX_HOME) {\r\n return path.join(process.env.CODEX_HOME, AUTH_FILENAME);\r\n }\r\n\r\n return path.join(os.homedir(), '.codex', AUTH_FILENAME);\r\n}\r\n\r\n/**\r\n * Returns whether the parsed auth file contains enough token data for `toTokens`\r\n * to produce usable credentials.\r\n */\r\nfunction hasUsableStoredTokens(data: StoredAuthFile | undefined): boolean {\r\n return Boolean(data?.tokens?.access_token);\r\n}\r\n\r\n/**\r\n * Attempts to read and parse auth.json files from the list of candidates.\r\n * Remembers the first successfully parsed file for future writes, but continues\r\n * searching until a file with usable token data is found.\r\n */\r\nasync function readAuthFile(candidates: string[]): Promise<{ filePath?: string; data?: StoredAuthFile }> {\r\n let firstParsed: { filePath?: string; data?: StoredAuthFile } | undefined;\r\n\r\n for (const candidate of candidates) {\r\n try {\r\n const raw = await fs.readFile(candidate, 'utf8');\r\n const parsed = JSON.parse(raw) as StoredAuthFile;\r\n\r\n if (!firstParsed) {\r\n firstParsed = { filePath: candidate, data: parsed };\r\n }\r\n\r\n if (hasUsableStoredTokens(parsed)) {\r\n return { filePath: firstParsed.filePath, data: parsed };\r\n }\r\n } catch {\r\n }\r\n }\r\n\r\n return firstParsed ?? {};\r\n}\r\n\r\n/**\r\n * Converts stored auth file format to OpenAIOAuthTokens format.\r\n * Derives account ID and expiry from JWT claims if not explicitly stored.\r\n */\r\nfunction toTokens(data: StoredAuthFile | undefined): OpenAIOAuthTokens | undefined {\r\n const accessToken = data?.tokens?.access_token;\r\n if (!accessToken) {\r\n return undefined;\r\n }\r\n\r\n const idToken = data?.tokens?.id_token;\r\n return {\r\n accessToken,\r\n refreshToken: data?.tokens?.refresh_token,\r\n idToken,\r\n accountId: data?.tokens?.account_id ?? deriveAccountId(idToken, accessToken),\r\n expiresAt: deriveExpiresAt(accessToken),\r\n };\r\n}\r\n\r\n/**\r\n * Writes auth data to a file, creating parent directories if needed.\r\n * Sets file mode to 0600 (owner read/write only) for security.\r\n */\r\nasync function writeAuthFile(filePath: string, data: StoredAuthFile): Promise<void> {\r\n await fs.mkdir(path.dirname(filePath), { recursive: true });\r\n await fs.writeFile(filePath, JSON.stringify(data, null, 2), {\r\n encoding: 'utf8',\r\n mode: 0o600,\r\n });\r\n\r\n try {\r\n await fs.chmod(filePath, 0o600);\r\n } catch {\r\n // Ignore platforms/filesystems that do not support chmod semantics.\r\n }\r\n}\r\n\r\n/**\r\n * Create a Node token store backed by Codex-compatible `auth.json` files.\r\n *\r\n * The store reads existing Codex/ChatGPT local auth files and writes refreshed\r\n * tokens back with file mode `0600` where supported by the OS.\r\n */\r\nexport function createCodexAuthFileStore(options: CodexAuthFileStoreOptions = {}): TokenStore {\r\n let sourcePath: string | undefined;\r\n let lastData: StoredAuthFile = {};\r\n\r\n return {\r\n async load() {\r\n const result = await readAuthFile(resolveAuthFileCandidates(options.authFilePath));\r\n sourcePath = result.filePath;\r\n lastData = result.data ?? {};\r\n return toTokens(result.data);\r\n },\r\n async save(tokens) {\r\n const filePath = sourcePath ?? resolveWritePath(options.authFilePath);\r\n const data: StoredAuthFile = {\r\n ...lastData,\r\n tokens: {\r\n ...(lastData.tokens ?? {}),\r\n access_token: tokens.accessToken,\r\n refresh_token: tokens.refreshToken,\r\n id_token: tokens.idToken,\r\n account_id: tokens.accountId,\r\n },\r\n last_refresh: new Date().toISOString(),\r\n };\r\n\r\n await writeAuthFile(filePath, data);\r\n sourcePath = filePath;\r\n lastData = data;\r\n },\r\n };\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/node/index.ts","../../src/node/auth-file-store.ts","../../src/jwt.ts"],"sourcesContent":["/**\n * @file index.ts\n *\n * Node.js-specific exports for the OpenAI Codex OAuth library.\n * Provides file-based token storage for desktop CLI applications.\n */\n\nexport { createCodexAuthFileStore, resolveAuthFileCandidates } from './auth-file-store';\nexport type { CodexAuthFileStoreOptions } from './auth-file-store';\n","/**\r\n * @file auth-file-store.ts\r\n *\r\n * Node.js-specific token store implementation that reads and writes\r\n * Codex-compatible auth.json files. This is the recommended storage\r\n * for desktop CLI applications using Codex.\r\n *\r\n * The store looks for auth files in multiple standard locations:\r\n * - $CHATGPT_LOCAL_HOME/auth.json\r\n * - $CODEX_HOME/auth.json\r\n * - ~/.chatgpt-local/auth.json\r\n * - ~/.codex/auth.json\r\n *\r\n * When writing, it prefers the environment variable locations or\r\n * defaults to ~/.codex/auth.json with mode 0600 for security.\r\n */\r\n\r\nimport { promises as fs } from 'node:fs';\r\nimport os from 'node:os';\r\nimport path from 'node:path';\r\nimport { deriveAccountId, deriveExpiresAt } from '../jwt';\r\nimport type { OpenAIOAuthTokens, TokenStore } from '../types';\r\n\r\n/** Standard filename for Codex/ChatGPT local auth files. */\r\nconst AUTH_FILENAME = 'auth.json';\r\n\r\n/**\r\n * Shape of the auth.json file stored by Codex/ChatGPT local.\r\n */\r\ntype StoredAuthFile = {\r\n OPENAI_API_KEY?: string;\r\n tokens?: {\r\n access_token?: string;\r\n refresh_token?: string;\r\n id_token?: string;\r\n account_id?: string;\r\n };\r\n last_refresh?: string;\r\n};\r\n\r\nexport type CodexAuthFileStoreOptions = {\r\n /** Explicit auth file path. When omitted, common Codex auth locations are searched. */\r\n authFilePath?: string;\r\n};\r\n\r\n/** Returns unique values from an array by converting to a Set and back. */\r\nfunction unique(values: string[]): string[] {\r\n return [...new Set(values)];\r\n}\r\n\r\n/**\r\n * Returns candidate paths to search for auth.json files.\r\n * If authFilePath is explicitly provided, returns just that path.\r\n * Otherwise returns all standard candidate paths based on environment variables.\r\n */\r\nexport function resolveAuthFileCandidates(authFilePath?: string): string[] {\r\n if (authFilePath) {\r\n return [authFilePath];\r\n }\r\n\r\n const chatgptHome = process.env.CHATGPT_LOCAL_HOME;\r\n const codexHome = process.env.CODEX_HOME;\r\n\r\n return unique(\r\n [\r\n chatgptHome ? path.join(chatgptHome, AUTH_FILENAME) : undefined,\r\n codexHome ? path.join(codexHome, AUTH_FILENAME) : undefined,\r\n path.join(os.homedir(), '.chatgpt-local', AUTH_FILENAME),\r\n path.join(os.homedir(), '.codex', AUTH_FILENAME),\r\n ].filter((value): value is string => Boolean(value)),\r\n );\r\n}\r\n\r\n/**\r\n * Resolves the path where auth tokens should be written.\r\n * Prefers explicit path, then environment variables, then defaults to ~/.codex/auth.json\r\n */\r\nfunction resolveWritePath(authFilePath?: string): string {\r\n if (authFilePath) {\r\n return authFilePath;\r\n }\r\n\r\n if (process.env.CHATGPT_LOCAL_HOME) {\r\n return path.join(process.env.CHATGPT_LOCAL_HOME, AUTH_FILENAME);\r\n }\r\n\r\n if (process.env.CODEX_HOME) {\r\n return path.join(process.env.CODEX_HOME, AUTH_FILENAME);\r\n }\r\n\r\n return path.join(os.homedir(), '.codex', AUTH_FILENAME);\r\n}\r\n\r\n/**\r\n * Returns whether the parsed auth file contains enough token data for `toTokens`\r\n * to produce usable credentials.\r\n */\r\nfunction hasUsableStoredTokens(data: StoredAuthFile | undefined): boolean {\r\n return Boolean(data?.tokens?.access_token);\r\n}\r\n\r\n/**\r\n * Attempts to read and parse auth.json files from the list of candidates.\r\n * Remembers the first successfully parsed file for future writes, but continues\r\n * searching until a file with usable token data is found.\r\n */\r\nasync function readAuthFile(candidates: string[]): Promise<{ filePath?: string; data?: StoredAuthFile }> {\r\n let firstParsed: { filePath?: string; data?: StoredAuthFile } | undefined;\r\n\r\n for (const candidate of candidates) {\r\n try {\r\n const raw = await fs.readFile(candidate, 'utf8');\r\n const parsed = JSON.parse(raw) as StoredAuthFile;\r\n\r\n if (!firstParsed) {\r\n firstParsed = { filePath: candidate, data: parsed };\r\n }\r\n\r\n if (hasUsableStoredTokens(parsed)) {\r\n return { filePath: firstParsed.filePath, data: parsed };\r\n }\r\n } catch {\r\n }\r\n }\r\n\r\n return firstParsed ?? {};\r\n}\r\n\r\n/**\r\n * Converts stored auth file format to OpenAIOAuthTokens format.\r\n * Derives account ID and expiry from JWT claims if not explicitly stored.\r\n */\r\nfunction toTokens(data: StoredAuthFile | undefined): OpenAIOAuthTokens | undefined {\r\n const accessToken = data?.tokens?.access_token;\r\n if (!accessToken) {\r\n return undefined;\r\n }\r\n\r\n const idToken = data?.tokens?.id_token;\r\n return {\r\n accessToken,\r\n refreshToken: data?.tokens?.refresh_token,\r\n idToken,\r\n accountId: data?.tokens?.account_id ?? deriveAccountId(idToken, accessToken),\r\n expiresAt: deriveExpiresAt(accessToken),\r\n };\r\n}\r\n\r\n/**\r\n * Writes auth data to a file, creating parent directories if needed.\r\n * Sets file mode to 0600 (owner read/write only) for security.\r\n */\r\nasync function writeAuthFile(filePath: string, data: StoredAuthFile): Promise<void> {\r\n await fs.mkdir(path.dirname(filePath), { recursive: true });\r\n await fs.writeFile(filePath, JSON.stringify(data, null, 2), {\r\n encoding: 'utf8',\r\n mode: 0o600,\r\n });\r\n\r\n try {\r\n await fs.chmod(filePath, 0o600);\r\n } catch {\r\n // Ignore platforms/filesystems that do not support chmod semantics.\r\n }\r\n}\r\n\r\n/**\r\n * Create a Node token store backed by Codex-compatible `auth.json` files.\r\n *\r\n * The store reads existing Codex/ChatGPT local auth files and writes refreshed\r\n * tokens back with file mode `0600` where supported by the OS.\r\n */\r\nexport function createCodexAuthFileStore(options: CodexAuthFileStoreOptions = {}): TokenStore {\r\n let sourcePath: string | undefined;\r\n let lastData: StoredAuthFile = {};\r\n\r\n return {\r\n async load() {\r\n const result = await readAuthFile(resolveAuthFileCandidates(options.authFilePath));\r\n sourcePath = result.filePath;\r\n lastData = result.data ?? {};\r\n return toTokens(result.data);\r\n },\r\n async save(tokens) {\r\n const filePath = sourcePath ?? resolveWritePath(options.authFilePath);\r\n const data: StoredAuthFile = {\r\n ...lastData,\r\n tokens: {\r\n ...(lastData.tokens ?? {}),\r\n access_token: tokens.accessToken,\r\n refresh_token: tokens.refreshToken,\r\n id_token: tokens.idToken,\r\n account_id: tokens.accountId,\r\n },\r\n last_refresh: new Date().toISOString(),\r\n };\r\n\r\n await writeAuthFile(filePath, data);\r\n sourcePath = filePath;\r\n lastData = data;\r\n },\r\n };\r\n}\r\n","/**\n * @file jwt.ts\n *\n * JWT (JSON Web Token) parsing utilities for OpenAI OAuth.\n * These functions decode and extract claims from JWTs without\n * verifying signatures (since we trust the OAuth issuer).\n */\n\nimport type { OpenAIOAuthTokens } from './types';\n\n/**\n * Decodes a Base64URL-encoded string to UTF-8 text.\n * Handles both browser (atob) and Node.js (Buffer) environments.\n */\nfunction decodeBase64Url(value: string): string | undefined {\n try {\n const base64 = value.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);\n\n if (typeof globalThis.atob === 'function') {\n const binary = globalThis.atob(padded);\n const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));\n return new TextDecoder().decode(bytes);\n }\n\n return Buffer.from(padded, 'base64').toString('utf8');\n } catch {\n return undefined;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Parse the JSON payload from a JWT without verifying the signature. */\nexport function parseJwtClaims(token: string | undefined): Record<string, unknown> | undefined {\n if (!token?.includes('.')) {\n return undefined;\n }\n\n const parts = token.split('.');\n if (parts.length !== 3 || !parts[1]) {\n return undefined;\n }\n\n const payload = decodeBase64Url(parts[1]);\n if (!payload) {\n return undefined;\n }\n\n try {\n const parsed = JSON.parse(payload) as unknown;\n return isRecord(parsed) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Derive the ChatGPT account id from OpenAI JWT claims when present. */\nexport function deriveAccountId(...tokens: Array<string | undefined>): string | undefined {\n for (const token of tokens) {\n const claims = parseJwtClaims(token);\n const auth = claims?.['https://api.openai.com/auth'];\n\n if (isRecord(auth) && typeof auth.chatgpt_account_id === 'string' && auth.chatgpt_account_id.length > 0) {\n return auth.chatgpt_account_id;\n }\n }\n\n return undefined;\n}\n\n/** Derive JWT expiry as epoch milliseconds from the `exp` claim. */\nexport function deriveExpiresAt(token: string | undefined): number | undefined {\n const claims = parseJwtClaims(token);\n return typeof claims?.exp === 'number' ? claims.exp * 1000 : undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACiBA,qBAA+B;AAC/B,qBAAe;AACf,uBAAiB;;;ACLjB,SAAS,gBAAgB,OAAmC;AAC1D,MAAI;AACF,UAAM,SAAS,MAAM,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AACzD,UAAM,SAAS,SAAS,IAAI,QAAQ,IAAK,OAAO,SAAS,KAAM,CAAC;AAEhE,QAAI,OAAO,WAAW,SAAS,YAAY;AACzC,YAAM,SAAS,WAAW,KAAK,MAAM;AACrC,YAAM,QAAQ,WAAW,KAAK,QAAQ,CAAC,SAAS,KAAK,WAAW,CAAC,CAAC;AAClE,aAAO,IAAI,YAAY,EAAE,OAAO,KAAK;AAAA,IACvC;AAEA,WAAO,OAAO,KAAK,QAAQ,QAAQ,EAAE,SAAS,MAAM;AAAA,EACtD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAGO,SAAS,eAAe,OAAgE;AAC7F,MAAI,CAAC,OAAO,SAAS,GAAG,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,MAAI,MAAM,WAAW,KAAK,CAAC,MAAM,CAAC,GAAG;AACnC,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,gBAAgB,MAAM,CAAC,CAAC;AACxC,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,OAAO;AACjC,WAAO,SAAS,MAAM,IAAI,SAAS;AAAA,EACrC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,mBAAmB,QAAuD;AACxF,aAAW,SAAS,QAAQ;AAC1B,UAAM,SAAS,eAAe,KAAK;AACnC,UAAM,OAAO,SAAS,6BAA6B;AAEnD,QAAI,SAAS,IAAI,KAAK,OAAO,KAAK,uBAAuB,YAAY,KAAK,mBAAmB,SAAS,GAAG;AACvG,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AAEA,SAAO;AACT;AAGO,SAAS,gBAAgB,OAA+C;AAC7E,QAAM,SAAS,eAAe,KAAK;AACnC,SAAO,OAAO,QAAQ,QAAQ,WAAW,OAAO,MAAM,MAAO;AAC/D;;;ADrDA,IAAM,gBAAgB;AAsBtB,SAAS,OAAO,QAA4B;AAC1C,SAAO,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC;AAC5B;AAOO,SAAS,0BAA0B,cAAiC;AACzE,MAAI,cAAc;AAChB,WAAO,CAAC,YAAY;AAAA,EACtB;AAEA,QAAM,cAAc,QAAQ,IAAI;AAChC,QAAM,YAAY,QAAQ,IAAI;AAE9B,SAAO;AAAA,IACL;AAAA,MACE,cAAc,iBAAAA,QAAK,KAAK,aAAa,aAAa,IAAI;AAAA,MACtD,YAAY,iBAAAA,QAAK,KAAK,WAAW,aAAa,IAAI;AAAA,MAClD,iBAAAA,QAAK,KAAK,eAAAC,QAAG,QAAQ,GAAG,kBAAkB,aAAa;AAAA,MACvD,iBAAAD,QAAK,KAAK,eAAAC,QAAG,QAAQ,GAAG,UAAU,aAAa;AAAA,IACjD,EAAE,OAAO,CAAC,UAA2B,QAAQ,KAAK,CAAC;AAAA,EACrD;AACF;AAMA,SAAS,iBAAiB,cAA+B;AACvD,MAAI,cAAc;AAChB,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,IAAI,oBAAoB;AAClC,WAAO,iBAAAD,QAAK,KAAK,QAAQ,IAAI,oBAAoB,aAAa;AAAA,EAChE;AAEA,MAAI,QAAQ,IAAI,YAAY;AAC1B,WAAO,iBAAAA,QAAK,KAAK,QAAQ,IAAI,YAAY,aAAa;AAAA,EACxD;AAEA,SAAO,iBAAAA,QAAK,KAAK,eAAAC,QAAG,QAAQ,GAAG,UAAU,aAAa;AACxD;AAMA,SAAS,sBAAsB,MAA2C;AACxE,SAAO,QAAQ,MAAM,QAAQ,YAAY;AAC3C;AAOA,eAAe,aAAa,YAA6E;AACvG,MAAI;AAEJ,aAAW,aAAa,YAAY;AAClC,QAAI;AACF,YAAM,MAAM,MAAM,eAAAC,SAAG,SAAS,WAAW,MAAM;AAC/C,YAAM,SAAS,KAAK,MAAM,GAAG;AAE7B,UAAI,CAAC,aAAa;AAChB,sBAAc,EAAE,UAAU,WAAW,MAAM,OAAO;AAAA,MACpD;AAEA,UAAI,sBAAsB,MAAM,GAAG;AACjC,eAAO,EAAE,UAAU,YAAY,UAAU,MAAM,OAAO;AAAA,MACxD;AAAA,IACF,QAAQ;AAAA,IACR;AAAA,EACF;AAEA,SAAO,eAAe,CAAC;AACzB;AAMA,SAAS,SAAS,MAAiE;AACjF,QAAM,cAAc,MAAM,QAAQ;AAClC,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,MAAM,QAAQ;AAC9B,SAAO;AAAA,IACL;AAAA,IACA,cAAc,MAAM,QAAQ;AAAA,IAC5B;AAAA,IACA,WAAW,MAAM,QAAQ,cAAc,gBAAgB,SAAS,WAAW;AAAA,IAC3E,WAAW,gBAAgB,WAAW;AAAA,EACxC;AACF;AAMA,eAAe,cAAc,UAAkB,MAAqC;AAClF,QAAM,eAAAA,SAAG,MAAM,iBAAAF,QAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAC1D,QAAM,eAAAE,SAAG,UAAU,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG;AAAA,IAC1D,UAAU;AAAA,IACV,MAAM;AAAA,EACR,CAAC;AAED,MAAI;AACF,UAAM,eAAAA,SAAG,MAAM,UAAU,GAAK;AAAA,EAChC,QAAQ;AAAA,EAER;AACF;AAQO,SAAS,yBAAyB,UAAqC,CAAC,GAAe;AAC5F,MAAI;AACJ,MAAI,WAA2B,CAAC;AAEhC,SAAO;AAAA,IACL,MAAM,OAAO;AACX,YAAM,SAAS,MAAM,aAAa,0BAA0B,QAAQ,YAAY,CAAC;AACjF,mBAAa,OAAO;AACpB,iBAAW,OAAO,QAAQ,CAAC;AAC3B,aAAO,SAAS,OAAO,IAAI;AAAA,IAC7B;AAAA,IACA,MAAM,KAAK,QAAQ;AACjB,YAAM,WAAW,cAAc,iBAAiB,QAAQ,YAAY;AACpE,YAAM,OAAuB;AAAA,QAC3B,GAAG;AAAA,QACH,QAAQ;AAAA,UACN,GAAI,SAAS,UAAU,CAAC;AAAA,UACxB,cAAc,OAAO;AAAA,UACrB,eAAe,OAAO;AAAA,UACtB,UAAU,OAAO;AAAA,UACjB,YAAY,OAAO;AAAA,QACrB;AAAA,QACA,eAAc,oBAAI,KAAK,GAAE,YAAY;AAAA,MACvC;AAEA,YAAM,cAAc,UAAU,IAAI;AAClC,mBAAa;AACb,iBAAW;AAAA,IACb;AAAA,EACF;AACF;","names":["path","os","fs"]}
|
package/dist/proxy.js
CHANGED
|
@@ -1,8 +1,104 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
7
9
|
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/proxy.ts
|
|
21
|
+
var proxy_exports = {};
|
|
22
|
+
__export(proxy_exports, {
|
|
23
|
+
createOpenAIOAuthProxy: () => createOpenAIOAuthProxy
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(proxy_exports);
|
|
26
|
+
|
|
27
|
+
// src/codex-fetch.ts
|
|
28
|
+
var DEFAULT_CODEX_BASE_URL = "https://chatgpt.com/backend-api/codex";
|
|
29
|
+
var DEFAULT_REFRESH_MARGIN_MS = 5 * 60 * 1e3;
|
|
30
|
+
var DEFAULT_INSTRUCTIONS = "";
|
|
31
|
+
function normalizeCodexResponsesBody(body, options = {}) {
|
|
32
|
+
const normalized = { ...body };
|
|
33
|
+
if (typeof normalized.instructions !== "string") {
|
|
34
|
+
normalized.instructions = options.instructions ?? DEFAULT_INSTRUCTIONS;
|
|
35
|
+
}
|
|
36
|
+
if (normalized.store === void 0) {
|
|
37
|
+
normalized.store = options.store ?? false;
|
|
38
|
+
}
|
|
39
|
+
delete normalized.max_output_tokens;
|
|
40
|
+
return normalized;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/proxy.ts
|
|
44
|
+
function pickFetch(customFetch) {
|
|
45
|
+
if (typeof customFetch === "function") return customFetch;
|
|
46
|
+
if (typeof globalThis.fetch === "function") return globalThis.fetch.bind(globalThis);
|
|
47
|
+
throw new Error("A fetch implementation is required for OpenAI Codex proxy handlers.");
|
|
48
|
+
}
|
|
49
|
+
function noStoreText(text, status) {
|
|
50
|
+
return new Response(text, {
|
|
51
|
+
status,
|
|
52
|
+
headers: {
|
|
53
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
54
|
+
"Cache-Control": "no-store"
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async function requestBody(request, options) {
|
|
59
|
+
const text = await request.text();
|
|
60
|
+
if (!text.trim()) return text;
|
|
61
|
+
try {
|
|
62
|
+
const parsed = JSON.parse(text);
|
|
63
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return text;
|
|
64
|
+
return JSON.stringify(normalizeCodexResponsesBody(parsed, options));
|
|
65
|
+
} catch {
|
|
66
|
+
return text;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function createOpenAIOAuthProxy(options = {}) {
|
|
70
|
+
const fetch = pickFetch(options.fetch);
|
|
71
|
+
const baseURL = (options.baseURL ?? DEFAULT_CODEX_BASE_URL).replace(/\/$/, "");
|
|
72
|
+
return {
|
|
73
|
+
async responses(request) {
|
|
74
|
+
const authorization = request.headers.get("authorization") ?? "";
|
|
75
|
+
const accountId = request.headers.get("chatgpt-account-id") ?? "";
|
|
76
|
+
if (!authorization.trim() || !accountId.trim()) {
|
|
77
|
+
return noStoreText("Missing OpenAI OAuth credentials.", 401);
|
|
78
|
+
}
|
|
79
|
+
const upstream = await fetch(`${baseURL}/responses${new URL(request.url).search}`, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: {
|
|
82
|
+
"Content-Type": "application/json",
|
|
83
|
+
Authorization: authorization,
|
|
84
|
+
"ChatGPT-Account-Id": accountId,
|
|
85
|
+
"OpenAI-Beta": "responses=experimental",
|
|
86
|
+
...options.originator === false ? {} : { originator: options.originator ?? "openai-codex-oauth" }
|
|
87
|
+
},
|
|
88
|
+
body: await requestBody(request, options)
|
|
89
|
+
});
|
|
90
|
+
return new Response(upstream.body, {
|
|
91
|
+
status: upstream.status,
|
|
92
|
+
headers: {
|
|
93
|
+
"Content-Type": upstream.headers.get("Content-Type") ?? "application/json",
|
|
94
|
+
"Cache-Control": upstream.headers.get("Cache-Control") ?? "no-store"
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
101
|
+
0 && (module.exports = {
|
|
102
|
+
createOpenAIOAuthProxy
|
|
103
|
+
});
|
|
8
104
|
//# sourceMappingURL=proxy.js.map
|
package/dist/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/proxy.ts","../src/codex-fetch.ts"],"sourcesContent":["/**\r\n * @file proxy.ts\r\n *\r\n * Server-side request handlers for proxying OpenAI Codex OAuth requests.\r\n * This module provides framework-agnostic handlers that can be used in\r\n * various server environments (Node.js, Deno, Cloudflare Workers, etc.)\r\n * to enable browser clients to make authenticated Codex requests.\r\n *\r\n * The proxy validates OAuth credentials (Authorization header and account ID),\r\n * rewrites requests to the Codex backend, and passes through responses.\r\n */\r\n\r\nimport { DEFAULT_CODEX_BASE_URL, normalizeCodexResponsesBody } from './codex-fetch';\r\nimport type { FetchLike, OpenAIOAuthSettings } from './types';\r\n\r\nexport type OpenAIOAuthProxyOptions = Pick<OpenAIOAuthSettings, 'instructions' | 'originator' | 'store'> & {\r\n fetch?: FetchLike;\r\n baseURL?: string;\r\n};\r\n\r\n/**\r\n * Resolves the fetch function to use for upstream requests.\r\n * Prefers a custom fetch if provided, falls back to globalThis.fetch,\r\n * and throws if neither is available.\r\n */\r\nfunction pickFetch(customFetch?: FetchLike): FetchLike {\r\n if (typeof customFetch === 'function') return customFetch;\r\n if (typeof globalThis.fetch === 'function') return globalThis.fetch.bind(globalThis);\r\n throw new Error('A fetch implementation is required for OpenAI Codex proxy handlers.');\r\n}\r\n\r\n/**\r\n * Creates a Response with no-store cache control header.\r\n * Used for error responses to prevent caching.\r\n */\r\nfunction noStoreText(text: string, status: number): Response {\r\n return new Response(text, {\r\n status,\r\n headers: {\r\n 'Content-Type': 'text/plain; charset=utf-8',\r\n 'Cache-Control': 'no-store',\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * Extracts and optionally normalizes the request body.\r\n * For JSON bodies, normalizes Codex-specific fields like instructions and store.\r\n */\r\nasync function requestBody(request: Request, options: OpenAIOAuthProxyOptions): Promise<string> {\r\n const text = await request.text();\r\n if (!text.trim()) return text;\r\n\r\n try {\r\n const parsed = JSON.parse(text) as unknown;\r\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return text;\r\n return JSON.stringify(normalizeCodexResponsesBody(parsed as Record<string, unknown>, options));\r\n } catch {\r\n return text;\r\n }\r\n}\r\n\r\n/** Create framework-agnostic server handlers for browser-safe Codex OAuth proxying. */\r\nexport function createOpenAIOAuthProxy(options: OpenAIOAuthProxyOptions = {}) {\r\n const fetch = pickFetch(options.fetch);\r\n const baseURL = (options.baseURL ?? DEFAULT_CODEX_BASE_URL).replace(/\\/$/, '');\r\n\r\n return {\r\n async responses(request: Request): Promise<Response> {\r\n const authorization = request.headers.get('authorization') ?? '';\r\n const accountId = request.headers.get('chatgpt-account-id') ?? '';\r\n if (!authorization.trim() || !accountId.trim()) {\r\n return noStoreText('Missing OpenAI OAuth credentials.', 401);\r\n }\r\n\r\n const upstream = await fetch(`${baseURL}/responses${new URL(request.url).search}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n Authorization: authorization,\r\n 'ChatGPT-Account-Id': accountId,\r\n 'OpenAI-Beta': 'responses=experimental',\r\n ...(options.originator === false ? {} : { originator: options.originator ?? 'openai-codex-oauth' }),\r\n },\r\n body: await requestBody(request, options),\r\n });\r\n\r\n return new Response(upstream.body, {\r\n status: upstream.status,\r\n headers: {\r\n 'Content-Type': upstream.headers.get('Content-Type') ?? 'application/json',\r\n 'Cache-Control': upstream.headers.get('Cache-Control') ?? 'no-store',\r\n },\r\n });\r\n },\r\n };\r\n}\r\n","/**\n * @file codex-fetch.ts\n *\n * Core module that provides the fetch wrapper for authenticated Codex requests.\n * This is the main entry point for making OAuth-authenticated requests to\n * the OpenAI Codex backend.\n *\n * Key features:\n * - Automatic token management and refresh before expiry\n * - Request/response normalization for Codex API compatibility\n * - Browser proxy URL resolution for cross-origin requests\n * - Account ID derivation from JWT claims\n */\n\nimport { refreshOpenAITokens } from './device-flow';\nimport { deriveAccountId, deriveExpiresAt } from './jwt';\nimport { createMemoryTokenStore } from './memory-token-store';\nimport type { FetchLike, OpenAIOAuthSettings, OpenAIOAuthTokens, TokenStore } from './types';\nimport { OpenAIOAuthError } from './types';\n\n/** Default OpenAI Codex backend base URL. */\nexport const DEFAULT_CODEX_BASE_URL = 'https://chatgpt.com/backend-api/codex';\n/** Default proxy base path when running in a browser environment. */\nconst DEFAULT_BROWSER_PROXY_BASE_URL = '/api/proxy/openai/codex';\n/** Default time (5 minutes) before expiry to trigger token refresh. */\nconst DEFAULT_REFRESH_MARGIN_MS = 5 * 60 * 1000;\n/** Default empty instructions value for Responses API requests. */\nconst DEFAULT_INSTRUCTIONS = '';\n/** Default originator header value identifying this library. */\nconst DEFAULT_ORIGINATOR = 'openai-codex-oauth';\n\n/**\n * Parsed request components used internally for request transformation.\n */\ntype RequestParts = {\n url: string;\n method?: string;\n headers: Headers;\n body?: BodyInit | null;\n signal?: AbortSignal | null;\n};\n\n/**\n * Resolves the fetch function to use for OAuth and Codex requests.\n * Prefers a custom fetch if provided, falls back to globalThis.fetch.\n */\nfunction pickFetch(customFetch?: FetchLike): FetchLike {\n if (typeof customFetch === 'function') {\n return customFetch;\n }\n\n if (typeof globalThis.fetch === 'function') {\n return globalThis.fetch.bind(globalThis);\n }\n\n throw new OpenAIOAuthError('fetch_required', 'A fetch implementation is required for OpenAI OAuth.');\n}\n\n/** Removes trailing slash from a URL string for consistent handling. */\nfunction withoutTrailingSlash(value: string): string {\n return value.replace(/\\/$/, '');\n}\n\n/** Resolves the base URL for Codex requests, defaulting to the official endpoint. */\nfunction resolveBaseURL(baseURL?: string): string {\n return withoutTrailingSlash(baseURL ?? DEFAULT_CODEX_BASE_URL);\n}\n\n/**\n * Creates a token store based on provided settings.\n * Prefers explicit tokenStore, then inline tokens, then throws an error.\n */\nfunction createStore(settings: OpenAIOAuthSettings): TokenStore {\n if (settings.tokenStore) {\n return settings.tokenStore;\n }\n\n if (settings.tokens) {\n return createMemoryTokenStore(settings.tokens);\n }\n\n throw new OpenAIOAuthError(\n 'tokens_required',\n 'OpenAI OAuth tokens are required. Pass `tokens`, `tokenStore`, or use `createCodexAuthFileStore` from `@tolksyn/openai-oauth/node`.',\n );\n}\n\n/**\n * Determines whether a token needs refresh based on expiry time.\n * Considers the marginMs parameter to refresh proactively before actual expiry.\n */\nfunction needsRefresh(tokens: OpenAIOAuthTokens, now: number, marginMs: number): boolean {\n const expiresAt = tokens.expiresAt ?? deriveExpiresAt(tokens.accessToken);\n if (!tokens.accessToken) {\n return true;\n }\n\n if (expiresAt == null || expiresAt <= 0) {\n return false;\n }\n\n return expiresAt <= now + marginMs;\n}\n\n/**\n * Checks if a token has already expired based on current time.\n */\nfunction hasExpired(tokens: OpenAIOAuthTokens, now: number): boolean {\n const expiresAt = tokens.expiresAt ?? deriveExpiresAt(tokens.accessToken);\n return expiresAt != null && expiresAt > 0 && expiresAt <= now;\n}\n\n/**\n * Manages OAuth token lifecycle including loading, caching, refresh, and validation.\n * Uses a promise coalescing pattern to handle concurrent token requests efficiently.\n */\nclass TokenManager {\n /** Promise for an in-flight token refresh to coalesce concurrent requests. */\n private inflight?: Promise<OpenAIOAuthTokens>;\n /** Cached current tokens to avoid redundant store loads. */\n private current?: OpenAIOAuthTokens;\n\n constructor(\n private readonly settings: OpenAIOAuthSettings,\n private readonly store: TokenStore,\n private readonly fetch: FetchLike,\n ) {}\n\n /**\n * Gets valid OAuth tokens, triggering refresh if needed.\n * Coalesces concurrent requests to avoid duplicate refresh calls.\n */\n async get(): Promise<OpenAIOAuthTokens> {\n if (this.inflight) {\n return this.inflight;\n }\n\n this.inflight = this.loadFresh()\n .then((tokens) => {\n this.current = tokens;\n this.inflight = undefined;\n return tokens;\n })\n .catch((error) => {\n this.inflight = undefined;\n throw error;\n });\n\n return this.inflight;\n }\n\n /**\n * Loads fresh tokens, optionally refreshing if expired or near expiry.\n * Derives account ID from JWT claims if not explicitly provided.\n */\n private async loadFresh(): Promise<OpenAIOAuthTokens> {\n let tokens = this.current ?? (await this.store.load());\n if (!tokens?.accessToken) {\n throw new OpenAIOAuthError('auth_failed', 'OpenAI OAuth access token is missing.');\n }\n\n const now = this.settings.now?.() ?? Date.now();\n const refreshMarginMs = this.settings.refreshMarginMs ?? DEFAULT_REFRESH_MARGIN_MS;\n\n if (needsRefresh(tokens, now, refreshMarginMs)) {\n const refreshed = await refreshOpenAITokens({\n tokens,\n fetch: this.fetch,\n clientId: this.settings.clientId,\n issuer: this.settings.issuer,\n tokenUrl: this.settings.tokenUrl,\n now: this.settings.now,\n });\n\n if (refreshed) {\n tokens = refreshed;\n await this.store.save(tokens);\n await this.settings.onTokens?.(tokens);\n } else if (hasExpired(tokens, now)) {\n throw new OpenAIOAuthError('auth_failed', 'OpenAI OAuth access token expired and refresh failed.');\n }\n }\n\n const accountId = tokens.accountId ?? deriveAccountId(tokens.idToken, tokens.accessToken);\n if (!accountId) {\n throw new OpenAIOAuthError(\n 'account_id_missing',\n 'OpenAI OAuth account id is missing. Store `accountId` with the tokens or include an id_token with the ChatGPT account claim.',\n );\n }\n\n return {\n ...tokens,\n accountId,\n };\n }\n}\n\n/**\n * Resolves a target URL relative to the Codex base URL.\n * Handles path rewriting from `/v1/responses` to Codex backend paths.\n */\nfunction resolveTargetUrl(input: string, baseURL: string): string {\n const base = new URL(baseURL);\n const parsed = /^https?:\\/\\//.test(input) ? new URL(input) : new URL(input, 'https://codex.invalid');\n let pathname = parsed.pathname;\n const basePath = withoutTrailingSlash(base.pathname);\n\n if (pathname === basePath) {\n pathname = '/';\n } else if (basePath && pathname.startsWith(`${basePath}/`)) {\n pathname = pathname.slice(basePath.length);\n }\n\n if (pathname === '/v1') {\n pathname = '/';\n } else if (pathname.startsWith('/v1/')) {\n pathname = pathname.slice(3);\n }\n\n if (!pathname.startsWith('/')) {\n pathname = `/${pathname}`;\n }\n\n return `${base.origin}${basePath}${pathname}${parsed.search}`;\n}\n\n/**\n * Detects the browser's origin from window.location if available.\n * Returns undefined in non-browser environments.\n */\nfunction browserOrigin(): string | undefined {\n const maybeWindow = (globalThis as { window?: { location?: { origin?: string } } }).window;\n return typeof maybeWindow?.location?.origin === 'string' ? maybeWindow.location.origin.replace(/\\/$/, '') : undefined;\n}\n\n/**\n * Resolves a proxy URL for browser environments to handle cross-origin requests.\n * When running in a browser, direct Codex requests may fail CORS, so we proxy\n * through a local path that forwards to the Codex backend.\n */\nfunction resolveBrowserProxyUrl(target: URL, baseURL: string, settings: OpenAIOAuthSettings): string | undefined {\n const origin = browserOrigin();\n if (!origin || settings.browserProxyBaseUrl === false) {\n return undefined;\n }\n\n const proxyBase = settings.browserProxyBaseUrl ?? DEFAULT_BROWSER_PROXY_BASE_URL;\n const absoluteProxyBase = /^https?:\\/\\//.test(proxyBase) ? proxyBase.replace(/\\/$/, '') : `${origin}${proxyBase.startsWith('/') ? '' : '/'}${proxyBase}`.replace(/\\/$/, '');\n const upstreamBasePath = withoutTrailingSlash(new URL(baseURL).pathname);\n let pathname = target.pathname;\n\n if (upstreamBasePath && pathname.startsWith(`${upstreamBasePath}/`)) {\n pathname = pathname.slice(upstreamBasePath.length);\n }\n\n return `${absoluteProxyBase}${pathname}${target.search}`;\n}\n\n/**\n * Parses a fetch input (Request or URL string) into structured request parts.\n * Handles merging of Request defaults with optional init overrides.\n */\nasync function readRequestParts(input: Parameters<FetchLike>[0], init?: Parameters<FetchLike>[1]): Promise<RequestParts> {\n if (input instanceof Request) {\n const headers = new Headers(input.headers);\n if (init?.headers) {\n new Headers(init.headers).forEach((value, key) => headers.set(key, value));\n }\n\n return {\n url: input.url,\n method: init?.method ?? input.method,\n headers,\n body: init?.body ?? (input.body == null ? undefined : await input.clone().text()),\n signal: init?.signal ?? input.signal,\n };\n }\n\n return {\n url: String(input),\n method: init?.method,\n headers: new Headers(init?.headers),\n body: init?.body,\n signal: init?.signal,\n };\n}\n\n/**\n * Attempts to decode a request body to a string for JSON parsing.\n * Supports string, Blob, and ArrayBuffer types; returns undefined for\n * types that cannot be meaningfully decoded (FormData, ReadableStream, etc.)\n */\nasync function decodeBody(body: BodyInit | null | undefined): Promise<string | undefined> {\n if (body == null) {\n return undefined;\n }\n\n if (typeof body === 'string') {\n return body;\n }\n\n if (body instanceof URLSearchParams || body instanceof FormData || body instanceof ReadableStream) {\n return undefined;\n }\n\n if (body instanceof Blob) {\n return body.text();\n }\n\n if (body instanceof ArrayBuffer) {\n return new TextDecoder().decode(body);\n }\n\n if (ArrayBuffer.isView(body)) {\n return new TextDecoder().decode(body);\n }\n\n return undefined;\n}\n\n/**\n * Normalize an OpenAI Responses request body for the Codex backend.\n *\n * Codex expects requests to be stateless by default, so `store` defaults to\n * `false`. Unsupported `max_output_tokens` is removed.\n */\nexport function normalizeCodexResponsesBody(\n body: Record<string, unknown>,\n options: Pick<OpenAIOAuthSettings, 'instructions' | 'store'> = {},\n): Record<string, unknown> {\n const normalized = { ...body };\n\n if (typeof normalized.instructions !== 'string') {\n normalized.instructions = options.instructions ?? DEFAULT_INSTRUCTIONS;\n }\n\n if (normalized.store === undefined) {\n normalized.store = options.store ?? false;\n }\n\n delete normalized.max_output_tokens;\n\n return normalized;\n}\n\n/**\n * Prepares the request body for Codex /responses endpoints.\n * Normalizes JSON bodies to ensure Codex-compatible request format.\n */\nasync function prepareBody(pathname: string, headers: Headers, body: BodyInit | null | undefined, settings: OpenAIOAuthSettings) {\n if (!pathname.endsWith('/responses')) {\n return body;\n }\n\n const contentType = headers.get('content-type');\n if (contentType && !contentType.includes('application/json')) {\n return body;\n }\n\n const bodyText = await decodeBody(body);\n if (typeof bodyText !== 'string') {\n return body;\n }\n\n try {\n const parsed = JSON.parse(bodyText) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return body;\n }\n\n return JSON.stringify(normalizeCodexResponsesBody(parsed as Record<string, unknown>, settings));\n } catch {\n return body;\n }\n}\n\n/**\n * Create a fetch implementation that authenticates OpenAI Responses requests\n * with Codex OAuth credentials.\n *\n * The returned function rewrites `/v1/responses` requests to the Codex backend,\n * injects `Authorization` and `ChatGPT-Account-Id`, and refreshes credentials\n * before expiry when a refresh token is available.\n */\nexport function createCodexOAuthFetch(settings: OpenAIOAuthSettings = {}): FetchLike {\n const fetch = pickFetch(settings.fetch);\n const store = createStore(settings);\n const manager = new TokenManager(settings, store, fetch);\n const baseURL = resolveBaseURL(settings.baseURL);\n\n return async (input, init) => {\n const request = await readRequestParts(input, init);\n const targetUrl = resolveTargetUrl(request.url, baseURL);\n const target = new URL(targetUrl);\n const tokens = await manager.get();\n const headers = new Headers(settings.headers);\n\n request.headers.forEach((value, key) => headers.set(key, value));\n headers.delete('authorization');\n headers.delete('chatgpt-account-id');\n headers.delete('openai-beta');\n\n headers.set('Authorization', `Bearer ${tokens.accessToken}`);\n headers.set('ChatGPT-Account-Id', tokens.accountId!);\n headers.set('OpenAI-Beta', 'responses=experimental');\n\n if (settings.originator !== false && !headers.has('originator')) {\n headers.set('originator', settings.originator ?? DEFAULT_ORIGINATOR);\n }\n\n const body = await prepareBody(target.pathname, headers, request.body, settings);\n\n return fetch(resolveBrowserProxyUrl(target, baseURL, settings) ?? target.toString(), {\n method: request.method ?? init?.method,\n headers,\n body,\n signal: request.signal ?? undefined,\n });\n };\n}\n\n/** Create a small Codex client around `createCodexOAuthFetch`. */\nexport function createCodexOAuthClient(settings: OpenAIOAuthSettings = {}) {\n const baseURL = resolveBaseURL(settings.baseURL);\n const fetch = createCodexOAuthFetch(settings);\n\n return {\n baseURL,\n fetch,\n request: (path: string, init?: RequestInit) => fetch(resolveTargetUrl(path, baseURL), init),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACqBO,IAAM,yBAAyB;AAItC,IAAM,4BAA4B,IAAI,KAAK;AAE3C,IAAM,uBAAuB;AA4StB,SAAS,4BACd,MACA,UAA+D,CAAC,GACvC;AACzB,QAAM,aAAa,EAAE,GAAG,KAAK;AAE7B,MAAI,OAAO,WAAW,iBAAiB,UAAU;AAC/C,eAAW,eAAe,QAAQ,gBAAgB;AAAA,EACpD;AAEA,MAAI,WAAW,UAAU,QAAW;AAClC,eAAW,QAAQ,QAAQ,SAAS;AAAA,EACtC;AAEA,SAAO,WAAW;AAElB,SAAO;AACT;;;AD/TA,SAAS,UAAU,aAAoC;AACrD,MAAI,OAAO,gBAAgB,WAAY,QAAO;AAC9C,MAAI,OAAO,WAAW,UAAU,WAAY,QAAO,WAAW,MAAM,KAAK,UAAU;AACnF,QAAM,IAAI,MAAM,qEAAqE;AACvF;AAMA,SAAS,YAAY,MAAc,QAA0B;AAC3D,SAAO,IAAI,SAAS,MAAM;AAAA,IACxB;AAAA,IACA,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,IACnB;AAAA,EACF,CAAC;AACH;AAMA,eAAe,YAAY,SAAkB,SAAmD;AAC9F,QAAM,OAAO,MAAM,QAAQ,KAAK;AAChC,MAAI,CAAC,KAAK,KAAK,EAAG,QAAO;AAEzB,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,QAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,MAAM,QAAQ,MAAM,EAAG,QAAO;AACnF,WAAO,KAAK,UAAU,4BAA4B,QAAmC,OAAO,CAAC;AAAA,EAC/F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,uBAAuB,UAAmC,CAAC,GAAG;AAC5E,QAAM,QAAQ,UAAU,QAAQ,KAAK;AACrC,QAAM,WAAW,QAAQ,WAAW,wBAAwB,QAAQ,OAAO,EAAE;AAE7E,SAAO;AAAA,IACL,MAAM,UAAU,SAAqC;AACnD,YAAM,gBAAgB,QAAQ,QAAQ,IAAI,eAAe,KAAK;AAC9D,YAAM,YAAY,QAAQ,QAAQ,IAAI,oBAAoB,KAAK;AAC/D,UAAI,CAAC,cAAc,KAAK,KAAK,CAAC,UAAU,KAAK,GAAG;AAC9C,eAAO,YAAY,qCAAqC,GAAG;AAAA,MAC7D;AAEA,YAAM,WAAW,MAAM,MAAM,GAAG,OAAO,aAAa,IAAI,IAAI,QAAQ,GAAG,EAAE,MAAM,IAAI;AAAA,QACjF,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe;AAAA,UACf,sBAAsB;AAAA,UACtB,eAAe;AAAA,UACf,GAAI,QAAQ,eAAe,QAAQ,CAAC,IAAI,EAAE,YAAY,QAAQ,cAAc,qBAAqB;AAAA,QACnG;AAAA,QACA,MAAM,MAAM,YAAY,SAAS,OAAO;AAAA,MAC1C,CAAC;AAED,aAAO,IAAI,SAAS,SAAS,MAAM;AAAA,QACjC,QAAQ,SAAS;AAAA,QACjB,SAAS;AAAA,UACP,gBAAgB,SAAS,QAAQ,IAAI,cAAc,KAAK;AAAA,UACxD,iBAAiB,SAAS,QAAQ,IAAI,eAAe,KAAK;AAAA,QAC5D;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openai-codex-oauth",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "OpenAI Codex OAuth provider and token helpers for the Vercel AI SDK.",
|
|
5
5
|
"author": "respectmathias",
|
|
6
|
-
"type": "module",
|
|
7
6
|
"license": "MIT",
|
|
8
7
|
"repository": {
|
|
9
8
|
"type": "git",
|