@code-yeongyu/senpi 2026.6.3 → 2026.6.4
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/CHANGELOG.md +19 -9
- package/README.md +2 -0
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/cli.js +1 -1
- package/dist/bun/cli.js.map +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +2 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli-main.d.ts +3 -0
- package/dist/cli-main.d.ts.map +1 -0
- package/dist/cli-main.js +10 -0
- package/dist/cli-main.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +46 -13
- package/dist/cli.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +4 -3
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/export-html/template.js +19 -6
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +2 -0
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/package-manager.d.ts +2 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +22 -6
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/provider-display-names.d.ts.map +1 -1
- package/dist/core/provider-display-names.js +2 -0
- package/dist/core/provider-display-names.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +0 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +3 -12
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/self-update-bootstrap.d.ts +19 -0
- package/dist/self-update-bootstrap.d.ts.map +1 -0
- package/dist/self-update-bootstrap.js +160 -0
- package/dist/self-update-bootstrap.js.map +1 -0
- package/dist/senpi +46 -13
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +54 -22
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/open-browser.d.ts +9 -0
- package/dist/utils/open-browser.d.ts.map +1 -0
- package/dist/utils/open-browser.js +22 -0
- package/dist/utils/open-browser.js.map +1 -0
- package/docs/containerization.md +111 -0
- package/docs/docs.json +4 -0
- package/docs/extensions.md +2 -0
- package/docs/index.md +1 -0
- package/docs/providers.md +3 -0
- package/examples/extensions/README.md +1 -0
- package/examples/extensions/gondolin/index.ts +531 -0
- package/examples/extensions/gondolin/package-lock.json +185 -0
- package/examples/extensions/gondolin/package.json +19 -0
- package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
- package/node_modules/@earendil-works/pi-ai/README.md +5 -1
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +251 -48
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +208 -44
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +25 -8
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js +13 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js +4 -2
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +5 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-codex.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/openai-codex.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiBH,OAAO,KAAK,EACX,gBAAgB,EAChB,mBAAmB,EAEnB,WAAW,EACX,sBAAsB,EACtB,MAAM,YAAY,CAAC;AAapB,eAAO,MAAM,iCAAiC,YAAY,CAAC;AAC3D,eAAO,MAAM,qCAAqC,gBAAgB,CAAC;AAgYnE;;GAEG;AACH,wBAAsB,0BAA0B,CAAC,OAAO,EAAE;IACzD,YAAY,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAClD,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAe5B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,CAAC,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/D,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACnD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAoF5B;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAE7F;AAED,eAAO,MAAM,wBAAwB,EAAE,sBA2CtC,CAAC","sourcesContent":["/**\n * OpenAI Codex (ChatGPT OAuth) flow\n *\n * NOTE: This module uses Node.js crypto and http for the OAuth callback.\n * It is only intended for CLI use, not browser environments.\n */\n\n// NEVER convert to top-level imports - breaks browser/Vite builds\nlet _randomBytes: typeof import(\"node:crypto\").randomBytes | null = null;\nlet _http: typeof import(\"node:http\") | null = null;\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\timport(\"node:crypto\").then((m) => {\n\t\t_randomBytes = m.randomBytes;\n\t});\n\timport(\"node:http\").then((m) => {\n\t\t_http = m;\n\t});\n}\n\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\nimport type {\n\tOAuthCredentials,\n\tOAuthDeviceCodeInfo,\n\tOAuthLoginCallbacks,\n\tOAuthPrompt,\n\tOAuthProviderInterface,\n} from \"./types.ts\";\n\nconst CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || \"127.0.0.1\";\nconst CLIENT_ID = \"app_EMoamEEZ73f0CkXaXp7hrann\";\nconst AUTH_BASE_URL = \"https://auth.openai.com\";\nconst AUTHORIZE_URL = `${AUTH_BASE_URL}/oauth/authorize`;\nconst TOKEN_URL = `${AUTH_BASE_URL}/oauth/token`;\nconst REDIRECT_URI = \"http://localhost:1455/auth/callback\";\nconst DEVICE_USER_CODE_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/usercode`;\nconst DEVICE_TOKEN_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/token`;\nconst DEVICE_VERIFICATION_URI = `${AUTH_BASE_URL}/codex/device`;\nconst DEVICE_REDIRECT_URI = `${AUTH_BASE_URL}/deviceauth/callback`;\nconst DEVICE_CODE_TIMEOUT_SECONDS = 15 * 60;\nexport const OPENAI_CODEX_BROWSER_LOGIN_METHOD = \"browser\";\nexport const OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD = \"device_code\";\nconst SCOPE = \"openid profile email offline_access\";\nconst JWT_CLAIM_PATH = \"https://api.openai.com/auth\";\n\ntype OAuthToken = { access: string; refresh: string; expires: number };\ntype TokenOperation = \"exchange\" | \"refresh\";\n\ntype DeviceAuthInfo = {\n\tdeviceAuthId: string;\n\tuserCode: string;\n\tintervalSeconds: number;\n};\n\ntype DeviceTokenSuccess = {\n\tauthorizationCode: string;\n\tcodeVerifier: 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\nfunction createState(): string {\n\tif (!_randomBytes) {\n\t\tthrow new Error(\"OpenAI Codex OAuth is only available in Node.js environments\");\n\t}\n\treturn _randomBytes(16).toString(\"hex\");\n}\n\nfunction parseAuthorizationInput(input: string): { code?: string; state?: string } {\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\tconst payload = parts[1] ?? \"\";\n\t\tconst decoded = atob(payload);\n\t\treturn JSON.parse(decoded) as JwtPayload;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function fetchWithLoginCancellation(input: string, init: RequestInit): Promise<Response> {\n\ttry {\n\t\treturn await fetch(input, init);\n\t} catch (error) {\n\t\tif (init.signal?.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nasync function readTokenResponse(response: Response, operation: TokenOperation): Promise<OAuthToken> {\n\tif (!response.ok) {\n\t\tconst text = await response.text().catch(() => \"\");\n\t\tthrow new Error(`OpenAI Codex token ${operation} failed (${response.status}): ${text || response.statusText}`);\n\t}\n\n\tconst rawJson = await response.json();\n\tconst json = rawJson as {\n\t\taccess_token?: string;\n\t\trefresh_token?: string;\n\t\texpires_in?: number;\n\t} | null;\n\tif (!json?.access_token || !json.refresh_token || typeof json.expires_in !== \"number\") {\n\t\tthrow new Error(`OpenAI Codex token ${operation} response missing fields: ${JSON.stringify(json)}`);\n\t}\n\n\treturn {\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 exchangeAuthorizationCode(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string = REDIRECT_URI,\n\tsignal?: AbortSignal,\n): Promise<OAuthToken> {\n\tconst response = await fetchWithLoginCancellation(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\tsignal,\n\t});\n\n\treturn readTokenResponse(response, \"exchange\");\n}\n\nasync function refreshAccessToken(refreshToken: string): Promise<OAuthToken> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = 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\t} catch (error) {\n\t\tthrow new Error(`OpenAI Codex token refresh error: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\n\treturn readTokenResponse(response, \"refresh\");\n}\n\nasync function startOpenAICodexDeviceAuth(signal?: AbortSignal): Promise<DeviceAuthInfo> {\n\tconst response = await fetchWithLoginCancellation(DEVICE_USER_CODE_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ client_id: CLIENT_ID }),\n\t\tsignal,\n\t});\n\n\tif (!response.ok) {\n\t\tif (response.status === 404) {\n\t\t\tthrow new Error(\n\t\t\t\t\"OpenAI Codex device code login is not enabled for this server. Use browser login or verify the server URL.\",\n\t\t\t);\n\t\t}\n\t\tconst responseBody = await response.text().catch(() => \"\");\n\t\tthrow new Error(\n\t\t\t`OpenAI Codex device code request failed with status ${response.status}${responseBody ? `: ${responseBody}` : \"\"}`,\n\t\t);\n\t}\n\n\tconst rawJson = await response.json();\n\tconst json = rawJson as {\n\t\tdevice_auth_id?: string;\n\t\tuser_code?: string;\n\t\tinterval?: number | string;\n\t} | null;\n\tconst intervalSeconds = typeof json?.interval === \"string\" ? Number(json.interval.trim()) : json?.interval;\n\tif (\n\t\t!json?.device_auth_id ||\n\t\t!json.user_code ||\n\t\ttypeof intervalSeconds !== \"number\" ||\n\t\t!Number.isFinite(intervalSeconds) ||\n\t\tintervalSeconds < 0\n\t) {\n\t\tthrow new Error(`Invalid OpenAI Codex device code response: ${JSON.stringify(json)}`);\n\t}\n\n\treturn {\n\t\tdeviceAuthId: json.device_auth_id,\n\t\tuserCode: json.user_code,\n\t\tintervalSeconds,\n\t};\n}\n\nasync function pollOpenAICodexDeviceAuth(device: DeviceAuthInfo, signal?: AbortSignal): Promise<DeviceTokenSuccess> {\n\treturn pollOAuthDeviceCodeFlow<DeviceTokenSuccess>({\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS,\n\t\tsignal,\n\t\tpoll: async () => {\n\t\t\tconst response = await fetchWithLoginCancellation(DEVICE_TOKEN_URL, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tdevice_auth_id: device.deviceAuthId,\n\t\t\t\t\tuser_code: device.userCode,\n\t\t\t\t}),\n\t\t\t\tsignal,\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\tconst rawJson = await response.json();\n\t\t\t\tconst json = rawJson as { authorization_code?: string; code_verifier?: string } | null;\n\t\t\t\tif (!json?.authorization_code || !json.code_verifier) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\t\tmessage: `Invalid OpenAI Codex device auth token response: ${JSON.stringify(json)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"complete\",\n\t\t\t\t\tvalue: { authorizationCode: json.authorization_code, codeVerifier: json.code_verifier },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (response.status === 403 || response.status === 404) {\n\t\t\t\treturn { status: \"pending\" };\n\t\t\t}\n\n\t\t\tconst responseBody = await response.text().catch(() => \"\");\n\t\t\tlet errorCode: unknown;\n\t\t\ttry {\n\t\t\t\tconst json = JSON.parse(responseBody) as { error?: string | { code?: string } } | null;\n\t\t\t\tconst error = json?.error;\n\t\t\t\terrorCode = typeof error === \"object\" ? error?.code : error;\n\t\t\t} catch {}\n\n\t\t\tif (errorCode === \"deviceauth_authorization_pending\") {\n\t\t\t\treturn { status: \"pending\" };\n\t\t\t}\n\t\t\tif (errorCode === \"slow_down\") {\n\t\t\t\treturn { status: \"slow_down\" };\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tmessage: `OpenAI Codex device auth failed with status ${response.status}${responseBody ? `: ${responseBody}` : \"\"}`,\n\t\t\t};\n\t\t},\n\t});\n}\n\nasync function createAuthorizationFlow(\n\toriginator: string = \"senpi\",\n): Promise<{ verifier: string; state: string; url: string }> {\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\", REDIRECT_URI);\n\turl.searchParams.set(\"scope\", 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\", originator);\n\n\treturn { verifier, state, url: url.toString() };\n}\n\ntype OAuthServerInfo = {\n\tclose: () => void;\n\tcancelWait: () => void;\n\twaitForCode: () => Promise<{ code: string } | null>;\n};\n\nfunction startLocalOAuthServer(state: string): Promise<OAuthServerInfo> {\n\tif (!_http) {\n\t\tthrow new Error(\"OpenAI Codex OAuth 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\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\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\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 {\n\t\t\tres.statusCode = 500;\n\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\tres.end(oauthErrorHtml(\"Internal error while processing OAuth callback.\"));\n\t\t}\n\t});\n\n\treturn new Promise((resolve) => {\n\t\tserver\n\t\t\t.listen(1455, CALLBACK_HOST, () => {\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\", (_err: NodeJS.ErrnoException) => {\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\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\nfunction getAccountId(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\nfunction credentialsFromToken(token: OAuthToken): OAuthCredentials {\n\tconst accountId = getAccountId(token.access);\n\tif (!accountId) {\n\t\tthrow new Error(\"Failed to extract accountId from token\");\n\t}\n\n\treturn {\n\t\taccess: token.access,\n\t\trefresh: token.refresh,\n\t\texpires: token.expires,\n\t\taccountId,\n\t};\n}\n\nasync function exchangeAuthorizationCodeForCredentials(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string,\n\tsignal?: AbortSignal,\n): Promise<OAuthCredentials> {\n\treturn credentialsFromToken(await exchangeAuthorizationCode(code, verifier, redirectUri, signal));\n}\n\n/**\n * Login with OpenAI Codex OAuth using the Codex device-code flow.\n */\nexport async function loginOpenAICodexDeviceCode(options: {\n\tonDeviceCode: (info: OAuthDeviceCodeInfo) => void;\n\tsignal?: AbortSignal;\n}): Promise<OAuthCredentials> {\n\tconst device = await startOpenAICodexDeviceAuth(options.signal);\n\toptions.onDeviceCode({\n\t\tuserCode: device.userCode,\n\t\tverificationUri: DEVICE_VERIFICATION_URI,\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS,\n\t});\n\tconst code = await pollOpenAICodexDeviceAuth(device, options.signal);\n\treturn exchangeAuthorizationCodeForCredentials(\n\t\tcode.authorizationCode,\n\t\tcode.codeVerifier,\n\t\tDEVICE_REDIRECT_URI,\n\t\toptions.signal,\n\t);\n}\n\n/**\n * Login with OpenAI Codex OAuth\n *\n * @param options.onAuth - Called with URL and instructions when auth starts\n * @param options.onPrompt - Called to prompt user for manual code paste (fallback if no onManualCodeInput)\n * @param options.onProgress - Optional progress messages\n * @param options.onManualCodeInput - Optional promise that resolves with user-pasted code.\n * Races with browser callback - whichever completes first wins.\n * Useful for showing paste input immediately alongside browser flow.\n * @param options.originator - OAuth originator parameter (defaults to \"senpi\")\n */\nexport async function loginOpenAICodex(options: {\n\tonAuth: (info: { url: string; instructions?: string }) => void;\n\tonPrompt: (prompt: OAuthPrompt) => Promise<string>;\n\tonProgress?: (message: string) => void;\n\tonManualCodeInput?: () => Promise<string>;\n\toriginator?: string;\n}): Promise<OAuthCredentials> {\n\tconst { verifier, state, url } = await createAuthorizationFlow(options.originator);\n\tconst server = await startLocalOAuthServer(state);\n\n\toptions.onAuth({ url, instructions: \"A browser window should open. Complete login to finish.\" });\n\n\tlet code: string | undefined;\n\ttry {\n\t\tif (options.onManualCodeInput) {\n\t\t\t// Race between browser callback and manual input\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\tserver.cancelWait();\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tmanualError = err instanceof Error ? err : new Error(String(err));\n\t\t\t\t\tserver.cancelWait();\n\t\t\t\t});\n\n\t\t\tconst result = await server.waitForCode();\n\n\t\t\t// If manual input was cancelled, throw that error\n\t\t\tif (manualError) {\n\t\t\t\tthrow manualError;\n\t\t\t}\n\n\t\t\tif (result?.code) {\n\t\t\t\t// Browser callback won\n\t\t\t\tcode = result.code;\n\t\t\t} else if (manualCode) {\n\t\t\t\t// Manual input won (or callback timed out and user had entered code)\n\t\t\t\tconst parsed = parseAuthorizationInput(manualCode);\n\t\t\t\tif (parsed.state && parsed.state !== state) {\n\t\t\t\t\tthrow new Error(\"State mismatch\");\n\t\t\t\t}\n\t\t\t\tcode = parsed.code;\n\t\t\t}\n\n\t\t\t// If still no code, wait for manual promise to complete and try that\n\t\t\tif (!code) {\n\t\t\t\tawait manualPromise;\n\t\t\t\tif (manualError) {\n\t\t\t\t\tthrow manualError;\n\t\t\t\t}\n\t\t\t\tif (manualCode) {\n\t\t\t\t\tconst parsed = parseAuthorizationInput(manualCode);\n\t\t\t\t\tif (parsed.state && parsed.state !== state) {\n\t\t\t\t\t\tthrow new Error(\"State mismatch\");\n\t\t\t\t\t}\n\t\t\t\t\tcode = parsed.code;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Original flow: wait for callback, then prompt if needed\n\t\t\tconst result = await server.waitForCode();\n\t\t\tif (result?.code) {\n\t\t\t\tcode = result.code;\n\t\t\t}\n\t\t}\n\n\t\t// Fallback to onPrompt if still no code\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 = parseAuthorizationInput(input);\n\t\t\tif (parsed.state && parsed.state !== state) {\n\t\t\t\tthrow new Error(\"State mismatch\");\n\t\t\t}\n\t\t\tcode = parsed.code;\n\t\t}\n\n\t\tif (!code) {\n\t\t\tthrow new Error(\"Missing authorization code\");\n\t\t}\n\n\t\treturn exchangeAuthorizationCodeForCredentials(code, verifier, REDIRECT_URI);\n\t} finally {\n\t\tserver.close();\n\t}\n}\n\n/**\n * Refresh OpenAI Codex OAuth token\n */\nexport async function refreshOpenAICodexToken(refreshToken: string): Promise<OAuthCredentials> {\n\treturn credentialsFromToken(await refreshAccessToken(refreshToken));\n}\n\nexport const openaiCodexOAuthProvider: OAuthProviderInterface = {\n\tid: \"openai-codex\",\n\tname: \"ChatGPT Plus/Pro (Codex Subscription)\",\n\tusesCallbackServer: true,\n\n\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\tconst loginMethod = await callbacks.onSelect({\n\t\t\tmessage: \"Select OpenAI Codex login method:\",\n\t\t\toptions: [\n\t\t\t\t{ id: OPENAI_CODEX_BROWSER_LOGIN_METHOD, label: \"Browser login (default)\" },\n\t\t\t\t{ id: OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD, label: \"Device code login (headless)\" },\n\t\t\t],\n\t\t});\n\t\tif (!loginMethod) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\n\t\tif (loginMethod === OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD) {\n\t\t\treturn loginOpenAICodexDeviceCode({\n\t\t\t\tonDeviceCode: callbacks.onDeviceCode,\n\t\t\t\tsignal: callbacks.signal,\n\t\t\t});\n\t\t}\n\n\t\tif (loginMethod !== OPENAI_CODEX_BROWSER_LOGIN_METHOD) {\n\t\t\tthrow new Error(`Unknown OpenAI Codex login method: ${loginMethod}`);\n\t\t}\n\n\t\treturn loginOpenAICodex({\n\t\t\tonAuth: callbacks.onAuth,\n\t\t\tonPrompt: callbacks.onPrompt,\n\t\t\tonProgress: callbacks.onProgress,\n\t\t\tonManualCodeInput: callbacks.onManualCodeInput,\n\t\t});\n\t},\n\n\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\treturn refreshOpenAICodexToken(credentials.refresh);\n\t},\n\n\tgetApiKey(credentials: OAuthCredentials): string {\n\t\treturn credentials.access;\n\t},\n};\n"]}
|
|
1
|
+
{"version":3,"file":"openai-codex.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/openai-codex.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiBH,OAAO,KAAK,EACX,gBAAgB,EAChB,mBAAmB,EAEnB,WAAW,EACX,sBAAsB,EACtB,MAAM,YAAY,CAAC;AAYpB,eAAO,MAAM,iCAAiC,YAAY,CAAC;AAC3D,eAAO,MAAM,qCAAqC,gBAAgB,CAAC;AAoYnE;;GAEG;AACH,wBAAsB,0BAA0B,CAAC,OAAO,EAAE;IACzD,YAAY,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAClD,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAe5B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,CAAC,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/D,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACnD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAoF5B;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAE7F;AAED,eAAO,MAAM,wBAAwB,EAAE,sBA2CtC,CAAC","sourcesContent":["/**\n * OpenAI Codex (ChatGPT OAuth) flow\n *\n * NOTE: This module uses Node.js crypto and http for the OAuth callback.\n * It is only intended for CLI use, not browser environments.\n */\n\n// NEVER convert to top-level imports - breaks browser/Vite builds\nlet _randomBytes: typeof import(\"node:crypto\").randomBytes | null = null;\nlet _http: typeof import(\"node:http\") | null = null;\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\timport(\"node:crypto\").then((m) => {\n\t\t_randomBytes = m.randomBytes;\n\t});\n\timport(\"node:http\").then((m) => {\n\t\t_http = m;\n\t});\n}\n\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\nimport type {\n\tOAuthCredentials,\n\tOAuthDeviceCodeInfo,\n\tOAuthLoginCallbacks,\n\tOAuthPrompt,\n\tOAuthProviderInterface,\n} from \"./types.ts\";\n\nconst CLIENT_ID = \"app_EMoamEEZ73f0CkXaXp7hrann\";\nconst AUTH_BASE_URL = \"https://auth.openai.com\";\nconst AUTHORIZE_URL = `${AUTH_BASE_URL}/oauth/authorize`;\nconst TOKEN_URL = `${AUTH_BASE_URL}/oauth/token`;\nconst REDIRECT_URI = \"http://localhost:1455/auth/callback\";\nconst DEVICE_USER_CODE_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/usercode`;\nconst DEVICE_TOKEN_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/token`;\nconst DEVICE_VERIFICATION_URI = `${AUTH_BASE_URL}/codex/device`;\nconst DEVICE_REDIRECT_URI = `${AUTH_BASE_URL}/deviceauth/callback`;\nconst DEVICE_CODE_TIMEOUT_SECONDS = 15 * 60;\nexport const OPENAI_CODEX_BROWSER_LOGIN_METHOD = \"browser\";\nexport const OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD = \"device_code\";\nconst SCOPE = \"openid profile email offline_access\";\nconst JWT_CLAIM_PATH = \"https://api.openai.com/auth\";\n\ntype OAuthToken = { access: string; refresh: string; expires: number };\ntype TokenOperation = \"exchange\" | \"refresh\";\n\nfunction getCallbackHost(): string {\n\treturn typeof process !== \"undefined\" ? process.env.PI_OAUTH_CALLBACK_HOST || \"127.0.0.1\" : \"127.0.0.1\";\n}\n\ntype DeviceAuthInfo = {\n\tdeviceAuthId: string;\n\tuserCode: string;\n\tintervalSeconds: number;\n};\n\ntype DeviceTokenSuccess = {\n\tauthorizationCode: string;\n\tcodeVerifier: 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\nfunction createState(): string {\n\tif (!_randomBytes) {\n\t\tthrow new Error(\"OpenAI Codex OAuth is only available in Node.js environments\");\n\t}\n\treturn _randomBytes(16).toString(\"hex\");\n}\n\nfunction parseAuthorizationInput(input: string): { code?: string; state?: string } {\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\tconst payload = parts[1] ?? \"\";\n\t\tconst decoded = atob(payload);\n\t\treturn JSON.parse(decoded) as JwtPayload;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function fetchWithLoginCancellation(input: string, init: RequestInit): Promise<Response> {\n\ttry {\n\t\treturn await fetch(input, init);\n\t} catch (error) {\n\t\tif (init.signal?.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nasync function readTokenResponse(response: Response, operation: TokenOperation): Promise<OAuthToken> {\n\tif (!response.ok) {\n\t\tconst text = await response.text().catch(() => \"\");\n\t\tthrow new Error(`OpenAI Codex token ${operation} failed (${response.status}): ${text || response.statusText}`);\n\t}\n\n\tconst rawJson = await response.json();\n\tconst json = rawJson as {\n\t\taccess_token?: string;\n\t\trefresh_token?: string;\n\t\texpires_in?: number;\n\t} | null;\n\tif (!json?.access_token || !json.refresh_token || typeof json.expires_in !== \"number\") {\n\t\tthrow new Error(`OpenAI Codex token ${operation} response missing fields: ${JSON.stringify(json)}`);\n\t}\n\n\treturn {\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 exchangeAuthorizationCode(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string = REDIRECT_URI,\n\tsignal?: AbortSignal,\n): Promise<OAuthToken> {\n\tconst response = await fetchWithLoginCancellation(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\tsignal,\n\t});\n\n\treturn readTokenResponse(response, \"exchange\");\n}\n\nasync function refreshAccessToken(refreshToken: string): Promise<OAuthToken> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = 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\t} catch (error) {\n\t\tthrow new Error(`OpenAI Codex token refresh error: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\n\treturn readTokenResponse(response, \"refresh\");\n}\n\nasync function startOpenAICodexDeviceAuth(signal?: AbortSignal): Promise<DeviceAuthInfo> {\n\tconst response = await fetchWithLoginCancellation(DEVICE_USER_CODE_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ client_id: CLIENT_ID }),\n\t\tsignal,\n\t});\n\n\tif (!response.ok) {\n\t\tif (response.status === 404) {\n\t\t\tthrow new Error(\n\t\t\t\t\"OpenAI Codex device code login is not enabled for this server. Use browser login or verify the server URL.\",\n\t\t\t);\n\t\t}\n\t\tconst responseBody = await response.text().catch(() => \"\");\n\t\tthrow new Error(\n\t\t\t`OpenAI Codex device code request failed with status ${response.status}${responseBody ? `: ${responseBody}` : \"\"}`,\n\t\t);\n\t}\n\n\tconst rawJson = await response.json();\n\tconst json = rawJson as {\n\t\tdevice_auth_id?: string;\n\t\tuser_code?: string;\n\t\tinterval?: number | string;\n\t} | null;\n\tconst intervalSeconds = typeof json?.interval === \"string\" ? Number(json.interval.trim()) : json?.interval;\n\tif (\n\t\t!json?.device_auth_id ||\n\t\t!json.user_code ||\n\t\ttypeof intervalSeconds !== \"number\" ||\n\t\t!Number.isFinite(intervalSeconds) ||\n\t\tintervalSeconds < 0\n\t) {\n\t\tthrow new Error(`Invalid OpenAI Codex device code response: ${JSON.stringify(json)}`);\n\t}\n\n\treturn {\n\t\tdeviceAuthId: json.device_auth_id,\n\t\tuserCode: json.user_code,\n\t\tintervalSeconds,\n\t};\n}\n\nasync function pollOpenAICodexDeviceAuth(device: DeviceAuthInfo, signal?: AbortSignal): Promise<DeviceTokenSuccess> {\n\treturn pollOAuthDeviceCodeFlow<DeviceTokenSuccess>({\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS,\n\t\tsignal,\n\t\tpoll: async () => {\n\t\t\tconst response = await fetchWithLoginCancellation(DEVICE_TOKEN_URL, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tdevice_auth_id: device.deviceAuthId,\n\t\t\t\t\tuser_code: device.userCode,\n\t\t\t\t}),\n\t\t\t\tsignal,\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\tconst rawJson = await response.json();\n\t\t\t\tconst json = rawJson as { authorization_code?: string; code_verifier?: string } | null;\n\t\t\t\tif (!json?.authorization_code || !json.code_verifier) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\t\tmessage: `Invalid OpenAI Codex device auth token response: ${JSON.stringify(json)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"complete\",\n\t\t\t\t\tvalue: { authorizationCode: json.authorization_code, codeVerifier: json.code_verifier },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (response.status === 403 || response.status === 404) {\n\t\t\t\treturn { status: \"pending\" };\n\t\t\t}\n\n\t\t\tconst responseBody = await response.text().catch(() => \"\");\n\t\t\tlet errorCode: unknown;\n\t\t\ttry {\n\t\t\t\tconst json = JSON.parse(responseBody) as { error?: string | { code?: string } } | null;\n\t\t\t\tconst error = json?.error;\n\t\t\t\terrorCode = typeof error === \"object\" ? error?.code : error;\n\t\t\t} catch {}\n\n\t\t\tif (errorCode === \"deviceauth_authorization_pending\") {\n\t\t\t\treturn { status: \"pending\" };\n\t\t\t}\n\t\t\tif (errorCode === \"slow_down\") {\n\t\t\t\treturn { status: \"slow_down\" };\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tmessage: `OpenAI Codex device auth failed with status ${response.status}${responseBody ? `: ${responseBody}` : \"\"}`,\n\t\t\t};\n\t\t},\n\t});\n}\n\nasync function createAuthorizationFlow(\n\toriginator: string = \"senpi\",\n): Promise<{ verifier: string; state: string; url: string }> {\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\", REDIRECT_URI);\n\turl.searchParams.set(\"scope\", 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\", originator);\n\n\treturn { verifier, state, url: url.toString() };\n}\n\ntype OAuthServerInfo = {\n\tclose: () => void;\n\tcancelWait: () => void;\n\twaitForCode: () => Promise<{ code: string } | null>;\n};\n\nfunction startLocalOAuthServer(state: string): Promise<OAuthServerInfo> {\n\tif (!_http) {\n\t\tthrow new Error(\"OpenAI Codex OAuth 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\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\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\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 {\n\t\t\tres.statusCode = 500;\n\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\tres.end(oauthErrorHtml(\"Internal error while processing OAuth callback.\"));\n\t\t}\n\t});\n\n\treturn new Promise((resolve) => {\n\t\tserver\n\t\t\t.listen(1455, getCallbackHost(), () => {\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\", (_err: NodeJS.ErrnoException) => {\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\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\nfunction getAccountId(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\nfunction credentialsFromToken(token: OAuthToken): OAuthCredentials {\n\tconst accountId = getAccountId(token.access);\n\tif (!accountId) {\n\t\tthrow new Error(\"Failed to extract accountId from token\");\n\t}\n\n\treturn {\n\t\taccess: token.access,\n\t\trefresh: token.refresh,\n\t\texpires: token.expires,\n\t\taccountId,\n\t};\n}\n\nasync function exchangeAuthorizationCodeForCredentials(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string,\n\tsignal?: AbortSignal,\n): Promise<OAuthCredentials> {\n\treturn credentialsFromToken(await exchangeAuthorizationCode(code, verifier, redirectUri, signal));\n}\n\n/**\n * Login with OpenAI Codex OAuth using the Codex device-code flow.\n */\nexport async function loginOpenAICodexDeviceCode(options: {\n\tonDeviceCode: (info: OAuthDeviceCodeInfo) => void;\n\tsignal?: AbortSignal;\n}): Promise<OAuthCredentials> {\n\tconst device = await startOpenAICodexDeviceAuth(options.signal);\n\toptions.onDeviceCode({\n\t\tuserCode: device.userCode,\n\t\tverificationUri: DEVICE_VERIFICATION_URI,\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS,\n\t});\n\tconst code = await pollOpenAICodexDeviceAuth(device, options.signal);\n\treturn exchangeAuthorizationCodeForCredentials(\n\t\tcode.authorizationCode,\n\t\tcode.codeVerifier,\n\t\tDEVICE_REDIRECT_URI,\n\t\toptions.signal,\n\t);\n}\n\n/**\n * Login with OpenAI Codex OAuth\n *\n * @param options.onAuth - Called with URL and instructions when auth starts\n * @param options.onPrompt - Called to prompt user for manual code paste (fallback if no onManualCodeInput)\n * @param options.onProgress - Optional progress messages\n * @param options.onManualCodeInput - Optional promise that resolves with user-pasted code.\n * Races with browser callback - whichever completes first wins.\n * Useful for showing paste input immediately alongside browser flow.\n * @param options.originator - OAuth originator parameter (defaults to \"senpi\")\n */\nexport async function loginOpenAICodex(options: {\n\tonAuth: (info: { url: string; instructions?: string }) => void;\n\tonPrompt: (prompt: OAuthPrompt) => Promise<string>;\n\tonProgress?: (message: string) => void;\n\tonManualCodeInput?: () => Promise<string>;\n\toriginator?: string;\n}): Promise<OAuthCredentials> {\n\tconst { verifier, state, url } = await createAuthorizationFlow(options.originator);\n\tconst server = await startLocalOAuthServer(state);\n\n\toptions.onAuth({ url, instructions: \"A browser window should open. Complete login to finish.\" });\n\n\tlet code: string | undefined;\n\ttry {\n\t\tif (options.onManualCodeInput) {\n\t\t\t// Race between browser callback and manual input\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\tserver.cancelWait();\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tmanualError = err instanceof Error ? err : new Error(String(err));\n\t\t\t\t\tserver.cancelWait();\n\t\t\t\t});\n\n\t\t\tconst result = await server.waitForCode();\n\n\t\t\t// If manual input was cancelled, throw that error\n\t\t\tif (manualError) {\n\t\t\t\tthrow manualError;\n\t\t\t}\n\n\t\t\tif (result?.code) {\n\t\t\t\t// Browser callback won\n\t\t\t\tcode = result.code;\n\t\t\t} else if (manualCode) {\n\t\t\t\t// Manual input won (or callback timed out and user had entered code)\n\t\t\t\tconst parsed = parseAuthorizationInput(manualCode);\n\t\t\t\tif (parsed.state && parsed.state !== state) {\n\t\t\t\t\tthrow new Error(\"State mismatch\");\n\t\t\t\t}\n\t\t\t\tcode = parsed.code;\n\t\t\t}\n\n\t\t\t// If still no code, wait for manual promise to complete and try that\n\t\t\tif (!code) {\n\t\t\t\tawait manualPromise;\n\t\t\t\tif (manualError) {\n\t\t\t\t\tthrow manualError;\n\t\t\t\t}\n\t\t\t\tif (manualCode) {\n\t\t\t\t\tconst parsed = parseAuthorizationInput(manualCode);\n\t\t\t\t\tif (parsed.state && parsed.state !== state) {\n\t\t\t\t\t\tthrow new Error(\"State mismatch\");\n\t\t\t\t\t}\n\t\t\t\t\tcode = parsed.code;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Original flow: wait for callback, then prompt if needed\n\t\t\tconst result = await server.waitForCode();\n\t\t\tif (result?.code) {\n\t\t\t\tcode = result.code;\n\t\t\t}\n\t\t}\n\n\t\t// Fallback to onPrompt if still no code\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 = parseAuthorizationInput(input);\n\t\t\tif (parsed.state && parsed.state !== state) {\n\t\t\t\tthrow new Error(\"State mismatch\");\n\t\t\t}\n\t\t\tcode = parsed.code;\n\t\t}\n\n\t\tif (!code) {\n\t\t\tthrow new Error(\"Missing authorization code\");\n\t\t}\n\n\t\treturn exchangeAuthorizationCodeForCredentials(code, verifier, REDIRECT_URI);\n\t} finally {\n\t\tserver.close();\n\t}\n}\n\n/**\n * Refresh OpenAI Codex OAuth token\n */\nexport async function refreshOpenAICodexToken(refreshToken: string): Promise<OAuthCredentials> {\n\treturn credentialsFromToken(await refreshAccessToken(refreshToken));\n}\n\nexport const openaiCodexOAuthProvider: OAuthProviderInterface = {\n\tid: \"openai-codex\",\n\tname: \"ChatGPT Plus/Pro (Codex Subscription)\",\n\tusesCallbackServer: true,\n\n\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\tconst loginMethod = await callbacks.onSelect({\n\t\t\tmessage: \"Select OpenAI Codex login method:\",\n\t\t\toptions: [\n\t\t\t\t{ id: OPENAI_CODEX_BROWSER_LOGIN_METHOD, label: \"Browser login (default)\" },\n\t\t\t\t{ id: OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD, label: \"Device code login (headless)\" },\n\t\t\t],\n\t\t});\n\t\tif (!loginMethod) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\n\t\tif (loginMethod === OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD) {\n\t\t\treturn loginOpenAICodexDeviceCode({\n\t\t\t\tonDeviceCode: callbacks.onDeviceCode,\n\t\t\t\tsignal: callbacks.signal,\n\t\t\t});\n\t\t}\n\n\t\tif (loginMethod !== OPENAI_CODEX_BROWSER_LOGIN_METHOD) {\n\t\t\tthrow new Error(`Unknown OpenAI Codex login method: ${loginMethod}`);\n\t\t}\n\n\t\treturn loginOpenAICodex({\n\t\t\tonAuth: callbacks.onAuth,\n\t\t\tonPrompt: callbacks.onPrompt,\n\t\t\tonProgress: callbacks.onProgress,\n\t\t\tonManualCodeInput: callbacks.onManualCodeInput,\n\t\t});\n\t},\n\n\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\treturn refreshOpenAICodexToken(credentials.refresh);\n\t},\n\n\tgetApiKey(credentials: OAuthCredentials): string {\n\t\treturn credentials.access;\n\t},\n};\n"]}
|
|
@@ -18,7 +18,6 @@ if (typeof process !== "undefined" && (process.versions?.node || process.version
|
|
|
18
18
|
import { pollOAuthDeviceCodeFlow } from "./device-code.js";
|
|
19
19
|
import { oauthErrorHtml, oauthSuccessHtml } from "./oauth-page.js";
|
|
20
20
|
import { generatePKCE } from "./pkce.js";
|
|
21
|
-
const CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || "127.0.0.1";
|
|
22
21
|
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
23
22
|
const AUTH_BASE_URL = "https://auth.openai.com";
|
|
24
23
|
const AUTHORIZE_URL = `${AUTH_BASE_URL}/oauth/authorize`;
|
|
@@ -33,6 +32,9 @@ export const OPENAI_CODEX_BROWSER_LOGIN_METHOD = "browser";
|
|
|
33
32
|
export const OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD = "device_code";
|
|
34
33
|
const SCOPE = "openid profile email offline_access";
|
|
35
34
|
const JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
35
|
+
function getCallbackHost() {
|
|
36
|
+
return typeof process !== "undefined" ? process.env.PI_OAUTH_CALLBACK_HOST || "127.0.0.1" : "127.0.0.1";
|
|
37
|
+
}
|
|
36
38
|
function createState() {
|
|
37
39
|
if (!_randomBytes) {
|
|
38
40
|
throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
|
|
@@ -287,7 +289,7 @@ function startLocalOAuthServer(state) {
|
|
|
287
289
|
});
|
|
288
290
|
return new Promise((resolve) => {
|
|
289
291
|
server
|
|
290
|
-
.listen(1455,
|
|
292
|
+
.listen(1455, getCallbackHost(), () => {
|
|
291
293
|
resolve({
|
|
292
294
|
close: () => server.close(),
|
|
293
295
|
cancelWait: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-codex.js","sourceRoot":"","sources":["../../../src/utils/oauth/openai-codex.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OpenAI Codex (ChatGPT OAuth) flow\n *\n * NOTE: This module uses Node.js crypto and http for the OAuth callback.\n * It is only intended for CLI use, not browser environments.\n */\n\n// NEVER convert to top-level imports - breaks browser/Vite builds\nlet _randomBytes: typeof import(\"node:crypto\").randomBytes | null = null;\nlet _http: typeof import(\"node:http\") | null = null;\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\timport(\"node:crypto\").then((m) => {\n\t\t_randomBytes = m.randomBytes;\n\t});\n\timport(\"node:http\").then((m) => {\n\t\t_http = m;\n\t});\n}\n\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\nimport type {\n\tOAuthCredentials,\n\tOAuthDeviceCodeInfo,\n\tOAuthLoginCallbacks,\n\tOAuthPrompt,\n\tOAuthProviderInterface,\n} from \"./types.ts\";\n\nconst CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || \"127.0.0.1\";\nconst CLIENT_ID = \"app_EMoamEEZ73f0CkXaXp7hrann\";\nconst AUTH_BASE_URL = \"https://auth.openai.com\";\nconst AUTHORIZE_URL = `${AUTH_BASE_URL}/oauth/authorize`;\nconst TOKEN_URL = `${AUTH_BASE_URL}/oauth/token`;\nconst REDIRECT_URI = \"http://localhost:1455/auth/callback\";\nconst DEVICE_USER_CODE_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/usercode`;\nconst DEVICE_TOKEN_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/token`;\nconst DEVICE_VERIFICATION_URI = `${AUTH_BASE_URL}/codex/device`;\nconst DEVICE_REDIRECT_URI = `${AUTH_BASE_URL}/deviceauth/callback`;\nconst DEVICE_CODE_TIMEOUT_SECONDS = 15 * 60;\nexport const OPENAI_CODEX_BROWSER_LOGIN_METHOD = \"browser\";\nexport const OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD = \"device_code\";\nconst SCOPE = \"openid profile email offline_access\";\nconst JWT_CLAIM_PATH = \"https://api.openai.com/auth\";\n\ntype OAuthToken = { access: string; refresh: string; expires: number };\ntype TokenOperation = \"exchange\" | \"refresh\";\n\ntype DeviceAuthInfo = {\n\tdeviceAuthId: string;\n\tuserCode: string;\n\tintervalSeconds: number;\n};\n\ntype DeviceTokenSuccess = {\n\tauthorizationCode: string;\n\tcodeVerifier: 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\nfunction createState(): string {\n\tif (!_randomBytes) {\n\t\tthrow new Error(\"OpenAI Codex OAuth is only available in Node.js environments\");\n\t}\n\treturn _randomBytes(16).toString(\"hex\");\n}\n\nfunction parseAuthorizationInput(input: string): { code?: string; state?: string } {\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\tconst payload = parts[1] ?? \"\";\n\t\tconst decoded = atob(payload);\n\t\treturn JSON.parse(decoded) as JwtPayload;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function fetchWithLoginCancellation(input: string, init: RequestInit): Promise<Response> {\n\ttry {\n\t\treturn await fetch(input, init);\n\t} catch (error) {\n\t\tif (init.signal?.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nasync function readTokenResponse(response: Response, operation: TokenOperation): Promise<OAuthToken> {\n\tif (!response.ok) {\n\t\tconst text = await response.text().catch(() => \"\");\n\t\tthrow new Error(`OpenAI Codex token ${operation} failed (${response.status}): ${text || response.statusText}`);\n\t}\n\n\tconst rawJson = await response.json();\n\tconst json = rawJson as {\n\t\taccess_token?: string;\n\t\trefresh_token?: string;\n\t\texpires_in?: number;\n\t} | null;\n\tif (!json?.access_token || !json.refresh_token || typeof json.expires_in !== \"number\") {\n\t\tthrow new Error(`OpenAI Codex token ${operation} response missing fields: ${JSON.stringify(json)}`);\n\t}\n\n\treturn {\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 exchangeAuthorizationCode(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string = REDIRECT_URI,\n\tsignal?: AbortSignal,\n): Promise<OAuthToken> {\n\tconst response = await fetchWithLoginCancellation(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\tsignal,\n\t});\n\n\treturn readTokenResponse(response, \"exchange\");\n}\n\nasync function refreshAccessToken(refreshToken: string): Promise<OAuthToken> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = 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\t} catch (error) {\n\t\tthrow new Error(`OpenAI Codex token refresh error: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\n\treturn readTokenResponse(response, \"refresh\");\n}\n\nasync function startOpenAICodexDeviceAuth(signal?: AbortSignal): Promise<DeviceAuthInfo> {\n\tconst response = await fetchWithLoginCancellation(DEVICE_USER_CODE_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ client_id: CLIENT_ID }),\n\t\tsignal,\n\t});\n\n\tif (!response.ok) {\n\t\tif (response.status === 404) {\n\t\t\tthrow new Error(\n\t\t\t\t\"OpenAI Codex device code login is not enabled for this server. Use browser login or verify the server URL.\",\n\t\t\t);\n\t\t}\n\t\tconst responseBody = await response.text().catch(() => \"\");\n\t\tthrow new Error(\n\t\t\t`OpenAI Codex device code request failed with status ${response.status}${responseBody ? `: ${responseBody}` : \"\"}`,\n\t\t);\n\t}\n\n\tconst rawJson = await response.json();\n\tconst json = rawJson as {\n\t\tdevice_auth_id?: string;\n\t\tuser_code?: string;\n\t\tinterval?: number | string;\n\t} | null;\n\tconst intervalSeconds = typeof json?.interval === \"string\" ? Number(json.interval.trim()) : json?.interval;\n\tif (\n\t\t!json?.device_auth_id ||\n\t\t!json.user_code ||\n\t\ttypeof intervalSeconds !== \"number\" ||\n\t\t!Number.isFinite(intervalSeconds) ||\n\t\tintervalSeconds < 0\n\t) {\n\t\tthrow new Error(`Invalid OpenAI Codex device code response: ${JSON.stringify(json)}`);\n\t}\n\n\treturn {\n\t\tdeviceAuthId: json.device_auth_id,\n\t\tuserCode: json.user_code,\n\t\tintervalSeconds,\n\t};\n}\n\nasync function pollOpenAICodexDeviceAuth(device: DeviceAuthInfo, signal?: AbortSignal): Promise<DeviceTokenSuccess> {\n\treturn pollOAuthDeviceCodeFlow<DeviceTokenSuccess>({\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS,\n\t\tsignal,\n\t\tpoll: async () => {\n\t\t\tconst response = await fetchWithLoginCancellation(DEVICE_TOKEN_URL, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tdevice_auth_id: device.deviceAuthId,\n\t\t\t\t\tuser_code: device.userCode,\n\t\t\t\t}),\n\t\t\t\tsignal,\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\tconst rawJson = await response.json();\n\t\t\t\tconst json = rawJson as { authorization_code?: string; code_verifier?: string } | null;\n\t\t\t\tif (!json?.authorization_code || !json.code_verifier) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\t\tmessage: `Invalid OpenAI Codex device auth token response: ${JSON.stringify(json)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"complete\",\n\t\t\t\t\tvalue: { authorizationCode: json.authorization_code, codeVerifier: json.code_verifier },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (response.status === 403 || response.status === 404) {\n\t\t\t\treturn { status: \"pending\" };\n\t\t\t}\n\n\t\t\tconst responseBody = await response.text().catch(() => \"\");\n\t\t\tlet errorCode: unknown;\n\t\t\ttry {\n\t\t\t\tconst json = JSON.parse(responseBody) as { error?: string | { code?: string } } | null;\n\t\t\t\tconst error = json?.error;\n\t\t\t\terrorCode = typeof error === \"object\" ? error?.code : error;\n\t\t\t} catch {}\n\n\t\t\tif (errorCode === \"deviceauth_authorization_pending\") {\n\t\t\t\treturn { status: \"pending\" };\n\t\t\t}\n\t\t\tif (errorCode === \"slow_down\") {\n\t\t\t\treturn { status: \"slow_down\" };\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tmessage: `OpenAI Codex device auth failed with status ${response.status}${responseBody ? `: ${responseBody}` : \"\"}`,\n\t\t\t};\n\t\t},\n\t});\n}\n\nasync function createAuthorizationFlow(\n\toriginator: string = \"senpi\",\n): Promise<{ verifier: string; state: string; url: string }> {\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\", REDIRECT_URI);\n\turl.searchParams.set(\"scope\", 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\", originator);\n\n\treturn { verifier, state, url: url.toString() };\n}\n\ntype OAuthServerInfo = {\n\tclose: () => void;\n\tcancelWait: () => void;\n\twaitForCode: () => Promise<{ code: string } | null>;\n};\n\nfunction startLocalOAuthServer(state: string): Promise<OAuthServerInfo> {\n\tif (!_http) {\n\t\tthrow new Error(\"OpenAI Codex OAuth 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\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\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\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 {\n\t\t\tres.statusCode = 500;\n\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\tres.end(oauthErrorHtml(\"Internal error while processing OAuth callback.\"));\n\t\t}\n\t});\n\n\treturn new Promise((resolve) => {\n\t\tserver\n\t\t\t.listen(1455, CALLBACK_HOST, () => {\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\", (_err: NodeJS.ErrnoException) => {\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\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\nfunction getAccountId(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\nfunction credentialsFromToken(token: OAuthToken): OAuthCredentials {\n\tconst accountId = getAccountId(token.access);\n\tif (!accountId) {\n\t\tthrow new Error(\"Failed to extract accountId from token\");\n\t}\n\n\treturn {\n\t\taccess: token.access,\n\t\trefresh: token.refresh,\n\t\texpires: token.expires,\n\t\taccountId,\n\t};\n}\n\nasync function exchangeAuthorizationCodeForCredentials(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string,\n\tsignal?: AbortSignal,\n): Promise<OAuthCredentials> {\n\treturn credentialsFromToken(await exchangeAuthorizationCode(code, verifier, redirectUri, signal));\n}\n\n/**\n * Login with OpenAI Codex OAuth using the Codex device-code flow.\n */\nexport async function loginOpenAICodexDeviceCode(options: {\n\tonDeviceCode: (info: OAuthDeviceCodeInfo) => void;\n\tsignal?: AbortSignal;\n}): Promise<OAuthCredentials> {\n\tconst device = await startOpenAICodexDeviceAuth(options.signal);\n\toptions.onDeviceCode({\n\t\tuserCode: device.userCode,\n\t\tverificationUri: DEVICE_VERIFICATION_URI,\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS,\n\t});\n\tconst code = await pollOpenAICodexDeviceAuth(device, options.signal);\n\treturn exchangeAuthorizationCodeForCredentials(\n\t\tcode.authorizationCode,\n\t\tcode.codeVerifier,\n\t\tDEVICE_REDIRECT_URI,\n\t\toptions.signal,\n\t);\n}\n\n/**\n * Login with OpenAI Codex OAuth\n *\n * @param options.onAuth - Called with URL and instructions when auth starts\n * @param options.onPrompt - Called to prompt user for manual code paste (fallback if no onManualCodeInput)\n * @param options.onProgress - Optional progress messages\n * @param options.onManualCodeInput - Optional promise that resolves with user-pasted code.\n * Races with browser callback - whichever completes first wins.\n * Useful for showing paste input immediately alongside browser flow.\n * @param options.originator - OAuth originator parameter (defaults to \"senpi\")\n */\nexport async function loginOpenAICodex(options: {\n\tonAuth: (info: { url: string; instructions?: string }) => void;\n\tonPrompt: (prompt: OAuthPrompt) => Promise<string>;\n\tonProgress?: (message: string) => void;\n\tonManualCodeInput?: () => Promise<string>;\n\toriginator?: string;\n}): Promise<OAuthCredentials> {\n\tconst { verifier, state, url } = await createAuthorizationFlow(options.originator);\n\tconst server = await startLocalOAuthServer(state);\n\n\toptions.onAuth({ url, instructions: \"A browser window should open. Complete login to finish.\" });\n\n\tlet code: string | undefined;\n\ttry {\n\t\tif (options.onManualCodeInput) {\n\t\t\t// Race between browser callback and manual input\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\tserver.cancelWait();\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tmanualError = err instanceof Error ? err : new Error(String(err));\n\t\t\t\t\tserver.cancelWait();\n\t\t\t\t});\n\n\t\t\tconst result = await server.waitForCode();\n\n\t\t\t// If manual input was cancelled, throw that error\n\t\t\tif (manualError) {\n\t\t\t\tthrow manualError;\n\t\t\t}\n\n\t\t\tif (result?.code) {\n\t\t\t\t// Browser callback won\n\t\t\t\tcode = result.code;\n\t\t\t} else if (manualCode) {\n\t\t\t\t// Manual input won (or callback timed out and user had entered code)\n\t\t\t\tconst parsed = parseAuthorizationInput(manualCode);\n\t\t\t\tif (parsed.state && parsed.state !== state) {\n\t\t\t\t\tthrow new Error(\"State mismatch\");\n\t\t\t\t}\n\t\t\t\tcode = parsed.code;\n\t\t\t}\n\n\t\t\t// If still no code, wait for manual promise to complete and try that\n\t\t\tif (!code) {\n\t\t\t\tawait manualPromise;\n\t\t\t\tif (manualError) {\n\t\t\t\t\tthrow manualError;\n\t\t\t\t}\n\t\t\t\tif (manualCode) {\n\t\t\t\t\tconst parsed = parseAuthorizationInput(manualCode);\n\t\t\t\t\tif (parsed.state && parsed.state !== state) {\n\t\t\t\t\t\tthrow new Error(\"State mismatch\");\n\t\t\t\t\t}\n\t\t\t\t\tcode = parsed.code;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Original flow: wait for callback, then prompt if needed\n\t\t\tconst result = await server.waitForCode();\n\t\t\tif (result?.code) {\n\t\t\t\tcode = result.code;\n\t\t\t}\n\t\t}\n\n\t\t// Fallback to onPrompt if still no code\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 = parseAuthorizationInput(input);\n\t\t\tif (parsed.state && parsed.state !== state) {\n\t\t\t\tthrow new Error(\"State mismatch\");\n\t\t\t}\n\t\t\tcode = parsed.code;\n\t\t}\n\n\t\tif (!code) {\n\t\t\tthrow new Error(\"Missing authorization code\");\n\t\t}\n\n\t\treturn exchangeAuthorizationCodeForCredentials(code, verifier, REDIRECT_URI);\n\t} finally {\n\t\tserver.close();\n\t}\n}\n\n/**\n * Refresh OpenAI Codex OAuth token\n */\nexport async function refreshOpenAICodexToken(refreshToken: string): Promise<OAuthCredentials> {\n\treturn credentialsFromToken(await refreshAccessToken(refreshToken));\n}\n\nexport const openaiCodexOAuthProvider: OAuthProviderInterface = {\n\tid: \"openai-codex\",\n\tname: \"ChatGPT Plus/Pro (Codex Subscription)\",\n\tusesCallbackServer: true,\n\n\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\tconst loginMethod = await callbacks.onSelect({\n\t\t\tmessage: \"Select OpenAI Codex login method:\",\n\t\t\toptions: [\n\t\t\t\t{ id: OPENAI_CODEX_BROWSER_LOGIN_METHOD, label: \"Browser login (default)\" },\n\t\t\t\t{ id: OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD, label: \"Device code login (headless)\" },\n\t\t\t],\n\t\t});\n\t\tif (!loginMethod) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\n\t\tif (loginMethod === OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD) {\n\t\t\treturn loginOpenAICodexDeviceCode({\n\t\t\t\tonDeviceCode: callbacks.onDeviceCode,\n\t\t\t\tsignal: callbacks.signal,\n\t\t\t});\n\t\t}\n\n\t\tif (loginMethod !== OPENAI_CODEX_BROWSER_LOGIN_METHOD) {\n\t\t\tthrow new Error(`Unknown OpenAI Codex login method: ${loginMethod}`);\n\t\t}\n\n\t\treturn loginOpenAICodex({\n\t\t\tonAuth: callbacks.onAuth,\n\t\t\tonPrompt: callbacks.onPrompt,\n\t\t\tonProgress: callbacks.onProgress,\n\t\t\tonManualCodeInput: callbacks.onManualCodeInput,\n\t\t});\n\t},\n\n\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\treturn refreshOpenAICodexToken(credentials.refresh);\n\t},\n\n\tgetApiKey(credentials: OAuthCredentials): string {\n\t\treturn credentials.access;\n\t},\n};\n"]}
|
|
1
|
+
{"version":3,"file":"openai-codex.js","sourceRoot":"","sources":["../../../src/utils/oauth/openai-codex.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OpenAI Codex (ChatGPT OAuth) flow\n *\n * NOTE: This module uses Node.js crypto and http for the OAuth callback.\n * It is only intended for CLI use, not browser environments.\n */\n\n// NEVER convert to top-level imports - breaks browser/Vite builds\nlet _randomBytes: typeof import(\"node:crypto\").randomBytes | null = null;\nlet _http: typeof import(\"node:http\") | null = null;\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\timport(\"node:crypto\").then((m) => {\n\t\t_randomBytes = m.randomBytes;\n\t});\n\timport(\"node:http\").then((m) => {\n\t\t_http = m;\n\t});\n}\n\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\nimport type {\n\tOAuthCredentials,\n\tOAuthDeviceCodeInfo,\n\tOAuthLoginCallbacks,\n\tOAuthPrompt,\n\tOAuthProviderInterface,\n} from \"./types.ts\";\n\nconst CLIENT_ID = \"app_EMoamEEZ73f0CkXaXp7hrann\";\nconst AUTH_BASE_URL = \"https://auth.openai.com\";\nconst AUTHORIZE_URL = `${AUTH_BASE_URL}/oauth/authorize`;\nconst TOKEN_URL = `${AUTH_BASE_URL}/oauth/token`;\nconst REDIRECT_URI = \"http://localhost:1455/auth/callback\";\nconst DEVICE_USER_CODE_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/usercode`;\nconst DEVICE_TOKEN_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/token`;\nconst DEVICE_VERIFICATION_URI = `${AUTH_BASE_URL}/codex/device`;\nconst DEVICE_REDIRECT_URI = `${AUTH_BASE_URL}/deviceauth/callback`;\nconst DEVICE_CODE_TIMEOUT_SECONDS = 15 * 60;\nexport const OPENAI_CODEX_BROWSER_LOGIN_METHOD = \"browser\";\nexport const OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD = \"device_code\";\nconst SCOPE = \"openid profile email offline_access\";\nconst JWT_CLAIM_PATH = \"https://api.openai.com/auth\";\n\ntype OAuthToken = { access: string; refresh: string; expires: number };\ntype TokenOperation = \"exchange\" | \"refresh\";\n\nfunction getCallbackHost(): string {\n\treturn typeof process !== \"undefined\" ? process.env.PI_OAUTH_CALLBACK_HOST || \"127.0.0.1\" : \"127.0.0.1\";\n}\n\ntype DeviceAuthInfo = {\n\tdeviceAuthId: string;\n\tuserCode: string;\n\tintervalSeconds: number;\n};\n\ntype DeviceTokenSuccess = {\n\tauthorizationCode: string;\n\tcodeVerifier: 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\nfunction createState(): string {\n\tif (!_randomBytes) {\n\t\tthrow new Error(\"OpenAI Codex OAuth is only available in Node.js environments\");\n\t}\n\treturn _randomBytes(16).toString(\"hex\");\n}\n\nfunction parseAuthorizationInput(input: string): { code?: string; state?: string } {\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\tconst payload = parts[1] ?? \"\";\n\t\tconst decoded = atob(payload);\n\t\treturn JSON.parse(decoded) as JwtPayload;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function fetchWithLoginCancellation(input: string, init: RequestInit): Promise<Response> {\n\ttry {\n\t\treturn await fetch(input, init);\n\t} catch (error) {\n\t\tif (init.signal?.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nasync function readTokenResponse(response: Response, operation: TokenOperation): Promise<OAuthToken> {\n\tif (!response.ok) {\n\t\tconst text = await response.text().catch(() => \"\");\n\t\tthrow new Error(`OpenAI Codex token ${operation} failed (${response.status}): ${text || response.statusText}`);\n\t}\n\n\tconst rawJson = await response.json();\n\tconst json = rawJson as {\n\t\taccess_token?: string;\n\t\trefresh_token?: string;\n\t\texpires_in?: number;\n\t} | null;\n\tif (!json?.access_token || !json.refresh_token || typeof json.expires_in !== \"number\") {\n\t\tthrow new Error(`OpenAI Codex token ${operation} response missing fields: ${JSON.stringify(json)}`);\n\t}\n\n\treturn {\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 exchangeAuthorizationCode(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string = REDIRECT_URI,\n\tsignal?: AbortSignal,\n): Promise<OAuthToken> {\n\tconst response = await fetchWithLoginCancellation(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\tsignal,\n\t});\n\n\treturn readTokenResponse(response, \"exchange\");\n}\n\nasync function refreshAccessToken(refreshToken: string): Promise<OAuthToken> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = 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\t} catch (error) {\n\t\tthrow new Error(`OpenAI Codex token refresh error: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\n\treturn readTokenResponse(response, \"refresh\");\n}\n\nasync function startOpenAICodexDeviceAuth(signal?: AbortSignal): Promise<DeviceAuthInfo> {\n\tconst response = await fetchWithLoginCancellation(DEVICE_USER_CODE_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ client_id: CLIENT_ID }),\n\t\tsignal,\n\t});\n\n\tif (!response.ok) {\n\t\tif (response.status === 404) {\n\t\t\tthrow new Error(\n\t\t\t\t\"OpenAI Codex device code login is not enabled for this server. Use browser login or verify the server URL.\",\n\t\t\t);\n\t\t}\n\t\tconst responseBody = await response.text().catch(() => \"\");\n\t\tthrow new Error(\n\t\t\t`OpenAI Codex device code request failed with status ${response.status}${responseBody ? `: ${responseBody}` : \"\"}`,\n\t\t);\n\t}\n\n\tconst rawJson = await response.json();\n\tconst json = rawJson as {\n\t\tdevice_auth_id?: string;\n\t\tuser_code?: string;\n\t\tinterval?: number | string;\n\t} | null;\n\tconst intervalSeconds = typeof json?.interval === \"string\" ? Number(json.interval.trim()) : json?.interval;\n\tif (\n\t\t!json?.device_auth_id ||\n\t\t!json.user_code ||\n\t\ttypeof intervalSeconds !== \"number\" ||\n\t\t!Number.isFinite(intervalSeconds) ||\n\t\tintervalSeconds < 0\n\t) {\n\t\tthrow new Error(`Invalid OpenAI Codex device code response: ${JSON.stringify(json)}`);\n\t}\n\n\treturn {\n\t\tdeviceAuthId: json.device_auth_id,\n\t\tuserCode: json.user_code,\n\t\tintervalSeconds,\n\t};\n}\n\nasync function pollOpenAICodexDeviceAuth(device: DeviceAuthInfo, signal?: AbortSignal): Promise<DeviceTokenSuccess> {\n\treturn pollOAuthDeviceCodeFlow<DeviceTokenSuccess>({\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS,\n\t\tsignal,\n\t\tpoll: async () => {\n\t\t\tconst response = await fetchWithLoginCancellation(DEVICE_TOKEN_URL, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tdevice_auth_id: device.deviceAuthId,\n\t\t\t\t\tuser_code: device.userCode,\n\t\t\t\t}),\n\t\t\t\tsignal,\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\tconst rawJson = await response.json();\n\t\t\t\tconst json = rawJson as { authorization_code?: string; code_verifier?: string } | null;\n\t\t\t\tif (!json?.authorization_code || !json.code_verifier) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\t\tmessage: `Invalid OpenAI Codex device auth token response: ${JSON.stringify(json)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"complete\",\n\t\t\t\t\tvalue: { authorizationCode: json.authorization_code, codeVerifier: json.code_verifier },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (response.status === 403 || response.status === 404) {\n\t\t\t\treturn { status: \"pending\" };\n\t\t\t}\n\n\t\t\tconst responseBody = await response.text().catch(() => \"\");\n\t\t\tlet errorCode: unknown;\n\t\t\ttry {\n\t\t\t\tconst json = JSON.parse(responseBody) as { error?: string | { code?: string } } | null;\n\t\t\t\tconst error = json?.error;\n\t\t\t\terrorCode = typeof error === \"object\" ? error?.code : error;\n\t\t\t} catch {}\n\n\t\t\tif (errorCode === \"deviceauth_authorization_pending\") {\n\t\t\t\treturn { status: \"pending\" };\n\t\t\t}\n\t\t\tif (errorCode === \"slow_down\") {\n\t\t\t\treturn { status: \"slow_down\" };\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tmessage: `OpenAI Codex device auth failed with status ${response.status}${responseBody ? `: ${responseBody}` : \"\"}`,\n\t\t\t};\n\t\t},\n\t});\n}\n\nasync function createAuthorizationFlow(\n\toriginator: string = \"senpi\",\n): Promise<{ verifier: string; state: string; url: string }> {\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\", REDIRECT_URI);\n\turl.searchParams.set(\"scope\", 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\", originator);\n\n\treturn { verifier, state, url: url.toString() };\n}\n\ntype OAuthServerInfo = {\n\tclose: () => void;\n\tcancelWait: () => void;\n\twaitForCode: () => Promise<{ code: string } | null>;\n};\n\nfunction startLocalOAuthServer(state: string): Promise<OAuthServerInfo> {\n\tif (!_http) {\n\t\tthrow new Error(\"OpenAI Codex OAuth 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\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\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\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 {\n\t\t\tres.statusCode = 500;\n\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\tres.end(oauthErrorHtml(\"Internal error while processing OAuth callback.\"));\n\t\t}\n\t});\n\n\treturn new Promise((resolve) => {\n\t\tserver\n\t\t\t.listen(1455, getCallbackHost(), () => {\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\", (_err: NodeJS.ErrnoException) => {\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\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\nfunction getAccountId(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\nfunction credentialsFromToken(token: OAuthToken): OAuthCredentials {\n\tconst accountId = getAccountId(token.access);\n\tif (!accountId) {\n\t\tthrow new Error(\"Failed to extract accountId from token\");\n\t}\n\n\treturn {\n\t\taccess: token.access,\n\t\trefresh: token.refresh,\n\t\texpires: token.expires,\n\t\taccountId,\n\t};\n}\n\nasync function exchangeAuthorizationCodeForCredentials(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string,\n\tsignal?: AbortSignal,\n): Promise<OAuthCredentials> {\n\treturn credentialsFromToken(await exchangeAuthorizationCode(code, verifier, redirectUri, signal));\n}\n\n/**\n * Login with OpenAI Codex OAuth using the Codex device-code flow.\n */\nexport async function loginOpenAICodexDeviceCode(options: {\n\tonDeviceCode: (info: OAuthDeviceCodeInfo) => void;\n\tsignal?: AbortSignal;\n}): Promise<OAuthCredentials> {\n\tconst device = await startOpenAICodexDeviceAuth(options.signal);\n\toptions.onDeviceCode({\n\t\tuserCode: device.userCode,\n\t\tverificationUri: DEVICE_VERIFICATION_URI,\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS,\n\t});\n\tconst code = await pollOpenAICodexDeviceAuth(device, options.signal);\n\treturn exchangeAuthorizationCodeForCredentials(\n\t\tcode.authorizationCode,\n\t\tcode.codeVerifier,\n\t\tDEVICE_REDIRECT_URI,\n\t\toptions.signal,\n\t);\n}\n\n/**\n * Login with OpenAI Codex OAuth\n *\n * @param options.onAuth - Called with URL and instructions when auth starts\n * @param options.onPrompt - Called to prompt user for manual code paste (fallback if no onManualCodeInput)\n * @param options.onProgress - Optional progress messages\n * @param options.onManualCodeInput - Optional promise that resolves with user-pasted code.\n * Races with browser callback - whichever completes first wins.\n * Useful for showing paste input immediately alongside browser flow.\n * @param options.originator - OAuth originator parameter (defaults to \"senpi\")\n */\nexport async function loginOpenAICodex(options: {\n\tonAuth: (info: { url: string; instructions?: string }) => void;\n\tonPrompt: (prompt: OAuthPrompt) => Promise<string>;\n\tonProgress?: (message: string) => void;\n\tonManualCodeInput?: () => Promise<string>;\n\toriginator?: string;\n}): Promise<OAuthCredentials> {\n\tconst { verifier, state, url } = await createAuthorizationFlow(options.originator);\n\tconst server = await startLocalOAuthServer(state);\n\n\toptions.onAuth({ url, instructions: \"A browser window should open. Complete login to finish.\" });\n\n\tlet code: string | undefined;\n\ttry {\n\t\tif (options.onManualCodeInput) {\n\t\t\t// Race between browser callback and manual input\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\tserver.cancelWait();\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tmanualError = err instanceof Error ? err : new Error(String(err));\n\t\t\t\t\tserver.cancelWait();\n\t\t\t\t});\n\n\t\t\tconst result = await server.waitForCode();\n\n\t\t\t// If manual input was cancelled, throw that error\n\t\t\tif (manualError) {\n\t\t\t\tthrow manualError;\n\t\t\t}\n\n\t\t\tif (result?.code) {\n\t\t\t\t// Browser callback won\n\t\t\t\tcode = result.code;\n\t\t\t} else if (manualCode) {\n\t\t\t\t// Manual input won (or callback timed out and user had entered code)\n\t\t\t\tconst parsed = parseAuthorizationInput(manualCode);\n\t\t\t\tif (parsed.state && parsed.state !== state) {\n\t\t\t\t\tthrow new Error(\"State mismatch\");\n\t\t\t\t}\n\t\t\t\tcode = parsed.code;\n\t\t\t}\n\n\t\t\t// If still no code, wait for manual promise to complete and try that\n\t\t\tif (!code) {\n\t\t\t\tawait manualPromise;\n\t\t\t\tif (manualError) {\n\t\t\t\t\tthrow manualError;\n\t\t\t\t}\n\t\t\t\tif (manualCode) {\n\t\t\t\t\tconst parsed = parseAuthorizationInput(manualCode);\n\t\t\t\t\tif (parsed.state && parsed.state !== state) {\n\t\t\t\t\t\tthrow new Error(\"State mismatch\");\n\t\t\t\t\t}\n\t\t\t\t\tcode = parsed.code;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Original flow: wait for callback, then prompt if needed\n\t\t\tconst result = await server.waitForCode();\n\t\t\tif (result?.code) {\n\t\t\t\tcode = result.code;\n\t\t\t}\n\t\t}\n\n\t\t// Fallback to onPrompt if still no code\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 = parseAuthorizationInput(input);\n\t\t\tif (parsed.state && parsed.state !== state) {\n\t\t\t\tthrow new Error(\"State mismatch\");\n\t\t\t}\n\t\t\tcode = parsed.code;\n\t\t}\n\n\t\tif (!code) {\n\t\t\tthrow new Error(\"Missing authorization code\");\n\t\t}\n\n\t\treturn exchangeAuthorizationCodeForCredentials(code, verifier, REDIRECT_URI);\n\t} finally {\n\t\tserver.close();\n\t}\n}\n\n/**\n * Refresh OpenAI Codex OAuth token\n */\nexport async function refreshOpenAICodexToken(refreshToken: string): Promise<OAuthCredentials> {\n\treturn credentialsFromToken(await refreshAccessToken(refreshToken));\n}\n\nexport const openaiCodexOAuthProvider: OAuthProviderInterface = {\n\tid: \"openai-codex\",\n\tname: \"ChatGPT Plus/Pro (Codex Subscription)\",\n\tusesCallbackServer: true,\n\n\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\tconst loginMethod = await callbacks.onSelect({\n\t\t\tmessage: \"Select OpenAI Codex login method:\",\n\t\t\toptions: [\n\t\t\t\t{ id: OPENAI_CODEX_BROWSER_LOGIN_METHOD, label: \"Browser login (default)\" },\n\t\t\t\t{ id: OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD, label: \"Device code login (headless)\" },\n\t\t\t],\n\t\t});\n\t\tif (!loginMethod) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\n\t\tif (loginMethod === OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD) {\n\t\t\treturn loginOpenAICodexDeviceCode({\n\t\t\t\tonDeviceCode: callbacks.onDeviceCode,\n\t\t\t\tsignal: callbacks.signal,\n\t\t\t});\n\t\t}\n\n\t\tif (loginMethod !== OPENAI_CODEX_BROWSER_LOGIN_METHOD) {\n\t\t\tthrow new Error(`Unknown OpenAI Codex login method: ${loginMethod}`);\n\t\t}\n\n\t\treturn loginOpenAICodex({\n\t\t\tonAuth: callbacks.onAuth,\n\t\t\tonPrompt: callbacks.onPrompt,\n\t\t\tonProgress: callbacks.onProgress,\n\t\t\tonManualCodeInput: callbacks.onManualCodeInput,\n\t\t});\n\t},\n\n\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\treturn refreshOpenAICodexToken(credentials.refresh);\n\t},\n\n\tgetApiKey(credentials: OAuthCredentials): string {\n\t\treturn credentials.access;\n\t},\n};\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@earendil-works/pi-tui",
|
|
3
3
|
"private": true,
|
|
4
|
-
"version": "2026.6.
|
|
4
|
+
"version": "2026.6.4",
|
|
5
5
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi",
|
|
3
|
-
"version": "2026.6.
|
|
3
|
+
"version": "2026.6.4",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@code-yeongyu/senpi",
|
|
9
|
-
"version": "2026.6.
|
|
9
|
+
"version": "2026.6.4",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@anthropic-ai/sdk": "0.91.1",
|
|
13
13
|
"@aws-sdk/client-bedrock-runtime": "3.1048.0",
|
|
14
|
-
"@earendil-works/pi-agent-core": "^2026.6.
|
|
15
|
-
"@earendil-works/pi-ai": "^2026.6.
|
|
16
|
-
"@earendil-works/pi-tui": "^2026.6.
|
|
14
|
+
"@earendil-works/pi-agent-core": "^2026.6.4",
|
|
15
|
+
"@earendil-works/pi-ai": "^2026.6.4",
|
|
16
|
+
"@earendil-works/pi-tui": "^2026.6.4",
|
|
17
17
|
"@mistralai/mistralai": "2.2.1",
|
|
18
18
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
19
19
|
"@smithy/node-http-handler": "4.7.3",
|
|
@@ -485,11 +485,11 @@
|
|
|
485
485
|
}
|
|
486
486
|
},
|
|
487
487
|
"node_modules/@earendil-works/pi-agent-core": {
|
|
488
|
-
"version": "2026.6.
|
|
489
|
-
"resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-2026.6.
|
|
488
|
+
"version": "2026.6.4",
|
|
489
|
+
"resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-2026.6.4.tgz",
|
|
490
490
|
"license": "MIT",
|
|
491
491
|
"dependencies": {
|
|
492
|
-
"@earendil-works/pi-ai": "^2026.6.
|
|
492
|
+
"@earendil-works/pi-ai": "^2026.6.4",
|
|
493
493
|
"ignore": "7.0.5",
|
|
494
494
|
"typebox": "1.1.38",
|
|
495
495
|
"yaml": "2.9.0"
|
|
@@ -499,8 +499,8 @@
|
|
|
499
499
|
}
|
|
500
500
|
},
|
|
501
501
|
"node_modules/@earendil-works/pi-ai": {
|
|
502
|
-
"version": "2026.6.
|
|
503
|
-
"resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-2026.6.
|
|
502
|
+
"version": "2026.6.4",
|
|
503
|
+
"resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-2026.6.4.tgz",
|
|
504
504
|
"license": "MIT",
|
|
505
505
|
"dependencies": {
|
|
506
506
|
"@anthropic-ai/sdk": "0.91.1",
|
|
@@ -526,8 +526,8 @@
|
|
|
526
526
|
}
|
|
527
527
|
},
|
|
528
528
|
"node_modules/@earendil-works/pi-tui": {
|
|
529
|
-
"version": "2026.6.
|
|
530
|
-
"resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-2026.6.
|
|
529
|
+
"version": "2026.6.4",
|
|
530
|
+
"resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-2026.6.4.tgz",
|
|
531
531
|
"license": "MIT",
|
|
532
532
|
"dependencies": {
|
|
533
533
|
"get-east-asian-width": "1.6.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi",
|
|
3
|
-
"version": "2026.6.
|
|
3
|
+
"version": "2026.6.4",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"dist",
|
|
27
27
|
"docs",
|
|
28
28
|
"examples",
|
|
29
|
+
"containerization.md",
|
|
29
30
|
"CHANGELOG.md",
|
|
30
31
|
"npm-shrinkwrap.json"
|
|
31
32
|
],
|
|
@@ -43,9 +44,9 @@
|
|
|
43
44
|
"dependencies": {
|
|
44
45
|
"@anthropic-ai/sdk": "0.91.1",
|
|
45
46
|
"@aws-sdk/client-bedrock-runtime": "3.1048.0",
|
|
46
|
-
"@earendil-works/pi-agent-core": "^2026.6.
|
|
47
|
-
"@earendil-works/pi-ai": "^2026.6.
|
|
48
|
-
"@earendil-works/pi-tui": "^2026.6.
|
|
47
|
+
"@earendil-works/pi-agent-core": "^2026.6.4",
|
|
48
|
+
"@earendil-works/pi-ai": "^2026.6.4",
|
|
49
|
+
"@earendil-works/pi-tui": "^2026.6.4",
|
|
49
50
|
"@mistralai/mistralai": "2.2.1",
|
|
50
51
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
51
52
|
"@smithy/node-http-handler": "4.7.3",
|