import { r as fetchWithSsrFGuard } from "./fetch-guard-B9YUBy5O.js";
import { a as oauthErrorHtml, c as resolveOAuthTokenExpiresAt, d as withOAuthLoginAbort, i as generatePKCE, l as resolveOAuthTokenLifetimeMs, n as createOAuthLoginCancelledError, o as oauthSuccessHtml, s as parseOAuthAuthorizationInput, u as throwIfOAuthLoginAborted } from "./provider-oauth-runtime-CHge3JIZ.js";
import "./ssrf-runtime-Ca9EhZRd.js";
import { n as resolveCodexAuthIdentity } from "./openai-chatgpt-auth-identity-BzMaAd2-.js";
import "./openai-chatgpt-oauth-abort.runtime.js";
import "./openai-chatgpt-oauth-page.runtime.js";
import "./openai-chatgpt-pkce.runtime.js";
//#region extensions/openai/openai-chatgpt-oauth-flow.runtime.ts
/**
* OpenAI Codex (ChatGPT OAuth) flow
*
* NOTE: This module uses Node.js crypto and http for the OAuth callback.
* It is only intended for CLI use, not browser environments.
*/
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
const AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
const TOKEN_URL = "https://auth.openai.com/oauth/token";
const CALLBACK_PORT = 1455;
const CALLBACK_PATH = "/auth/callback";
const DEFAULT_CALLBACK_HOST = "localhost";
const LOOPBACK_CALLBACK_HOSTS = new Set([
	"localhost",
	"127.0.0.1",
	"::1"
]);
const CALLBACK_HOST = resolveCallbackHost();
const REDIRECT_URI = resolveRedirectUri(CALLBACK_HOST);
const MANUAL_PROMPT_FALLBACK_MS = 15e3;
const TOKEN_REQUEST_TIMEOUT_MS = 3e4;
const SCOPE = "openid profile email offline_access";
let nodeOAuthRuntimePromise = null;
function loadNodeOAuthRuntime() {
	if (typeof process === "undefined" || !process.versions?.node && !process.versions?.bun) return Promise.reject(/* @__PURE__ */ new Error("OpenAI Codex OAuth is only available in Node.js environments"));
	nodeOAuthRuntimePromise ??= Promise.all([import("node:crypto"), import("node:http")]).then(([cryptoModule, httpModule]) => ({
		randomBytes: cryptoModule.randomBytes,
		http: httpModule
	}));
	return nodeOAuthRuntimePromise;
}
function resolveCallbackHost(env = process.env) {
	const host = env.OPENCLAW_OAUTH_CALLBACK_HOST?.trim() || DEFAULT_CALLBACK_HOST;
	if (!LOOPBACK_CALLBACK_HOSTS.has(host)) throw new Error("OpenAI Codex OAuth callback host must be localhost, 127.0.0.1, or ::1");
	return host;
}
function resolveRedirectUri(host = CALLBACK_HOST) {
	const url = new URL(`http://${host === "::1" ? "[::1]" : host}:${CALLBACK_PORT}`);
	url.pathname = CALLBACK_PATH;
	return url.toString();
}
function createState(randomBytes) {
	return randomBytes(16).toString("hex");
}
function waitForManualPromptFallback(signal) {
	return new Promise((resolve, reject) => {
		if (signal?.aborted) {
			reject(createOAuthLoginCancelledError());
			return;
		}
		const cleanup = () => {
			signal?.removeEventListener("abort", abort);
		};
		const abort = () => {
			clearTimeout(timeout);
			cleanup();
			reject(createOAuthLoginCancelledError());
		};
		const timeout = setTimeout(() => {
			cleanup();
			resolve(null);
		}, MANUAL_PROMPT_FALLBACK_MS);
		signal?.addEventListener("abort", abort, { once: true });
		timeout.unref?.();
	});
}
async function promptForAuthorizationCode(onPrompt, state) {
	const parsed = parseOAuthAuthorizationInput(await onPrompt({ message: "Paste the authorization code (or full redirect URL):" }));
	if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
	return parsed.code;
}
function formatMissingTokenResponseFields(json) {
	const missing = [];
	if (!json.access_token) missing.push("access_token");
	if (!json.refresh_token) missing.push("refresh_token");
	if (resolveOAuthTokenLifetimeMs(json.expires_in) === void 0) missing.push("expires_in");
	return missing.join(", ");
}
function formatTokenRequestError(operation, error, timeoutMs, signal) {
	if (signal?.aborted) return "Login cancelled";
	if (error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError")) return `OpenAI Codex token ${operation} timed out after ${timeoutMs}ms`;
	return `OpenAI Codex token ${operation} error: ${error instanceof Error ? error.message : String(error)}`;
}
async function postTokenForm(body, options = {}) {
	const timeoutMs = options.timeoutMs ?? TOKEN_REQUEST_TIMEOUT_MS;
	throwIfOAuthLoginAborted(options.signal);
	const { response, release } = await fetchWithSsrFGuard({
		url: TOKEN_URL,
		init: {
			method: "POST",
			headers: { "Content-Type": "application/x-www-form-urlencoded" },
			body
		},
		timeoutMs,
		signal: options.signal,
		auditContext: "openai-chatgpt-oauth-token"
	});
	try {
		const responseBody = await response.arrayBuffer();
		return new Response(responseBody, {
			status: response.status,
			statusText: response.statusText,
			headers: response.headers
		});
	} finally {
		await release();
	}
}
async function exchangeAuthorizationCode(code, verifier, redirectUri = REDIRECT_URI, options = {}) {
	const timeoutMs = options.timeoutMs ?? TOKEN_REQUEST_TIMEOUT_MS;
	let response;
	try {
		response = await postTokenForm(new URLSearchParams({
			grant_type: "authorization_code",
			client_id: CLIENT_ID,
			code,
			code_verifier: verifier,
			redirect_uri: redirectUri
		}), {
			signal: options.signal,
			timeoutMs
		});
	} catch (error) {
		return {
			type: "failed",
			message: formatTokenRequestError("exchange", error, timeoutMs, options.signal)
		};
	}
	if (!response.ok) {
		const text = await response.text().catch(() => "");
		return {
			type: "failed",
			status: response.status,
			message: `OpenAI Codex token exchange failed (${response.status}): ${text || response.statusText}`
		};
	}
	const json = await response.json();
	const expires = resolveOAuthTokenExpiresAt(json.expires_in);
	if (!json.access_token || !json.refresh_token || expires === void 0) return {
		type: "failed",
		message: `OpenAI Codex token exchange response missing fields: ${formatMissingTokenResponseFields(json)}`
	};
	return {
		type: "success",
		access: json.access_token,
		refresh: json.refresh_token,
		expires
	};
}
async function refreshAccessToken(refreshToken, options = {}) {
	try {
		const timeoutMs = options.timeoutMs ?? TOKEN_REQUEST_TIMEOUT_MS;
		const response = await postTokenForm(new URLSearchParams({
			grant_type: "refresh_token",
			refresh_token: refreshToken,
			client_id: CLIENT_ID
		}), {
			signal: options.signal,
			timeoutMs
		});
		if (!response.ok) {
			const text = await response.text().catch(() => "");
			return {
				type: "failed",
				status: response.status,
				message: `OpenAI Codex token refresh failed (${response.status}): ${text || response.statusText}`
			};
		}
		const json = await response.json();
		const expires = resolveOAuthTokenExpiresAt(json.expires_in);
		if (!json.access_token || !json.refresh_token || expires === void 0) return {
			type: "failed",
			message: `OpenAI Codex token refresh response missing fields: ${formatMissingTokenResponseFields(json)}`
		};
		return {
			type: "success",
			access: json.access_token,
			refresh: json.refresh_token,
			expires
		};
	} catch (error) {
		return {
			type: "failed",
			message: formatTokenRequestError("refresh", error, options.timeoutMs ?? TOKEN_REQUEST_TIMEOUT_MS, options.signal)
		};
	}
}
async function createAuthorizationFlow(originator = "openclaw") {
	const [{ verifier, challenge }, runtime] = await Promise.all([generatePKCE(), loadNodeOAuthRuntime()]);
	const state = createState(runtime.randomBytes);
	const url = new URL(AUTHORIZE_URL);
	url.searchParams.set("response_type", "code");
	url.searchParams.set("client_id", CLIENT_ID);
	const redirectUri = REDIRECT_URI;
	url.searchParams.set("redirect_uri", redirectUri);
	url.searchParams.set("scope", SCOPE);
	url.searchParams.set("code_challenge", challenge);
	url.searchParams.set("code_challenge_method", "S256");
	url.searchParams.set("state", state);
	url.searchParams.set("id_token_add_organizations", "true");
	url.searchParams.set("codex_cli_simplified_flow", "true");
	url.searchParams.set("originator", originator);
	return {
		verifier,
		redirectUri,
		state,
		url: url.toString()
	};
}
async function startLocalOAuthServer(state) {
	const { http } = await loadNodeOAuthRuntime();
	let settleWait;
	const waitForCodePromise = new Promise((resolve) => {
		let settled = false;
		settleWait = (value) => {
			if (settled) return;
			settled = true;
			resolve(value);
		};
	});
	const server = http.createServer((req, res) => {
		try {
			const url = new URL(req.url || "", "http://localhost");
			if (url.pathname !== "/auth/callback") {
				res.statusCode = 404;
				res.setHeader("Content-Type", "text/html; charset=utf-8");
				res.end(oauthErrorHtml("Callback route not found."));
				return;
			}
			if (url.searchParams.get("state") !== state) {
				res.statusCode = 400;
				res.setHeader("Content-Type", "text/html; charset=utf-8");
				res.end(oauthErrorHtml("State mismatch."));
				return;
			}
			const code = url.searchParams.get("code");
			if (!code) {
				res.statusCode = 400;
				res.setHeader("Content-Type", "text/html; charset=utf-8");
				res.end(oauthErrorHtml("Missing authorization code."));
				return;
			}
			res.statusCode = 200;
			res.setHeader("Content-Type", "text/html; charset=utf-8");
			res.end(oauthSuccessHtml("OpenAI authentication completed. You can close this window."));
			settleWait?.({ code });
		} catch {
			res.statusCode = 500;
			res.setHeader("Content-Type", "text/html; charset=utf-8");
			res.end(oauthErrorHtml("Internal error while processing OAuth callback."));
		}
	});
	return new Promise((resolve) => {
		server.listen(CALLBACK_PORT, CALLBACK_HOST, () => {
			resolve({
				close: () => server.close(),
				cancelWait: () => {
					settleWait?.(null);
				},
				waitForCode: () => waitForCodePromise
			});
		}).on("error", () => {
			settleWait?.(null);
			resolve({
				close: () => {
					try {
						server.close();
					} catch {}
				},
				cancelWait: () => {},
				waitForCode: async () => null
			});
		});
	});
}
function getAccountId(accessToken) {
	const accountId = resolveCodexAuthIdentity({ accessToken }).accountId;
	return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
}
/**
* Login with OpenAI Codex OAuth
*
* @param options.onAuth - Called with URL and instructions when auth starts
* @param options.onPrompt - Called to prompt user for manual code paste (fallback if no onManualCodeInput)
* @param options.onProgress - Optional progress messages
* @param options.onManualCodeInput - Optional promise that resolves with user-pasted code.
*                                    Races with browser callback - whichever completes first wins.
*                                    Useful for showing paste input immediately alongside browser flow.
* @param options.originator - OAuth originator parameter (defaults to "openclaw")
*/
async function loginOpenAICodex(options) {
	throwIfOAuthLoginAborted(options.signal);
	const { verifier, redirectUri, state, url } = await createAuthorizationFlow(options.originator);
	const server = await startLocalOAuthServer(state);
	let code;
	try {
		throwIfOAuthLoginAborted(options.signal);
		options.onAuth({
			url,
			instructions: "A browser window should open. Complete login to finish."
		});
		throwIfOAuthLoginAborted(options.signal);
		if (options.onManualCodeInput) {
			let manualCode;
			let manualError;
			const manualPromise = options.onManualCodeInput().then((input) => {
				manualCode = input;
				server.cancelWait();
			}).catch((err) => {
				manualError = err instanceof Error ? err : new Error(String(err));
				server.cancelWait();
			});
			const result = await withOAuthLoginAbort(server.waitForCode(), options.signal, server.cancelWait);
			if (manualError) throw manualError;
			if (result?.code) code = result.code;
			else if (manualCode) {
				const parsed = parseOAuthAuthorizationInput(manualCode);
				if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
				code = parsed.code;
			}
			if (!code) {
				await withOAuthLoginAbort(manualPromise, options.signal, server.cancelWait);
				if (manualError) throw toLintErrorObject(manualError, "Non-Error thrown");
				if (manualCode) {
					const parsed = parseOAuthAuthorizationInput(manualCode);
					if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
					code = parsed.code;
				}
			}
		} else {
			const callbackPromise = server.waitForCode();
			const result = await withOAuthLoginAbort(Promise.race([callbackPromise, waitForManualPromptFallback(options.signal)]), options.signal, server.cancelWait);
			if (result?.code) code = result.code;
			else {
				const promptCodePromise = promptForAuthorizationCode(options.onPrompt, state).then((promptCode) => {
					server.cancelWait();
					return promptCode;
				});
				code = await withOAuthLoginAbort(Promise.race([callbackPromise.then((callback) => callback?.code), promptCodePromise]), options.signal, server.cancelWait);
			}
		}
		if (!code) code = await withOAuthLoginAbort(promptForAuthorizationCode(options.onPrompt, state), options.signal, server.cancelWait);
		if (!code) throw new Error("Missing authorization code");
		const tokenResult = await exchangeAuthorizationCode(code, verifier, redirectUri, { signal: options.signal });
		if (tokenResult.type !== "success") throw new Error(tokenResult.message);
		const accountId = getAccountId(tokenResult.access);
		if (!accountId) throw new Error("Failed to extract accountId from token");
		return {
			access: tokenResult.access,
			refresh: tokenResult.refresh,
			expires: tokenResult.expires,
			accountId
		};
	} finally {
		server.close();
	}
}
/**
* Refresh OpenAI Codex OAuth token
*/
async function refreshOpenAICodexToken(refreshToken) {
	const result = await refreshAccessToken(refreshToken);
	if (result.type !== "success") throw new Error(result.message);
	const accountId = getAccountId(result.access);
	if (!accountId) throw new Error("Failed to extract accountId from token");
	return {
		access: result.access,
		refresh: result.refresh,
		expires: result.expires,
		accountId
	};
}
const openaiCodexOAuthProvider = {
	id: "openai",
	name: "ChatGPT Plus/Pro (Codex Subscription)",
	usesCallbackServer: true,
	async login(callbacks) {
		return loginOpenAICodex({
			onAuth: callbacks.onAuth,
			onPrompt: callbacks.onPrompt,
			onProgress: callbacks.onProgress,
			onManualCodeInput: callbacks.onManualCodeInput,
			signal: callbacks.signal
		});
	},
	async refreshToken(credentials) {
		return refreshOpenAICodexToken(credentials.refresh);
	},
	getApiKey(credentials) {
		return credentials.access;
	}
};
const testing = {
	callbackHost: CALLBACK_HOST,
	createAuthorizationFlow,
	exchangeAuthorizationCode,
	loginOpenAICodex,
	refreshAccessToken,
	resolveCallbackHost,
	resolveRedirectUri
};
function toLintErrorObject(value, fallbackMessage) {
	if (value instanceof Error) return value;
	if (typeof value === "string") return new Error(value);
	const error = new Error(fallbackMessage, { cause: value });
	if (typeof value === "object" && value !== null || typeof value === "function") Object.assign(error, value);
	return error;
}
//#endregion
export { testing as i, openaiCodexOAuthProvider as n, refreshOpenAICodexToken as r, loginOpenAICodex as t };
