@codeworksh/aikit 0.4.0 → 0.4.1-dev.20260530133305
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +109 -137
- package/cli.d.mts +1 -0
- package/cli.mjs +308 -0
- package/cli.mjs.map +1 -0
- package/codex-DfYk740t.mjs +521 -0
- package/codex-DfYk740t.mjs.map +1 -0
- package/index.d.mts +7197 -0
- package/index.mjs +1478 -0
- package/index.mjs.map +1 -0
- package/oauth/openai/codex.d.mts +107 -0
- package/oauth/openai/codex.mjs +2 -0
- package/package.json +28 -33
- package/{dist/pack/index.mjs → registry-CYfzJ6lk.mjs} +210 -3300
- package/registry-CYfzJ6lk.mjs.map +1 -0
- package/dist/pack/agent/codemode/drivers/drivers.d.mts +0 -7
- package/dist/pack/agent/codemode/drivers/drivers.mjs +0 -122
- package/dist/pack/agent/codemode/drivers/drivers.mjs.map +0 -1
- package/dist/pack/codemode-BEDXG7WN.d.mts +0 -8736
- package/dist/pack/index.d.mts +0 -71
- package/dist/pack/index.mjs.map +0 -1
- package/src/agent/agent.ts +0 -790
- package/src/agent/codemode/codemode.ts +0 -561
- package/src/agent/codemode/drivers/drivers.ts +0 -1
- package/src/agent/codemode/drivers/quickjs-wasi-driver.ts +0 -149
- package/src/agent/codemode/index.ts +0 -1
- package/src/agent/loop.ts +0 -903
- package/src/agent.ts +0 -11
- package/src/event/event.ts +0 -277
- package/src/index.ts +0 -6
- package/src/llm.ts +0 -31
- package/src/message/message.ts +0 -311
- package/src/model/catalog.ts +0 -94
- package/src/model/model.ts +0 -250
- package/src/model/transform.ts +0 -59
- package/src/provider/provider.ts +0 -60
- package/src/provider/providers/anthropic/index.ts +0 -792
- package/src/provider/providers/github/github.ts +0 -34
- package/src/provider/providers/openai/completions.ts +0 -953
- package/src/provider/providers/utils.ts +0 -7
- package/src/provider/register.ts +0 -6
- package/src/provider/stream.ts +0 -278
- package/src/stream.ts +0 -72
- package/src/utils/eventstream.ts +0 -95
- package/src/utils/helpers.ts +0 -24
- package/src/utils/jsonparse.ts +0 -32
- package/src/utils/overflow.ts +0 -121
- package/src/utils/sanitize.ts +0 -25
- package/src/utils/validation.ts +0 -87
package/cli.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["pkg.version"],"sources":["../../package.json","../../src/cli/modelgen.ts","../../src/cli/oauth.ts","../../src/cli.ts"],"sourcesContent":["","import { Filesystem, lazy } from \"@codeworksh/utils\";\nimport { resolve } from \"node:path\";\nimport { mapValues, pickBy, pipe } from \"remeda\";\nimport type { CommandModule } from \"yargs\";\nimport { DEFAULT_AI_SDK_PACKAGE, isAISDKPackage, protocolForPackage } from \"../llm/registry\";\nimport { ModelCatalog } from \"../model/catalog\";\nimport { Model } from \"../model/model\";\n\nconst DEFAULT_PROVIDER_BASE_URLS: Partial<Record<Model.KnownProviderEnum, string>> = {\n\t[Model.KnownProviderEnum.anthropic]: \"https://api.anthropic.com/v1\",\n\t[Model.KnownProviderEnum.openai]: \"https://api.openai.com/v1\",\n\t[Model.KnownProviderEnum.openrouter]: \"https://openrouter.ai/api/v1\",\n\t[Model.KnownProviderEnum.xai]: \"https://api.x.ai/v1\",\n};\n\ninterface ModelsDevModel {\n\tid: string;\n\tname: string;\n\tfamily: string;\n\tattachment: boolean;\n\treasoning?: boolean;\n\ttool_call?: boolean;\n\ttemperature?: boolean;\n\tknowledge?: string | boolean;\n\trelease_date: string;\n\tlast_updated: string;\n\tmodalities: {\n\t\tinput?: string[];\n\t\toutput: string[];\n\t};\n\topen_weights: boolean;\n\tbaseUrl?: string;\n\theaders?: Record<string, string>;\n\tcost?: {\n\t\tinput?: number;\n\t\toutput?: number;\n\t\tcache_read?: number;\n\t\tcache_write?: number;\n\t};\n\tlimit?: {\n\t\tcontext?: number;\n\t\toutput?: number;\n\t};\n\tstructured_output?: boolean;\n\tprovider?: {\n\t\tnpm?: string;\n\t\tapi?: string;\n\t\theaders?: Record<string, string>;\n\t};\n}\n\ninterface ModelsDevProvider {\n\tid: string;\n\tenv: string[];\n\tnpm?: string;\n\tapi?: string;\n\tname: string;\n\tdoc?: string; // reference documentation link\n\tkey?: string; // runtime placeholder for api/oauth key\n\theaders?: Record<string, string>; // custom overrides at provider level\n\tmodels: Record<string, ModelsDevModel>;\n}\n\nexport type BuiltInModels = Partial<Record<string, Record<string, Model.Info>>>;\n\n// Doesn't represent the full spec from models.dev.\n// Represents the provider entries on a best-effort basis.\ntype ModelsDevCatalog = Record<string, ModelsDevProvider>;\ntype LazyModelsDevCatalog = ReturnType<typeof lazy<Promise<ModelsDevCatalog>>>;\n\nfunction modelsDevURL(): string {\n\treturn process.env.OPENCODE_MODELS_URL || \"https://models.dev\";\n}\n\nfunction modelsDevPath(): string | undefined {\n\treturn process.env.CODEWORK_MODELS_DEV;\n}\n\nconst modelsDevData: LazyModelsDevCatalog = lazy(async () => {\n\tconst path = modelsDevPath();\n\tif (path) {\n\t\tconst result = await Filesystem.readJson<ModelsDevCatalog>(path).catch(() => undefined);\n\t\tif (result) return result;\n\t}\n\n\tconst json = await fetch(`${modelsDevURL()}/api.json`).then((x) => x.text());\n\treturn JSON.parse(json) as ModelsDevCatalog;\n});\n\nasync function pullModelsDevData(): Promise<ModelsDevCatalog> {\n\treturn modelsDevData();\n}\n\nfunction toModelValue(providerId: string, provider: ModelsDevProvider, model: ModelsDevModel): Model.Info | undefined {\n\treturn applyModification(providerId, provider, model);\n}\n\nasync function loadBuiltInFromModelsDev() {\n\tconst catalog = await pullModelsDevData();\n\treturn pipe(\n\t\tcatalog,\n\t\tpickBy((provider) =>\n\t\t\tObject.values(provider.models).some(\n\t\t\t\t(model) => Boolean(model.tool_call) && isAISDKPackage(resolveModelNpm(provider, model)),\n\t\t\t),\n\t\t),\n\t\tmapValues((provider, providerId) =>\n\t\t\tpipe(\n\t\t\t\tprovider.models,\n\t\t\t\tpickBy((model) => Boolean(model.tool_call) && isAISDKPackage(resolveModelNpm(provider, model))),\n\t\t\t\tmapValues((model) => toModelValue(providerId, provider, model)),\n\t\t\t\tpickBy((model): model is Model.Info => model !== undefined),\n\t\t\t),\n\t\t),\n\t) as BuiltInModels;\n}\n\ntype ThinkingLevelMap = NonNullable<Model.Info[\"thinkingLevelMap\"]>;\n\nfunction resolveModelNpm(provider: ModelsDevProvider, model: ModelsDevModel): string {\n\treturn model.provider?.npm ?? provider.npm ?? DEFAULT_AI_SDK_PACKAGE;\n}\n\nfunction resolveModelBaseUrl(provider: ModelsDevProvider, model: ModelsDevModel): string {\n\tconst npm = resolveModelNpm(provider, model);\n\tconst protocol = protocolForPackage(npm);\n\treturn model.baseUrl ?? model.provider?.api ?? provider.api ?? DEFAULT_PROVIDER_BASE_URLS[protocol] ?? \"\";\n}\n\nfunction resolveModelAPIMethod(npm: string): Model.APIMethodEnum {\n\tconst protocol = protocolForPackage(npm);\n\t// `By default, xai(modelId) uses the Chat API.\n\t// To use the Responses API with server-side agentic tools, explicitly use xai.responses(modelId).`\n\t// References: https://ai-sdk.dev/providers/ai-sdk-providers/xai\n\tif (protocol === Model.KnownProviderEnum.openai || protocol === Model.KnownProviderEnum.xai) {\n\t\treturn Model.APIMethodEnum.responses;\n\t}\n\treturn Model.APIMethodEnum.languageModel;\n}\n\nfunction resolveProviderOptionsKey(protocol: Model.KnownProviderEnum): string {\n\tif (protocol === Model.KnownProviderEnum.openaiCompatible) return \"openai-compatible\";\n\tif (protocol === Model.KnownProviderEnum.googleVertex) return \"google-vertex\";\n\tif (protocol === Model.KnownProviderEnum.googleVertexAnthropic) return \"google-vertex-anthropic\";\n\treturn protocol;\n}\n\n//\n// adhoc helpers variations\nconst OPENAI_RESPONSES_NONE_REASONING_MODELS = new Set([\n\t\"gpt-5.1\",\n\t\"gpt-5.2\",\n\t\"gpt-5.3-codex\",\n\t\"gpt-5.4\",\n\t\"gpt-5.4-mini\",\n\t\"gpt-5.4-nano\",\n\t\"gpt-5.5\",\n]);\n\nfunction supportsOpenAiXhigh(modelId: string): boolean {\n\treturn (\n\t\tmodelId.includes(\"gpt-5.2\") ||\n\t\tmodelId.includes(\"gpt-5.3\") ||\n\t\tmodelId.includes(\"gpt-5.4\") ||\n\t\tmodelId.includes(\"gpt-5.5\")\n\t);\n}\n\nfunction mergeThinkingLevelMap(model: Model.Info, map: ThinkingLevelMap): void {\n\tmodel.thinkingLevelMap = { ...model.thinkingLevelMap, ...map };\n}\n\nfunction applyThinkingLevelMetadata(model: Model.Info): void {\n\tif (model.protocol === Model.KnownProviderEnum.openai && model.id.startsWith(\"gpt-5\")) {\n\t\tmergeThinkingLevelMap(model, { off: null });\n\t}\n\tif (\n\t\tmodel.protocol === Model.KnownProviderEnum.openai &&\n\t\tmodel.provider.id === \"openai\" &&\n\t\tOPENAI_RESPONSES_NONE_REASONING_MODELS.has(model.id)\n\t) {\n\t\tmergeThinkingLevelMap(model, { off: \"none\" });\n\t}\n\tif (supportsOpenAiXhigh(model.id)) {\n\t\tmergeThinkingLevelMap(model, { xhigh: \"xhigh\" });\n\t}\n\tif (model.id.includes(\"opus-4-6\") || model.id.includes(\"opus-4.6\")) {\n\t\tmergeThinkingLevelMap(model, { xhigh: \"max\" });\n\t}\n\tif (model.id.includes(\"opus-4-7\") || model.id.includes(\"opus-4.7\")) {\n\t\tmergeThinkingLevelMap(model, { xhigh: \"xhigh\" });\n\t}\n}\n\nexport async function generateModels(args: { path?: string } = {}): Promise<string> {\n\tconst path = resolve(args.path ?? ModelCatalog.path());\n\tconst models = await loadBuiltInFromModelsDev();\n\tawait Filesystem.writeJson(path, models);\n\treturn path;\n}\n\n//\n// CLI entry\nexport const ModelgenCommand: CommandModule<object, { path?: string }> = {\n\tcommand: \"modelgen [path]\",\n\tdescribe: \"generate models.gen.json\",\n\tbuilder: (yargs) =>\n\t\tyargs.positional(\"path\", {\n\t\t\ttype: \"string\",\n\t\t\tdescribe: \"output path (defaults to CODEWORK_MODELGEN_PATH or ./models.gen.json)\",\n\t\t}),\n\thandler: async (args) => {\n\t\tconst path = await generateModels(args);\n\t\tconsole.log(`Generated model catalog at ${path}`);\n\t},\n};\n\nexport function applyModification(\n\tproviderId: string,\n\tprovider: ModelsDevProvider,\n\tmodel: ModelsDevModel,\n): Model.Info | undefined {\n\tconst npm = resolveModelNpm(provider, model);\n\tif (!isAISDKPackage(npm)) return;\n\n\tconst protocol = protocolForPackage(npm);\n\tconst baseUrl = resolveModelBaseUrl(provider, model);\n\tconst providerInfo: Model.ProviderInfo = {\n\t\tid: providerId,\n\t\tname: provider.name,\n\t\tsource: \"api\",\n\t\tenv: provider.env,\n\t};\n\tif (provider.key) providerInfo.key = provider.key;\n\tconst api: Model.APIMetadata = {\n\t\tid: model.id,\n\t\tmethod: resolveModelAPIMethod(npm),\n\t};\n\tif (baseUrl) api.url = baseUrl;\n\n\tconst info: Model.Info = {\n\t\tid: model.id,\n\t\tname: model.name,\n\t\tprovider: providerInfo,\n\t\tbaseUrl,\n\t\treasoning: Boolean(model.reasoning),\n\t\tinput: Model.normalizeInput(model.modalities.input),\n\t\tcost: {\n\t\t\tinput: model.cost?.input ?? 0,\n\t\t\toutput: model.cost?.output ?? 0,\n\t\t\tcacheRead: model.cost?.cache_read ?? 0,\n\t\t\tcacheWrite: model.cost?.cache_write ?? 0,\n\t\t},\n\t\tcontextWindow: model.limit?.context ?? 4096,\n\t\tmaxTokens: model.limit?.output ?? 4096,\n\t\theaders: {\n\t\t\t...provider.headers,\n\t\t\t...model.provider?.headers,\n\t\t\t...model.headers,\n\t\t},\n\t\tnpm,\n\t\tapi,\n\t\tproviderOptionsKey: resolveProviderOptionsKey(protocol),\n\t\tprotocol,\n\t};\n\tif (Object.keys(info.headers ?? {}).length === 0) delete info.headers;\n\tapplyThinkingLevelMetadata(info);\n\treturn info;\n}\n","import type { CommandModule } from \"yargs\";\nimport {\n\tJsonOpenAICodexAuthStorage,\n\tOpenAICodexOAuthClient,\n\ttype OpenAICodexOAuthCredentials,\n\topenAICodexHeaders,\n} from \"../oauth/openai/codex\";\n\ntype AuthArgs = {\n\topenaiCodex?: boolean;\n\tauthFile?: string;\n\tbrowser?: boolean;\n\tmanual?: boolean;\n\tstatus?: boolean;\n\trefresh?: boolean;\n\tlogout?: boolean;\n\tjson?: boolean;\n\tprintHeaders?: boolean;\n\toriginator?: string;\n};\n\nasync function promptLine(message: string): Promise<string> {\n\tconst readline = await import(\"node:readline/promises\");\n\tconst rl = readline.createInterface({\n\t\tinput: process.stdin,\n\t\toutput: process.stdout,\n\t});\n\n\ttry {\n\t\treturn await rl.question(`${message} `);\n\t} finally {\n\t\trl.close();\n\t}\n}\n\nasync function openBrowser(url: string): Promise<void> {\n\tconst { spawn } = await import(\"node:child_process\");\n\tconst command =\n\t\tprocess.platform === \"darwin\"\n\t\t\t? { file: \"open\", args: [url] }\n\t\t\t: process.platform === \"win32\"\n\t\t\t\t? { file: \"cmd\", args: [\"/c\", \"start\", \"\", url] }\n\t\t\t\t: { file: \"xdg-open\", args: [url] };\n\n\tconst child = spawn(command.file, command.args, {\n\t\tdetached: true,\n\t\tstdio: \"ignore\",\n\t});\n\tchild.unref();\n}\n\nfunction printCredentials(\n\tcredentials: OpenAICodexOAuthCredentials,\n\toptions: { json?: boolean; printHeaders?: boolean },\n) {\n\tif (options.json) {\n\t\tconsole.log(\n\t\t\tJSON.stringify(\n\t\t\t\t{\n\t\t\t\t\taccountId: credentials.accountId,\n\t\t\t\t\texpires: credentials.expires,\n\t\t\t\t\texpiresAt: new Date(credentials.expires).toISOString(),\n\t\t\t\t\theaders: options.printHeaders ? openAICodexHeaders(credentials) : undefined,\n\t\t\t\t},\n\t\t\t\tnull,\n\t\t\t\t2,\n\t\t\t),\n\t\t);\n\t\treturn;\n\t}\n\n\tconsole.log(`Account: ${credentials.accountId}`);\n\tconsole.log(`Expires: ${new Date(credentials.expires).toLocaleString()}`);\n\tif (options.printHeaders) {\n\t\tconsole.log(\"Headers:\");\n\t\tfor (const [name, value] of Object.entries(openAICodexHeaders(credentials))) {\n\t\t\tconsole.log(`${name}: ${value}`);\n\t\t}\n\t}\n}\n\nasync function runOpenAICodexAuth(args: AuthArgs): Promise<void> {\n\tconst storage = new JsonOpenAICodexAuthStorage({ path: args.authFile });\n\tconst client = new OpenAICodexOAuthClient({ storage });\n\n\tif (args.logout) {\n\t\tawait client.logout();\n\t\tconsole.log(`Cleared OpenAI Codex credentials from ${storage.path}`);\n\t\treturn;\n\t}\n\n\tif (args.status) {\n\t\tconst credentials = await storage.get();\n\t\tif (!credentials) {\n\t\t\tconsole.log(`No OpenAI Codex credentials found at ${storage.path}`);\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t\tprintCredentials(credentials, args);\n\t\treturn;\n\t}\n\n\tif (args.refresh) {\n\t\tconst credentials = await client.getCredentials();\n\t\tif (!credentials) {\n\t\t\tconsole.log(`No OpenAI Codex credentials found at ${storage.path}`);\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t\tconsole.log(`Refreshed OpenAI Codex credentials in ${storage.path}`);\n\t\tprintCredentials(credentials, args);\n\t\treturn;\n\t}\n\n\tconst credentials = await client.login({\n\t\toriginator: args.originator,\n\t\tonAuth: (info) => {\n\t\t\tconsole.log(info.instructions ?? \"Complete OpenAI Codex authentication in your browser.\");\n\t\t\tconsole.log(info.url);\n\t\t\tif (args.browser !== false) {\n\t\t\t\tvoid openBrowser(info.url).catch((error) => {\n\t\t\t\t\tconsole.warn(`Failed to open browser: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t\tonPrompt: async (prompt) => promptLine(prompt.message),\n\t\tonManualCodeInput: args.manual\n\t\t\t? async () => promptLine(\"Paste the redirect URL or authorization code, or wait for browser callback:\")\n\t\t\t: undefined,\n\t});\n\n\tconsole.log(`Saved OpenAI Codex credentials to ${storage.path}`);\n\tprintCredentials(credentials, args);\n}\n\nexport const OAuthCommand: CommandModule<object, AuthArgs> = {\n\tcommand: \"auth\",\n\tdescribe: \"manage OAuth credentials\",\n\tbuilder: (yargs) =>\n\t\tyargs\n\t\t\t.option(\"openai-codex\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"use OpenAI Codex OAuth\",\n\t\t\t})\n\t\t\t.option(\"auth-file\", {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdescribe: \"path to auth.json (defaults to CODEWORK_CREDENTIALS or ~/.codework/aikit/auth.json)\",\n\t\t\t})\n\t\t\t.option(\"browser\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdefault: true,\n\t\t\t\tdescribe: \"open the authorization URL in the default browser\",\n\t\t\t})\n\t\t\t.option(\"manual\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdefault: false,\n\t\t\t\tdescribe: \"also prompt for a pasted redirect URL or authorization code\",\n\t\t\t})\n\t\t\t.option(\"originator\", {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdefault: \"codework\",\n\t\t\t\tdescribe: \"OAuth originator value\",\n\t\t\t})\n\t\t\t.option(\"status\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"show stored credential status without refreshing\",\n\t\t\t})\n\t\t\t.option(\"refresh\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"refresh stored credentials if expired\",\n\t\t\t})\n\t\t\t.option(\"logout\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"clear stored credentials\",\n\t\t\t})\n\t\t\t.option(\"json\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"print machine-readable output\",\n\t\t\t})\n\t\t\t.option(\"print-headers\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"print request headers for Codex API calls\",\n\t\t\t})\n\t\t\t.check((args) => {\n\t\t\t\tif (!args.openaiCodex) throw new Error(\"Choose an auth provider, for example: auth --openai-codex\");\n\t\t\t\tconst actions = [args.status, args.refresh, args.logout].filter(Boolean).length;\n\t\t\t\tif (actions > 1) throw new Error(\"Choose only one of --status, --refresh, or --logout\");\n\t\t\t\treturn true;\n\t\t\t}),\n\thandler: async (args) => {\n\t\tawait runOpenAICodexAuth(args);\n\t},\n};\n","#!/usr/bin/env node\nimport pkg from \"../package.json\" with { type: \"json\" };\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { ModelgenCommand } from \"./cli/modelgen\";\nimport { OAuthCommand } from \"./cli/oauth\";\n\nconst cli = yargs(hideBin(process.argv))\n\t.scriptName(\"aikit\")\n\t.wrap(100)\n\t.help(\"help\", \"show help\")\n\t.alias(\"help\", \"h\")\n\t.version(\"version\", \"show version number\", pkg.version)\n\t.alias(\"version\", \"v\")\n\t.command(ModelgenCommand)\n\t.command(OAuthCommand)\n\t.demandCommand(1, \"Choose a command\")\n\t.strict()\n\t.fail((message, error) => {\n\t\tif (error) throw error;\n\t\tif (message) throw new Error(message);\n\t});\n\ntry {\n\tawait cli.parse();\n} catch (error) {\n\tprocess.exitCode = 1;\n\tconsole.error(error instanceof Error ? error.message : String(error));\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
import dedent from "dedent";
|
|
2
|
+
//#region src/oauth/openai/codex.ts
|
|
3
|
+
/**
|
|
4
|
+
* OpenAI Codex OAuth helpers.
|
|
5
|
+
*
|
|
6
|
+
* This flow is intended for Node.js CLI/server environments. The low-level
|
|
7
|
+
* authorization helpers are storage-free so they can also be used from an
|
|
8
|
+
* existing HTTP server callback route.
|
|
9
|
+
*/
|
|
10
|
+
const CODEWORK_OAUTH_CALLBACK_HOST = "CODEWORK_OAUTH_CALLBACK_HOST";
|
|
11
|
+
const CODEWORK_CREDENTIALS = "CODEWORK_CREDENTIALS";
|
|
12
|
+
const CODEWORK_HOME_DIR = "CODEWORK_HOME_DIR";
|
|
13
|
+
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
14
|
+
const AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
|
|
15
|
+
const TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
16
|
+
const DEFAULT_REDIRECT_URI = "http://localhost:1455/auth/callback";
|
|
17
|
+
const DEFAULT_SCOPE = "openid profile email offline_access";
|
|
18
|
+
const DEFAULT_ORIGINATOR = "codework";
|
|
19
|
+
const DEFAULT_PROVIDER_ID = "openai-codex";
|
|
20
|
+
const JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
21
|
+
const REFRESH_SKEW_MS = 60 * 1e3;
|
|
22
|
+
function readEnv(name) {
|
|
23
|
+
if (typeof process === "undefined") return void 0;
|
|
24
|
+
return process.env[name];
|
|
25
|
+
}
|
|
26
|
+
function joinPath(...parts) {
|
|
27
|
+
return parts.filter(Boolean).join("/").replaceAll(/\/+/g, "/");
|
|
28
|
+
}
|
|
29
|
+
function expandHome(path) {
|
|
30
|
+
if (path === "~") return homeDirectory();
|
|
31
|
+
if (path.startsWith("~/")) return joinPath(homeDirectory(), path.slice(2));
|
|
32
|
+
return path;
|
|
33
|
+
}
|
|
34
|
+
function homeDirectory() {
|
|
35
|
+
const home = readEnv("HOME") ?? readEnv("USERPROFILE");
|
|
36
|
+
if (!home) throw new Error("Unable to resolve home directory for OpenAI Codex auth storage");
|
|
37
|
+
return home;
|
|
38
|
+
}
|
|
39
|
+
function codeworkHomeDirectory() {
|
|
40
|
+
const override = readEnv(CODEWORK_HOME_DIR);
|
|
41
|
+
if (override) return expandHome(override);
|
|
42
|
+
return joinPath(homeDirectory(), ".codework");
|
|
43
|
+
}
|
|
44
|
+
function defaultOpenAICodexAuthFilePath() {
|
|
45
|
+
const authFile = readEnv(CODEWORK_CREDENTIALS);
|
|
46
|
+
if (authFile) return expandHome(authFile);
|
|
47
|
+
return joinPath(codeworkHomeDirectory(), "aikit", "auth.json");
|
|
48
|
+
}
|
|
49
|
+
function isObject(value) {
|
|
50
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
51
|
+
}
|
|
52
|
+
function isOpenAICodexCredentials(value) {
|
|
53
|
+
if (!isObject(value)) return false;
|
|
54
|
+
return typeof value.access === "string" && typeof value.refresh === "string" && typeof value.expires === "number" && typeof value.accountId === "string";
|
|
55
|
+
}
|
|
56
|
+
function base64UrlEncode(bytes) {
|
|
57
|
+
let binary = "";
|
|
58
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
59
|
+
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
60
|
+
}
|
|
61
|
+
function base64UrlDecode(value) {
|
|
62
|
+
const base64 = value.replaceAll("-", "+").replaceAll("_", "/");
|
|
63
|
+
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
|
|
64
|
+
return atob(padded);
|
|
65
|
+
}
|
|
66
|
+
function getCrypto() {
|
|
67
|
+
if (!globalThis.crypto?.getRandomValues || !globalThis.crypto.subtle) throw new Error("OpenAI Codex OAuth requires Web Crypto support");
|
|
68
|
+
return globalThis.crypto;
|
|
69
|
+
}
|
|
70
|
+
function createState() {
|
|
71
|
+
const bytes = new Uint8Array(16);
|
|
72
|
+
getCrypto().getRandomValues(bytes);
|
|
73
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
74
|
+
}
|
|
75
|
+
async function generatePKCE() {
|
|
76
|
+
const bytes = new Uint8Array(32);
|
|
77
|
+
getCrypto().getRandomValues(bytes);
|
|
78
|
+
const verifier = base64UrlEncode(bytes);
|
|
79
|
+
const data = new TextEncoder().encode(verifier);
|
|
80
|
+
const hash = await getCrypto().subtle.digest("SHA-256", data);
|
|
81
|
+
return {
|
|
82
|
+
verifier,
|
|
83
|
+
challenge: base64UrlEncode(new Uint8Array(hash))
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function parseOpenAICodexAuthorizationInput(input) {
|
|
87
|
+
const value = input.trim();
|
|
88
|
+
if (!value) return {};
|
|
89
|
+
try {
|
|
90
|
+
const url = new URL(value);
|
|
91
|
+
return {
|
|
92
|
+
code: url.searchParams.get("code") ?? void 0,
|
|
93
|
+
state: url.searchParams.get("state") ?? void 0
|
|
94
|
+
};
|
|
95
|
+
} catch {}
|
|
96
|
+
if (value.includes("#")) {
|
|
97
|
+
const [code, state] = value.split("#", 2);
|
|
98
|
+
return {
|
|
99
|
+
code,
|
|
100
|
+
state
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (value.includes("code=")) {
|
|
104
|
+
const params = new URLSearchParams(value);
|
|
105
|
+
return {
|
|
106
|
+
code: params.get("code") ?? void 0,
|
|
107
|
+
state: params.get("state") ?? void 0
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return { code: value };
|
|
111
|
+
}
|
|
112
|
+
function decodeJwt(token) {
|
|
113
|
+
try {
|
|
114
|
+
const parts = token.split(".");
|
|
115
|
+
if (parts.length !== 3) return null;
|
|
116
|
+
return JSON.parse(base64UrlDecode(parts[1] ?? ""));
|
|
117
|
+
} catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function getOpenAICodexAccountId(accessToken) {
|
|
122
|
+
const accountId = (decodeJwt(accessToken)?.[JWT_CLAIM_PATH])?.chatgpt_account_id;
|
|
123
|
+
return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
|
|
124
|
+
}
|
|
125
|
+
async function createOpenAICodexAuthorizationFlow(options = {}) {
|
|
126
|
+
const redirectUri = options.redirectUri ?? DEFAULT_REDIRECT_URI;
|
|
127
|
+
const { verifier, challenge } = await generatePKCE();
|
|
128
|
+
const state = createState();
|
|
129
|
+
const url = new URL(AUTHORIZE_URL);
|
|
130
|
+
url.searchParams.set("response_type", "code");
|
|
131
|
+
url.searchParams.set("client_id", CLIENT_ID);
|
|
132
|
+
url.searchParams.set("redirect_uri", redirectUri);
|
|
133
|
+
url.searchParams.set("scope", options.scope ?? DEFAULT_SCOPE);
|
|
134
|
+
url.searchParams.set("code_challenge", challenge);
|
|
135
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
136
|
+
url.searchParams.set("state", state);
|
|
137
|
+
url.searchParams.set("id_token_add_organizations", "true");
|
|
138
|
+
url.searchParams.set("codex_cli_simplified_flow", "true");
|
|
139
|
+
url.searchParams.set("originator", options.originator ?? DEFAULT_ORIGINATOR);
|
|
140
|
+
return {
|
|
141
|
+
verifier,
|
|
142
|
+
state,
|
|
143
|
+
redirectUri,
|
|
144
|
+
url: url.toString()
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
async function exchangeOpenAICodexAuthorizationCode(code, verifier, redirectUri = DEFAULT_REDIRECT_URI) {
|
|
148
|
+
const response = await fetch(TOKEN_URL, {
|
|
149
|
+
method: "POST",
|
|
150
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
151
|
+
body: new URLSearchParams({
|
|
152
|
+
grant_type: "authorization_code",
|
|
153
|
+
client_id: CLIENT_ID,
|
|
154
|
+
code,
|
|
155
|
+
code_verifier: verifier,
|
|
156
|
+
redirect_uri: redirectUri
|
|
157
|
+
})
|
|
158
|
+
});
|
|
159
|
+
if (!response.ok) {
|
|
160
|
+
const text = await response.text().catch(() => "");
|
|
161
|
+
return {
|
|
162
|
+
type: "failed",
|
|
163
|
+
status: response.status,
|
|
164
|
+
message: `OpenAI Codex token exchange failed (${response.status}): ${text || response.statusText}`
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const json = await response.json();
|
|
168
|
+
if (!json.access_token || !json.refresh_token || typeof json.expires_in !== "number") return {
|
|
169
|
+
type: "failed",
|
|
170
|
+
message: `OpenAI Codex token exchange response missing fields: ${JSON.stringify(json)}`
|
|
171
|
+
};
|
|
172
|
+
return {
|
|
173
|
+
type: "success",
|
|
174
|
+
access: json.access_token,
|
|
175
|
+
refresh: json.refresh_token,
|
|
176
|
+
expires: Date.now() + json.expires_in * 1e3
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async function refreshAccessToken(refreshToken) {
|
|
180
|
+
try {
|
|
181
|
+
const response = await fetch(TOKEN_URL, {
|
|
182
|
+
method: "POST",
|
|
183
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
184
|
+
body: new URLSearchParams({
|
|
185
|
+
grant_type: "refresh_token",
|
|
186
|
+
refresh_token: refreshToken,
|
|
187
|
+
client_id: CLIENT_ID
|
|
188
|
+
})
|
|
189
|
+
});
|
|
190
|
+
if (!response.ok) {
|
|
191
|
+
const text = await response.text().catch(() => "");
|
|
192
|
+
return {
|
|
193
|
+
type: "failed",
|
|
194
|
+
status: response.status,
|
|
195
|
+
message: `OpenAI Codex token refresh failed (${response.status}): ${text || response.statusText}`
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const json = await response.json();
|
|
199
|
+
if (!json.access_token || !json.refresh_token || typeof json.expires_in !== "number") return {
|
|
200
|
+
type: "failed",
|
|
201
|
+
message: `OpenAI Codex token refresh response missing fields: ${JSON.stringify(json)}`
|
|
202
|
+
};
|
|
203
|
+
return {
|
|
204
|
+
type: "success",
|
|
205
|
+
access: json.access_token,
|
|
206
|
+
refresh: json.refresh_token,
|
|
207
|
+
expires: Date.now() + json.expires_in * 1e3
|
|
208
|
+
};
|
|
209
|
+
} catch (error) {
|
|
210
|
+
return {
|
|
211
|
+
type: "failed",
|
|
212
|
+
message: `OpenAI Codex token refresh error: ${error instanceof Error ? error.message : String(error)}`
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function oauthPage(options) {
|
|
217
|
+
const escapeHtml = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
218
|
+
const details = options.details ? escapeHtml(options.details) : void 0;
|
|
219
|
+
return dedent`
|
|
220
|
+
<!doctype html>
|
|
221
|
+
<html lang="en">
|
|
222
|
+
<head>
|
|
223
|
+
<meta charset="utf-8" />
|
|
224
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
225
|
+
<title>${escapeHtml(options.title)}</title>
|
|
226
|
+
<style>
|
|
227
|
+
html { color-scheme: dark; }
|
|
228
|
+
body {
|
|
229
|
+
margin: 0;
|
|
230
|
+
min-height: 100vh;
|
|
231
|
+
display: grid;
|
|
232
|
+
place-items: center;
|
|
233
|
+
padding: 24px;
|
|
234
|
+
background: #09090b;
|
|
235
|
+
color: #fafafa;
|
|
236
|
+
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
237
|
+
text-align: center;
|
|
238
|
+
}
|
|
239
|
+
main { width: 100%; max-width: 560px; }
|
|
240
|
+
h1 { margin: 0 0 10px; font-size: 28px; line-height: 1.15; font-weight: 650; }
|
|
241
|
+
p { margin: 0; line-height: 1.7; color: #a1a1aa; font-size: 15px; }
|
|
242
|
+
.details { margin-top: 16px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; color: #a1a1aa; white-space: pre-wrap; word-break: break-word; }
|
|
243
|
+
</style>
|
|
244
|
+
</head>
|
|
245
|
+
<body>
|
|
246
|
+
<main>
|
|
247
|
+
<h1>${escapeHtml(options.heading)}</h1>
|
|
248
|
+
<p>${escapeHtml(options.message)}</p>
|
|
249
|
+
${details ? `<div class="details">${details}</div>` : ""}
|
|
250
|
+
</main>
|
|
251
|
+
</body>
|
|
252
|
+
</html>
|
|
253
|
+
`;
|
|
254
|
+
}
|
|
255
|
+
function oauthSuccessHtml(message) {
|
|
256
|
+
return oauthPage({
|
|
257
|
+
title: "Authentication successful",
|
|
258
|
+
heading: "Authentication successful",
|
|
259
|
+
message
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
function oauthErrorHtml(message, details) {
|
|
263
|
+
return oauthPage({
|
|
264
|
+
title: "Authentication failed",
|
|
265
|
+
heading: "Authentication failed",
|
|
266
|
+
message,
|
|
267
|
+
details
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
async function startLocalOAuthServer(state) {
|
|
271
|
+
const http = await import("node:http").catch(() => void 0);
|
|
272
|
+
if (!http) throw new Error("OpenAI Codex OAuth callback server is only available in Node.js environments");
|
|
273
|
+
let settleWait;
|
|
274
|
+
const waitForCodePromise = new Promise((resolve) => {
|
|
275
|
+
let settled = false;
|
|
276
|
+
settleWait = (value) => {
|
|
277
|
+
if (settled) return;
|
|
278
|
+
settled = true;
|
|
279
|
+
resolve(value);
|
|
280
|
+
};
|
|
281
|
+
});
|
|
282
|
+
const server = http.createServer((req, res) => {
|
|
283
|
+
try {
|
|
284
|
+
const url = new URL(req.url || "", "http://localhost");
|
|
285
|
+
if (url.pathname !== "/auth/callback") {
|
|
286
|
+
res.statusCode = 404;
|
|
287
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
288
|
+
res.end(oauthErrorHtml("Callback route not found."));
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (url.searchParams.get("state") !== state) {
|
|
292
|
+
res.statusCode = 400;
|
|
293
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
294
|
+
res.end(oauthErrorHtml("State mismatch."));
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const code = url.searchParams.get("code");
|
|
298
|
+
if (!code) {
|
|
299
|
+
res.statusCode = 400;
|
|
300
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
301
|
+
res.end(oauthErrorHtml("Missing authorization code."));
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
res.statusCode = 200;
|
|
305
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
306
|
+
res.end(oauthSuccessHtml("OpenAI authentication completed. You can close this window."));
|
|
307
|
+
settleWait?.({ code });
|
|
308
|
+
} catch (error) {
|
|
309
|
+
res.statusCode = 500;
|
|
310
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
311
|
+
res.end(oauthErrorHtml("Internal error while processing OAuth callback.", error instanceof Error ? error.message : String(error)));
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
return new Promise((resolve) => {
|
|
315
|
+
server.listen(1455, readEnv(CODEWORK_OAUTH_CALLBACK_HOST) || "127.0.0.1", () => {
|
|
316
|
+
resolve({
|
|
317
|
+
close: () => server.close(),
|
|
318
|
+
cancelWait: () => {
|
|
319
|
+
settleWait?.(null);
|
|
320
|
+
},
|
|
321
|
+
waitForCode: () => waitForCodePromise
|
|
322
|
+
});
|
|
323
|
+
}).on("error", () => {
|
|
324
|
+
settleWait?.(null);
|
|
325
|
+
resolve({
|
|
326
|
+
close: () => {
|
|
327
|
+
try {
|
|
328
|
+
server.close();
|
|
329
|
+
} catch {}
|
|
330
|
+
},
|
|
331
|
+
cancelWait: () => {},
|
|
332
|
+
waitForCode: async () => null
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
async function loginOpenAICodex(options) {
|
|
338
|
+
const flow = await createOpenAICodexAuthorizationFlow(options);
|
|
339
|
+
const server = await startLocalOAuthServer(flow.state);
|
|
340
|
+
options.onAuth({
|
|
341
|
+
url: flow.url,
|
|
342
|
+
instructions: "Complete the browser login to finish OpenAI Codex authentication."
|
|
343
|
+
});
|
|
344
|
+
let code;
|
|
345
|
+
try {
|
|
346
|
+
if (options.onManualCodeInput) {
|
|
347
|
+
let manualCode;
|
|
348
|
+
let manualError;
|
|
349
|
+
const manualPromise = options.onManualCodeInput().then((input) => {
|
|
350
|
+
manualCode = input;
|
|
351
|
+
if (input.trim()) server.cancelWait();
|
|
352
|
+
}).catch((error) => {
|
|
353
|
+
manualError = error instanceof Error ? error : new Error(String(error));
|
|
354
|
+
server.cancelWait();
|
|
355
|
+
});
|
|
356
|
+
const result = await server.waitForCode();
|
|
357
|
+
if (manualError) throw manualError;
|
|
358
|
+
if (result?.code) code = result.code;
|
|
359
|
+
else if (manualCode) {
|
|
360
|
+
const parsed = parseOpenAICodexAuthorizationInput(manualCode);
|
|
361
|
+
if (parsed.state && parsed.state !== flow.state) throw new Error("State mismatch");
|
|
362
|
+
code = parsed.code;
|
|
363
|
+
}
|
|
364
|
+
if (!code) {
|
|
365
|
+
await manualPromise;
|
|
366
|
+
if (manualError) throw manualError;
|
|
367
|
+
if (manualCode) {
|
|
368
|
+
const parsed = parseOpenAICodexAuthorizationInput(manualCode);
|
|
369
|
+
if (parsed.state && parsed.state !== flow.state) throw new Error("State mismatch");
|
|
370
|
+
code = parsed.code;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
} else {
|
|
374
|
+
const result = await server.waitForCode();
|
|
375
|
+
if (result?.code) code = result.code;
|
|
376
|
+
}
|
|
377
|
+
if (!code) {
|
|
378
|
+
const parsed = parseOpenAICodexAuthorizationInput(await options.onPrompt({ message: "Paste the authorization code or full redirect URL:" }));
|
|
379
|
+
if (parsed.state && parsed.state !== flow.state) throw new Error("State mismatch");
|
|
380
|
+
code = parsed.code;
|
|
381
|
+
}
|
|
382
|
+
if (!code) throw new Error("Missing authorization code");
|
|
383
|
+
const tokenResult = await exchangeOpenAICodexAuthorizationCode(code, flow.verifier, flow.redirectUri);
|
|
384
|
+
if (tokenResult.type !== "success") throw new Error(tokenResult.message);
|
|
385
|
+
const accountId = getOpenAICodexAccountId(tokenResult.access);
|
|
386
|
+
if (!accountId) throw new Error("Failed to extract accountId from token");
|
|
387
|
+
return {
|
|
388
|
+
access: tokenResult.access,
|
|
389
|
+
refresh: tokenResult.refresh,
|
|
390
|
+
expires: tokenResult.expires,
|
|
391
|
+
accountId
|
|
392
|
+
};
|
|
393
|
+
} finally {
|
|
394
|
+
server.close();
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
async function refreshOpenAICodexToken(refreshToken) {
|
|
398
|
+
const result = await refreshAccessToken(refreshToken);
|
|
399
|
+
if (result.type !== "success") throw new Error(result.message);
|
|
400
|
+
const accountId = getOpenAICodexAccountId(result.access);
|
|
401
|
+
if (!accountId) throw new Error("Failed to extract accountId from token");
|
|
402
|
+
return {
|
|
403
|
+
access: result.access,
|
|
404
|
+
refresh: result.refresh,
|
|
405
|
+
expires: result.expires,
|
|
406
|
+
accountId
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
function openAICodexHeaders(credentials) {
|
|
410
|
+
return {
|
|
411
|
+
Authorization: `Bearer ${credentials.access}`,
|
|
412
|
+
"chatgpt-account-id": credentials.accountId,
|
|
413
|
+
"OpenAI-Beta": "responses=experimental"
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
var JsonOpenAICodexAuthStorage = class {
|
|
417
|
+
path;
|
|
418
|
+
providerId;
|
|
419
|
+
constructor(options = {}) {
|
|
420
|
+
this.path = options.path ? expandHome(options.path) : defaultOpenAICodexAuthFilePath();
|
|
421
|
+
this.providerId = options.providerId ?? DEFAULT_PROVIDER_ID;
|
|
422
|
+
}
|
|
423
|
+
async get() {
|
|
424
|
+
const file = await this.readFile();
|
|
425
|
+
if (!file) return void 0;
|
|
426
|
+
if (isOpenAICodexCredentials(file)) return file;
|
|
427
|
+
const direct = file[this.providerId];
|
|
428
|
+
if (isOpenAICodexCredentials(direct)) return direct;
|
|
429
|
+
const providers = file.providers;
|
|
430
|
+
if (isObject(providers) && isOpenAICodexCredentials(providers[this.providerId])) return providers[this.providerId];
|
|
431
|
+
}
|
|
432
|
+
async set(credentials) {
|
|
433
|
+
const current = await this.readFile();
|
|
434
|
+
const next = isObject(current) && !isOpenAICodexCredentials(current) ? current : {};
|
|
435
|
+
next[this.providerId] = credentials;
|
|
436
|
+
await this.writeFile(next);
|
|
437
|
+
}
|
|
438
|
+
async clear() {
|
|
439
|
+
const current = await this.readFile();
|
|
440
|
+
if (!current) return;
|
|
441
|
+
if (isOpenAICodexCredentials(current)) {
|
|
442
|
+
await this.writeFile({});
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
delete current[this.providerId];
|
|
446
|
+
const providers = current.providers;
|
|
447
|
+
if (isObject(providers)) delete providers[this.providerId];
|
|
448
|
+
await this.writeFile(current);
|
|
449
|
+
}
|
|
450
|
+
async readFile() {
|
|
451
|
+
const fs = await import("node:fs/promises");
|
|
452
|
+
try {
|
|
453
|
+
const text = await fs.readFile(this.path, "utf8");
|
|
454
|
+
const parsed = JSON.parse(text);
|
|
455
|
+
return isObject(parsed) ? parsed : void 0;
|
|
456
|
+
} catch (error) {
|
|
457
|
+
if (isObject(error) && error.code === "ENOENT") return void 0;
|
|
458
|
+
throw error;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
async writeFile(value) {
|
|
462
|
+
const fs = await import("node:fs/promises");
|
|
463
|
+
const path = await import("node:path");
|
|
464
|
+
await fs.mkdir(path.dirname(this.path), { recursive: true });
|
|
465
|
+
const tempPath = `${this.path}.${process.pid}.${Date.now()}.tmp`;
|
|
466
|
+
await fs.writeFile(tempPath, `${JSON.stringify(value, null, " ")}\n`, "utf8");
|
|
467
|
+
await fs.rename(tempPath, this.path);
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
var OpenAICodexOAuthClient = class {
|
|
471
|
+
storage;
|
|
472
|
+
refreshSkewMs;
|
|
473
|
+
constructor(options = {}) {
|
|
474
|
+
this.storage = options.storage ?? new JsonOpenAICodexAuthStorage();
|
|
475
|
+
this.refreshSkewMs = options.refreshSkewMs ?? REFRESH_SKEW_MS;
|
|
476
|
+
}
|
|
477
|
+
async login(options) {
|
|
478
|
+
const credentials = await loginOpenAICodex(options);
|
|
479
|
+
await this.storage.set(credentials);
|
|
480
|
+
return credentials;
|
|
481
|
+
}
|
|
482
|
+
async getCredentials() {
|
|
483
|
+
const credentials = await this.storage.get();
|
|
484
|
+
if (!credentials) return void 0;
|
|
485
|
+
if (credentials.expires > Date.now() + this.refreshSkewMs) return credentials;
|
|
486
|
+
const refreshed = await refreshOpenAICodexToken(credentials.refresh);
|
|
487
|
+
await this.storage.set(refreshed);
|
|
488
|
+
return refreshed;
|
|
489
|
+
}
|
|
490
|
+
async getApiKey() {
|
|
491
|
+
return (await this.getCredentials())?.access;
|
|
492
|
+
}
|
|
493
|
+
async getHeaders() {
|
|
494
|
+
const credentials = await this.getCredentials();
|
|
495
|
+
return credentials ? openAICodexHeaders(credentials) : void 0;
|
|
496
|
+
}
|
|
497
|
+
async logout() {
|
|
498
|
+
await this.storage.clear();
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
const openAICodexOAuthProvider = {
|
|
502
|
+
id: DEFAULT_PROVIDER_ID,
|
|
503
|
+
name: "ChatGPT Plus/Pro (OpenAI Codex)",
|
|
504
|
+
usesCallbackServer: true,
|
|
505
|
+
async login(options) {
|
|
506
|
+
return loginOpenAICodex(options);
|
|
507
|
+
},
|
|
508
|
+
async refreshToken(credentials) {
|
|
509
|
+
return refreshOpenAICodexToken(credentials.refresh);
|
|
510
|
+
},
|
|
511
|
+
getApiKey(credentials) {
|
|
512
|
+
return credentials.access;
|
|
513
|
+
},
|
|
514
|
+
getHeaders(credentials) {
|
|
515
|
+
return openAICodexHeaders(credentials);
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
//#endregion
|
|
519
|
+
export { exchangeOpenAICodexAuthorizationCode as a, openAICodexHeaders as c, refreshOpenAICodexToken as d, defaultOpenAICodexAuthFilePath as i, openAICodexOAuthProvider as l, OpenAICodexOAuthClient as n, getOpenAICodexAccountId as o, createOpenAICodexAuthorizationFlow as r, loginOpenAICodex as s, JsonOpenAICodexAuthStorage as t, parseOpenAICodexAuthorizationInput as u };
|
|
520
|
+
|
|
521
|
+
//# sourceMappingURL=codex-DfYk740t.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-DfYk740t.mjs","names":[],"sources":["../../src/oauth/openai/codex.ts"],"sourcesContent":["/**\n * OpenAI Codex OAuth helpers.\n *\n * This flow is intended for Node.js CLI/server environments. The low-level\n * authorization helpers are storage-free so they can also be used from an\n * existing HTTP server callback route.\n */\n\nimport dedent from \"dedent\";\n\nconst CODEWORK_OAUTH_CALLBACK_HOST = \"CODEWORK_OAUTH_CALLBACK_HOST\";\nconst CODEWORK_CREDENTIALS = \"CODEWORK_CREDENTIALS\";\nconst CODEWORK_HOME_DIR = \"CODEWORK_HOME_DIR\";\n\nconst CLIENT_ID = \"app_EMoamEEZ73f0CkXaXp7hrann\";\nconst AUTHORIZE_URL = \"https://auth.openai.com/oauth/authorize\";\nconst TOKEN_URL = \"https://auth.openai.com/oauth/token\";\nconst DEFAULT_REDIRECT_URI = \"http://localhost:1455/auth/callback\";\nconst DEFAULT_SCOPE = \"openid profile email offline_access\";\nconst DEFAULT_ORIGINATOR = \"codework\";\nconst DEFAULT_PROVIDER_ID = \"openai-codex\";\nconst JWT_CLAIM_PATH = \"https://api.openai.com/auth\";\nconst REFRESH_SKEW_MS = 60 * 1000;\n\nexport type OpenAICodexOAuthCredentials = {\n\taccess: string;\n\trefresh: string;\n\texpires: number;\n\taccountId: string;\n};\n\nexport type OpenAICodexTokenSuccess = {\n\ttype: \"success\";\n\taccess: string;\n\trefresh: string;\n\texpires: number;\n};\n\nexport type OpenAICodexTokenFailure = {\n\ttype: \"failed\";\n\tmessage: string;\n\tstatus?: number;\n};\n\nexport type OpenAICodexTokenResult = OpenAICodexTokenSuccess | OpenAICodexTokenFailure;\n\nexport type OpenAICodexAuthorizationFlow = {\n\turl: string;\n\tverifier: string;\n\tstate: string;\n\tredirectUri: string;\n};\n\nexport type OpenAICodexAuthorizationOptions = {\n\toriginator?: string;\n\tredirectUri?: string;\n\tscope?: string;\n};\n\nexport type OpenAICodexPrompt = {\n\tmessage: string;\n\tplaceholder?: string;\n\tallowEmpty?: boolean;\n};\n\nexport type OpenAICodexLoginOptions = OpenAICodexAuthorizationOptions & {\n\tonAuth: (info: { url: string; instructions?: string }) => void;\n\tonPrompt: (prompt: OpenAICodexPrompt) => Promise<string>;\n\tonProgress?: (message: string) => void;\n\tonManualCodeInput?: () => Promise<string>;\n};\n\nexport interface OpenAICodexAuthStorage {\n\tget(): Promise<OpenAICodexOAuthCredentials | undefined>;\n\tset(credentials: OpenAICodexOAuthCredentials): Promise<void>;\n\tclear(): Promise<void>;\n}\n\nexport type JsonOpenAICodexAuthStorageOptions = {\n\tpath?: string;\n\tproviderId?: string;\n};\n\ntype JwtPayload = {\n\t[JWT_CLAIM_PATH]?: {\n\t\tchatgpt_account_id?: string;\n\t};\n\t[key: string]: unknown;\n};\n\ntype OAuthServerInfo = {\n\tclose: () => void;\n\tcancelWait: () => void;\n\twaitForCode: () => Promise<{ code: string } | null>;\n};\n\ntype AuthFile = Record<string, unknown>;\n\nfunction readEnv(name: string): string | undefined {\n\tif (typeof process === \"undefined\") return undefined;\n\treturn process.env[name];\n}\n\nfunction joinPath(...parts: string[]): string {\n\treturn parts.filter(Boolean).join(\"/\").replaceAll(/\\/+/g, \"/\");\n}\n\nfunction expandHome(path: string): string {\n\tif (path === \"~\") return homeDirectory();\n\tif (path.startsWith(\"~/\")) return joinPath(homeDirectory(), path.slice(2));\n\treturn path;\n}\n\nfunction homeDirectory(): string {\n\tconst home = readEnv(\"HOME\") ?? readEnv(\"USERPROFILE\");\n\tif (!home) {\n\t\tthrow new Error(\"Unable to resolve home directory for OpenAI Codex auth storage\");\n\t}\n\treturn home;\n}\n\nfunction codeworkHomeDirectory(): string {\n\tconst override = readEnv(CODEWORK_HOME_DIR);\n\tif (override) return expandHome(override);\n\treturn joinPath(homeDirectory(), \".codework\");\n}\n\nexport function defaultOpenAICodexAuthFilePath(): string {\n\tconst authFile = readEnv(CODEWORK_CREDENTIALS);\n\tif (authFile) return expandHome(authFile);\n\n\treturn joinPath(codeworkHomeDirectory(), \"aikit\", \"auth.json\");\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isOpenAICodexCredentials(value: unknown): value is OpenAICodexOAuthCredentials {\n\tif (!isObject(value)) return false;\n\treturn (\n\t\ttypeof value.access === \"string\" &&\n\t\ttypeof value.refresh === \"string\" &&\n\t\ttypeof value.expires === \"number\" &&\n\t\ttypeof value.accountId === \"string\"\n\t);\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n\tlet binary = \"\";\n\tfor (const byte of bytes) {\n\t\tbinary += String.fromCharCode(byte);\n\t}\n\treturn btoa(binary).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replaceAll(\"=\", \"\");\n}\n\nfunction base64UrlDecode(value: string): string {\n\tconst base64 = value.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\");\n\tconst padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, \"=\");\n\treturn atob(padded);\n}\n\ntype WebCrypto = typeof globalThis.crypto;\n\nfunction getCrypto(): WebCrypto {\n\tif (!globalThis.crypto?.getRandomValues || !globalThis.crypto.subtle) {\n\t\tthrow new Error(\"OpenAI Codex OAuth requires Web Crypto support\");\n\t}\n\treturn globalThis.crypto;\n}\n\nfunction createState(): string {\n\tconst bytes = new Uint8Array(16);\n\tgetCrypto().getRandomValues(bytes);\n\treturn Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nasync function generatePKCE(): Promise<{\n\tverifier: string;\n\tchallenge: string;\n}> {\n\tconst bytes = new Uint8Array(32);\n\tgetCrypto().getRandomValues(bytes);\n\tconst verifier = base64UrlEncode(bytes);\n\n\tconst data = new TextEncoder().encode(verifier);\n\tconst hash = await getCrypto().subtle.digest(\"SHA-256\", data);\n\tconst challenge = base64UrlEncode(new Uint8Array(hash));\n\n\treturn { verifier, challenge };\n}\n\nexport function parseOpenAICodexAuthorizationInput(input: string): {\n\tcode?: string;\n\tstate?: string;\n} {\n\tconst value = input.trim();\n\tif (!value) return {};\n\n\ttry {\n\t\tconst url = new URL(value);\n\t\treturn {\n\t\t\tcode: url.searchParams.get(\"code\") ?? undefined,\n\t\t\tstate: url.searchParams.get(\"state\") ?? undefined,\n\t\t};\n\t} catch {\n\t\t// Not a URL.\n\t}\n\n\tif (value.includes(\"#\")) {\n\t\tconst [code, state] = value.split(\"#\", 2);\n\t\treturn { code, state };\n\t}\n\n\tif (value.includes(\"code=\")) {\n\t\tconst params = new URLSearchParams(value);\n\t\treturn {\n\t\t\tcode: params.get(\"code\") ?? undefined,\n\t\t\tstate: params.get(\"state\") ?? undefined,\n\t\t};\n\t}\n\n\treturn { code: value };\n}\n\nfunction decodeJwt(token: string): JwtPayload | null {\n\ttry {\n\t\tconst parts = token.split(\".\");\n\t\tif (parts.length !== 3) return null;\n\t\treturn JSON.parse(base64UrlDecode(parts[1] ?? \"\")) as JwtPayload;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function getOpenAICodexAccountId(accessToken: string): string | null {\n\tconst payload = decodeJwt(accessToken);\n\tconst auth = payload?.[JWT_CLAIM_PATH];\n\tconst accountId = auth?.chatgpt_account_id;\n\treturn typeof accountId === \"string\" && accountId.length > 0 ? accountId : null;\n}\n\nexport async function createOpenAICodexAuthorizationFlow(\n\toptions: OpenAICodexAuthorizationOptions = {},\n): Promise<OpenAICodexAuthorizationFlow> {\n\tconst redirectUri = options.redirectUri ?? DEFAULT_REDIRECT_URI;\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst state = createState();\n\n\tconst url = new URL(AUTHORIZE_URL);\n\turl.searchParams.set(\"response_type\", \"code\");\n\turl.searchParams.set(\"client_id\", CLIENT_ID);\n\turl.searchParams.set(\"redirect_uri\", redirectUri);\n\turl.searchParams.set(\"scope\", options.scope ?? DEFAULT_SCOPE);\n\turl.searchParams.set(\"code_challenge\", challenge);\n\turl.searchParams.set(\"code_challenge_method\", \"S256\");\n\turl.searchParams.set(\"state\", state);\n\turl.searchParams.set(\"id_token_add_organizations\", \"true\");\n\turl.searchParams.set(\"codex_cli_simplified_flow\", \"true\");\n\turl.searchParams.set(\"originator\", options.originator ?? DEFAULT_ORIGINATOR);\n\n\treturn { verifier, state, redirectUri, url: url.toString() };\n}\n\nexport async function exchangeOpenAICodexAuthorizationCode(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string = DEFAULT_REDIRECT_URI,\n): Promise<OpenAICodexTokenResult> {\n\tconst response = await fetch(TOKEN_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: \"authorization_code\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tcode,\n\t\t\tcode_verifier: verifier,\n\t\t\tredirect_uri: redirectUri,\n\t\t}),\n\t});\n\n\tif (!response.ok) {\n\t\tconst text = await response.text().catch(() => \"\");\n\t\treturn {\n\t\t\ttype: \"failed\",\n\t\t\tstatus: response.status,\n\t\t\tmessage: `OpenAI Codex token exchange failed (${response.status}): ${text || response.statusText}`,\n\t\t};\n\t}\n\n\tconst json = (await response.json()) as {\n\t\taccess_token?: string;\n\t\trefresh_token?: string;\n\t\texpires_in?: number;\n\t};\n\n\tif (!json.access_token || !json.refresh_token || typeof json.expires_in !== \"number\") {\n\t\treturn {\n\t\t\ttype: \"failed\",\n\t\t\tmessage: `OpenAI Codex token exchange response missing fields: ${JSON.stringify(json)}`,\n\t\t};\n\t}\n\n\treturn {\n\t\ttype: \"success\",\n\t\taccess: json.access_token,\n\t\trefresh: json.refresh_token,\n\t\texpires: Date.now() + json.expires_in * 1000,\n\t};\n}\n\nasync function refreshAccessToken(refreshToken: string): Promise<OpenAICodexTokenResult> {\n\ttry {\n\t\tconst response = await fetch(TOKEN_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n\t\t\tbody: new URLSearchParams({\n\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\trefresh_token: refreshToken,\n\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t}),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst text = await response.text().catch(() => \"\");\n\t\t\treturn {\n\t\t\t\ttype: \"failed\",\n\t\t\t\tstatus: response.status,\n\t\t\t\tmessage: `OpenAI Codex token refresh failed (${response.status}): ${text || response.statusText}`,\n\t\t\t};\n\t\t}\n\n\t\tconst json = (await response.json()) as {\n\t\t\taccess_token?: string;\n\t\t\trefresh_token?: string;\n\t\t\texpires_in?: number;\n\t\t};\n\n\t\tif (!json.access_token || !json.refresh_token || typeof json.expires_in !== \"number\") {\n\t\t\treturn {\n\t\t\t\ttype: \"failed\",\n\t\t\t\tmessage: `OpenAI Codex token refresh response missing fields: ${JSON.stringify(json)}`,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\ttype: \"success\",\n\t\t\taccess: json.access_token,\n\t\t\trefresh: json.refresh_token,\n\t\t\texpires: Date.now() + json.expires_in * 1000,\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\ttype: \"failed\",\n\t\t\tmessage: `OpenAI Codex token refresh error: ${error instanceof Error ? error.message : String(error)}`,\n\t\t};\n\t}\n}\n\nfunction oauthPage(options: { title: string; heading: string; message: string; details?: string }): string {\n\tconst escapeHtml = (value: string) =>\n\t\tvalue\n\t\t\t.replaceAll(\"&\", \"&\")\n\t\t\t.replaceAll(\"<\", \"<\")\n\t\t\t.replaceAll(\">\", \">\")\n\t\t\t.replaceAll('\"', \""\")\n\t\t\t.replaceAll(\"'\", \"'\");\n\tconst details = options.details ? escapeHtml(options.details) : undefined;\n\n\treturn dedent`\n\t<!doctype html>\n\t<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t<title>${escapeHtml(options.title)}</title>\n\t\t<style>\n\t\t\thtml { color-scheme: dark; }\n\t\t\tbody {\n\t\t\tmargin: 0;\n\t\t\tmin-height: 100vh;\n\t\t\tdisplay: grid;\n\t\t\tplace-items: center;\n\t\t\tpadding: 24px;\n\t\t\tbackground: #09090b;\n\t\t\tcolor: #fafafa;\n\t\t\tfont-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n\t\t\ttext-align: center;\n\t\t\t}\n\t\t\tmain { width: 100%; max-width: 560px; }\n\t\t\th1 { margin: 0 0 10px; font-size: 28px; line-height: 1.15; font-weight: 650; }\n\t\t\tp { margin: 0; line-height: 1.7; color: #a1a1aa; font-size: 15px; }\n\t\t\t.details { margin-top: 16px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; color: #a1a1aa; white-space: pre-wrap; word-break: break-word; }\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<main>\n\t\t\t<h1>${escapeHtml(options.heading)}</h1>\n\t\t\t<p>${escapeHtml(options.message)}</p>\n\t\t\t${details ? `<div class=\"details\">${details}</div>` : \"\"}\n\t\t</main>\n\t</body>\n\t</html>\n\t`;\n}\n\nfunction oauthSuccessHtml(message: string): string {\n\treturn oauthPage({\n\t\ttitle: \"Authentication successful\",\n\t\theading: \"Authentication successful\",\n\t\tmessage,\n\t});\n}\n\nfunction oauthErrorHtml(message: string, details?: string): string {\n\treturn oauthPage({\n\t\ttitle: \"Authentication failed\",\n\t\theading: \"Authentication failed\",\n\t\tmessage,\n\t\tdetails,\n\t});\n}\n\nasync function startLocalOAuthServer(state: string): Promise<OAuthServerInfo> {\n\tconst http = await import(\"node:http\").catch(() => undefined);\n\tif (!http) {\n\t\tthrow new Error(\"OpenAI Codex OAuth callback server is only available in Node.js environments\");\n\t}\n\n\tlet settleWait: ((value: { code: string } | null) => void) | undefined;\n\tconst waitForCodePromise = new Promise<{ code: string } | null>((resolve) => {\n\t\tlet settled = false;\n\t\tsettleWait = (value) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tresolve(value);\n\t\t};\n\t});\n\n\tconst server = http.createServer((req, res) => {\n\t\ttry {\n\t\t\tconst url = new URL(req.url || \"\", \"http://localhost\");\n\t\t\tif (url.pathname !== \"/auth/callback\") {\n\t\t\t\tres.statusCode = 404;\n\t\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\t\tres.end(oauthErrorHtml(\"Callback route not found.\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (url.searchParams.get(\"state\") !== state) {\n\t\t\t\tres.statusCode = 400;\n\t\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\t\tres.end(oauthErrorHtml(\"State mismatch.\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst code = url.searchParams.get(\"code\");\n\t\t\tif (!code) {\n\t\t\t\tres.statusCode = 400;\n\t\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\t\tres.end(oauthErrorHtml(\"Missing authorization code.\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tres.statusCode = 200;\n\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\tres.end(oauthSuccessHtml(\"OpenAI authentication completed. You can close this window.\"));\n\t\t\tsettleWait?.({ code });\n\t\t} catch (error) {\n\t\t\tres.statusCode = 500;\n\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\tres.end(\n\t\t\t\toauthErrorHtml(\n\t\t\t\t\t\"Internal error while processing OAuth callback.\",\n\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t});\n\n\treturn new Promise((resolve) => {\n\t\tserver\n\t\t\t.listen(1455, readEnv(CODEWORK_OAUTH_CALLBACK_HOST) || \"127.0.0.1\", () => {\n\t\t\t\tresolve({\n\t\t\t\t\tclose: () => server.close(),\n\t\t\t\t\tcancelWait: () => {\n\t\t\t\t\t\tsettleWait?.(null);\n\t\t\t\t\t},\n\t\t\t\t\twaitForCode: () => waitForCodePromise,\n\t\t\t\t});\n\t\t\t})\n\t\t\t.on(\"error\", () => {\n\t\t\t\tsettleWait?.(null);\n\t\t\t\tresolve({\n\t\t\t\t\tclose: () => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tserver.close();\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Ignore close errors after a listen failure.\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tcancelWait: () => {},\n\t\t\t\t\twaitForCode: async () => null,\n\t\t\t\t});\n\t\t\t});\n\t});\n}\n\nexport async function loginOpenAICodex(options: OpenAICodexLoginOptions): Promise<OpenAICodexOAuthCredentials> {\n\tconst flow = await createOpenAICodexAuthorizationFlow(options);\n\tconst server = await startLocalOAuthServer(flow.state);\n\n\toptions.onAuth({\n\t\turl: flow.url,\n\t\tinstructions: \"Complete the browser login to finish OpenAI Codex authentication.\",\n\t});\n\n\tlet code: string | undefined;\n\ttry {\n\t\tif (options.onManualCodeInput) {\n\t\t\tlet manualCode: string | undefined;\n\t\t\tlet manualError: Error | undefined;\n\t\t\tconst manualPromise = options\n\t\t\t\t.onManualCodeInput()\n\t\t\t\t.then((input) => {\n\t\t\t\t\tmanualCode = input;\n\t\t\t\t\tif (input.trim()) server.cancelWait();\n\t\t\t\t})\n\t\t\t\t.catch((error) => {\n\t\t\t\t\tmanualError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\t\tserver.cancelWait();\n\t\t\t\t});\n\n\t\t\tconst result = await server.waitForCode();\n\t\t\tif (manualError) throw manualError;\n\n\t\t\tif (result?.code) {\n\t\t\t\tcode = result.code;\n\t\t\t} else if (manualCode) {\n\t\t\t\tconst parsed = parseOpenAICodexAuthorizationInput(manualCode);\n\t\t\t\tif (parsed.state && parsed.state !== flow.state) throw new Error(\"State mismatch\");\n\t\t\t\tcode = parsed.code;\n\t\t\t}\n\n\t\t\tif (!code) {\n\t\t\t\tawait manualPromise;\n\t\t\t\tif (manualError) throw manualError;\n\t\t\t\tif (manualCode) {\n\t\t\t\t\tconst parsed = parseOpenAICodexAuthorizationInput(manualCode);\n\t\t\t\t\tif (parsed.state && parsed.state !== flow.state) throw new Error(\"State mismatch\");\n\t\t\t\t\tcode = parsed.code;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst result = await server.waitForCode();\n\t\t\tif (result?.code) code = result.code;\n\t\t}\n\n\t\tif (!code) {\n\t\t\tconst input = await options.onPrompt({\n\t\t\t\tmessage: \"Paste the authorization code or full redirect URL:\",\n\t\t\t});\n\t\t\tconst parsed = parseOpenAICodexAuthorizationInput(input);\n\t\t\tif (parsed.state && parsed.state !== flow.state) throw new Error(\"State mismatch\");\n\t\t\tcode = parsed.code;\n\t\t}\n\n\t\tif (!code) throw new Error(\"Missing authorization code\");\n\n\t\tconst tokenResult = await exchangeOpenAICodexAuthorizationCode(code, flow.verifier, flow.redirectUri);\n\t\tif (tokenResult.type !== \"success\") throw new Error(tokenResult.message);\n\n\t\tconst accountId = getOpenAICodexAccountId(tokenResult.access);\n\t\tif (!accountId) throw new Error(\"Failed to extract accountId from token\");\n\n\t\treturn {\n\t\t\taccess: tokenResult.access,\n\t\t\trefresh: tokenResult.refresh,\n\t\t\texpires: tokenResult.expires,\n\t\t\taccountId,\n\t\t};\n\t} finally {\n\t\tserver.close();\n\t}\n}\n\nexport async function refreshOpenAICodexToken(refreshToken: string): Promise<OpenAICodexOAuthCredentials> {\n\tconst result = await refreshAccessToken(refreshToken);\n\tif (result.type !== \"success\") {\n\t\tthrow new Error(result.message);\n\t}\n\n\tconst accountId = getOpenAICodexAccountId(result.access);\n\tif (!accountId) {\n\t\tthrow new Error(\"Failed to extract accountId from token\");\n\t}\n\n\treturn {\n\t\taccess: result.access,\n\t\trefresh: result.refresh,\n\t\texpires: result.expires,\n\t\taccountId,\n\t};\n}\n\nexport function openAICodexHeaders(credentials: OpenAICodexOAuthCredentials): Record<string, string> {\n\treturn {\n\t\tAuthorization: `Bearer ${credentials.access}`,\n\t\t\"chatgpt-account-id\": credentials.accountId,\n\t\t\"OpenAI-Beta\": \"responses=experimental\",\n\t};\n}\n\nexport class JsonOpenAICodexAuthStorage implements OpenAICodexAuthStorage {\n\treadonly path: string;\n\treadonly providerId: string;\n\n\tconstructor(options: JsonOpenAICodexAuthStorageOptions = {}) {\n\t\tthis.path = options.path ? expandHome(options.path) : defaultOpenAICodexAuthFilePath();\n\t\tthis.providerId = options.providerId ?? DEFAULT_PROVIDER_ID;\n\t}\n\n\tasync get(): Promise<OpenAICodexOAuthCredentials | undefined> {\n\t\tconst file = await this.readFile();\n\t\tif (!file) return undefined;\n\n\t\tif (isOpenAICodexCredentials(file)) return file;\n\n\t\tconst direct = file[this.providerId];\n\t\tif (isOpenAICodexCredentials(direct)) return direct;\n\n\t\tconst providers = file.providers;\n\t\tif (isObject(providers) && isOpenAICodexCredentials(providers[this.providerId])) {\n\t\t\treturn providers[this.providerId] as OpenAICodexOAuthCredentials;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tasync set(credentials: OpenAICodexOAuthCredentials): Promise<void> {\n\t\tconst current = await this.readFile();\n\t\tconst next = isObject(current) && !isOpenAICodexCredentials(current) ? current : {};\n\t\tnext[this.providerId] = credentials;\n\t\tawait this.writeFile(next);\n\t}\n\n\tasync clear(): Promise<void> {\n\t\tconst current = await this.readFile();\n\t\tif (!current) return;\n\n\t\tif (isOpenAICodexCredentials(current)) {\n\t\t\tawait this.writeFile({});\n\t\t\treturn;\n\t\t}\n\n\t\tdelete current[this.providerId];\n\t\tconst providers = current.providers;\n\t\tif (isObject(providers)) delete providers[this.providerId];\n\t\tawait this.writeFile(current);\n\t}\n\n\tprivate async readFile(): Promise<AuthFile | undefined> {\n\t\tconst fs = await import(\"node:fs/promises\");\n\t\ttry {\n\t\t\tconst text = await fs.readFile(this.path, \"utf8\");\n\t\t\tconst parsed = JSON.parse(text) as unknown;\n\t\t\treturn isObject(parsed) ? parsed : undefined;\n\t\t} catch (error) {\n\t\t\tif (isObject(error) && error.code === \"ENOENT\") return undefined;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate async writeFile(value: AuthFile): Promise<void> {\n\t\tconst fs = await import(\"node:fs/promises\");\n\t\tconst path = await import(\"node:path\");\n\t\tawait fs.mkdir(path.dirname(this.path), { recursive: true });\n\t\tconst tempPath = `${this.path}.${process.pid}.${Date.now()}.tmp`;\n\t\tawait fs.writeFile(tempPath, `${JSON.stringify(value, null, \"\\t\")}\\n`, \"utf8\");\n\t\tawait fs.rename(tempPath, this.path);\n\t}\n}\n\nexport type OpenAICodexOAuthClientOptions = {\n\tstorage?: OpenAICodexAuthStorage;\n\trefreshSkewMs?: number;\n};\n\nexport class OpenAICodexOAuthClient {\n\treadonly storage: OpenAICodexAuthStorage;\n\treadonly refreshSkewMs: number;\n\n\tconstructor(options: OpenAICodexOAuthClientOptions = {}) {\n\t\tthis.storage = options.storage ?? new JsonOpenAICodexAuthStorage();\n\t\tthis.refreshSkewMs = options.refreshSkewMs ?? REFRESH_SKEW_MS;\n\t}\n\n\tasync login(options: OpenAICodexLoginOptions): Promise<OpenAICodexOAuthCredentials> {\n\t\tconst credentials = await loginOpenAICodex(options);\n\t\tawait this.storage.set(credentials);\n\t\treturn credentials;\n\t}\n\n\tasync getCredentials(): Promise<OpenAICodexOAuthCredentials | undefined> {\n\t\tconst credentials = await this.storage.get();\n\t\tif (!credentials) return undefined;\n\n\t\tif (credentials.expires > Date.now() + this.refreshSkewMs) {\n\t\t\treturn credentials;\n\t\t}\n\n\t\tconst refreshed = await refreshOpenAICodexToken(credentials.refresh);\n\t\tawait this.storage.set(refreshed);\n\t\treturn refreshed;\n\t}\n\n\tasync getApiKey(): Promise<string | undefined> {\n\t\treturn (await this.getCredentials())?.access;\n\t}\n\n\tasync getHeaders(): Promise<Record<string, string> | undefined> {\n\t\tconst credentials = await this.getCredentials();\n\t\treturn credentials ? openAICodexHeaders(credentials) : undefined;\n\t}\n\n\tasync logout(): Promise<void> {\n\t\tawait this.storage.clear();\n\t}\n}\n\nexport const openAICodexOAuthProvider = {\n\tid: DEFAULT_PROVIDER_ID,\n\tname: \"ChatGPT Plus/Pro (OpenAI Codex)\",\n\tusesCallbackServer: true,\n\n\tasync login(options: OpenAICodexLoginOptions): Promise<OpenAICodexOAuthCredentials> {\n\t\treturn loginOpenAICodex(options);\n\t},\n\n\tasync refreshToken(credentials: OpenAICodexOAuthCredentials): Promise<OpenAICodexOAuthCredentials> {\n\t\treturn refreshOpenAICodexToken(credentials.refresh);\n\t},\n\n\tgetApiKey(credentials: OpenAICodexOAuthCredentials): string {\n\t\treturn credentials.access;\n\t},\n\n\tgetHeaders(credentials: OpenAICodexOAuthCredentials): Record<string, string> {\n\t\treturn openAICodexHeaders(credentials);\n\t},\n} as const;\n"],"mappings":"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"}
|