import { i as __exportAll } from "./tool-display-DMBmPBI-.js";
import { x as DEFAULT_OAUTH_CALLBACK_PORT } from "./paths-WcYpcMAY.js";
import { o as createSubsystemLogger, r as logAcceptedEnvOption } from "./entry.js";
import { t as runCommandWithTimeout } from "./exec-CmvuUufz.js";
import { o as loadConfig } from "./config-CoCeK5B2.js";
import { a as loadSessionStore, u as updateSessionStore } from "./sessions-CFJLtXRH.js";
import { n as routeReply } from "./route-reply-0Dr2Q7BS.js";
import { t as resolveDefaultSessionStorePath } from "./paths-CDTtU20P.js";
import { a as traceGatewayEvent } from "./chat-trace-C0wGIaWO.js";
import { a as getGoogleClientType, d as syncToGogKeyring, i as getGoogleClientSecret, n as getCredentialsDir, o as getLegacyCredentialsDir, r as getGoogleClientId, t as buildCredentialFilename, u as setGoogleClientCredentialsFile } from "./gog-credentials-tQ9F8HpD.js";
import { t as upsertSharedEnvVar } from "./env-file-Dk0_93lQ.js";
import fs from "fs/promises";
import path from "path";
import crypto from "crypto";
import http from "http";
import { URL as URL$1 } from "url";

//#region src/hooks/gog-oauth-types.ts
/**
* Google OAuth scope mappings
*/
const GOOGLE_SERVICE_SCOPES = {
	gmail: [
		"https://www.googleapis.com/auth/gmail.readonly",
		"https://www.googleapis.com/auth/gmail.send",
		"https://www.googleapis.com/auth/gmail.modify",
		"https://www.googleapis.com/auth/gmail.labels"
	],
	calendar: ["https://www.googleapis.com/auth/calendar.readonly", "https://www.googleapis.com/auth/calendar.events"],
	drive: ["https://www.googleapis.com/auth/drive.readonly", "https://www.googleapis.com/auth/drive.file"],
	contacts: ["https://www.googleapis.com/auth/contacts.readonly"],
	docs: ["https://www.googleapis.com/auth/documents.readonly", "https://www.googleapis.com/auth/documents"],
	sheets: ["https://www.googleapis.com/auth/spreadsheets.readonly", "https://www.googleapis.com/auth/spreadsheets"]
};
/**
* Get OAuth scopes for requested services
*/
function getScopesForServices(services) {
	const scopes = /* @__PURE__ */ new Set();
	for (const service of services) {
		const serviceScopes = GOOGLE_SERVICE_SCOPES[service];
		if (serviceScopes) for (const scope of serviceScopes) scopes.add(scope);
	}
	return Array.from(scopes);
}
/**
* Reverse-map a granted scope string back to service names.
* A service counts as granted if at least one of its scopes was approved.
*/
function getServicesFromScopes(grantedScope) {
	const granted = new Set(grantedScope.split(" "));
	const services = [];
	for (const [service, scopes] of Object.entries(GOOGLE_SERVICE_SCOPES)) if (scopes.some((s) => granted.has(s))) services.push(service);
	return services;
}

//#endregion
//#region src/auth/google/google-auth-provider.ts
/**
* Google OAuth provider implementation.
* Implements the AuthProvider interface for Google OAuth flows.
*
* Extracts Google-specific logic (URL building, token exchange, scope mapping,
* credential storage) behind the provider abstraction so future providers
* (Microsoft, GitHub) can implement the same interface.
*/
const log$2 = createSubsystemLogger("google-auth-provider");
/**
* Create a Google OAuth provider instance.
* Uses factory function pattern per codebase convention.
*/
function createGoogleAuthProvider() {
	return {
		id: "google",
		displayName: "Google",
		buildAuthUrl(params) {
			const clientId = getGoogleClientId();
			const scopes = getScopesForServices(params.services);
			const authUrl = new URL("https://accounts.google.com/o/oauth2/v2/auth");
			authUrl.searchParams.set("client_id", clientId);
			authUrl.searchParams.set("redirect_uri", params.redirectUri);
			authUrl.searchParams.set("response_type", "code");
			authUrl.searchParams.set("scope", scopes.join(" "));
			authUrl.searchParams.set("state", params.state);
			authUrl.searchParams.set("access_type", "offline");
			authUrl.searchParams.set("prompt", "consent");
			authUrl.searchParams.set("login_hint", params.email);
			return authUrl.toString();
		},
		async exchangeCode(code, redirectUri) {
			const clientId = getGoogleClientId();
			const clientSecret = getGoogleClientSecret();
			const response = await fetch("https://oauth2.googleapis.com/token", {
				method: "POST",
				headers: { "Content-Type": "application/x-www-form-urlencoded" },
				body: new URLSearchParams({
					code,
					client_id: clientId,
					client_secret: clientSecret,
					redirect_uri: redirectUri,
					grant_type: "authorization_code"
				})
			});
			if (!response.ok) {
				const error = await response.text();
				throw new Error(`Token exchange failed (${response.status}): ${error}`);
			}
			return await response.json();
		},
		getScopesForServices(services) {
			return getScopesForServices(services);
		},
		getSupportedServices() {
			return Object.keys(GOOGLE_SERVICE_SCOPES);
		},
		async storeCredentials(params) {
			const dir = getCredentialsDir(params.agentId);
			await fs.mkdir(dir, {
				recursive: true,
				mode: 448
			});
			const filename = buildCredentialFilename(params.sessionKey, params.email);
			const credPath = path.join(dir, filename);
			const credentials = {
				email: params.email,
				accessToken: params.tokens.access_token,
				refreshToken: params.tokens.refresh_token ?? "",
				expiresAt: Date.now() + params.tokens.expires_in * 1e3,
				createdAt: Date.now(),
				sessionKey: params.sessionKey,
				agentId: params.agentId,
				services: params.services,
				filePath: credPath
			};
			await fs.writeFile(credPath, JSON.stringify(credentials, null, 2), { mode: 384 });
			await syncToGogKeyring(credentials);
			return credPath;
		},
		async loadCredentials(agentId, sessionKey, email) {
			const filename = buildCredentialFilename(sessionKey, email);
			const newDir = getCredentialsDir(agentId);
			const newPath = path.join(newDir, filename);
			try {
				const data = await fs.readFile(newPath, "utf-8");
				const creds = JSON.parse(data);
				creds.filePath = newPath;
				return creds;
			} catch {}
			const oldDir = getLegacyCredentialsDir(agentId);
			const oldPath = path.join(oldDir, filename);
			try {
				const data = await fs.readFile(oldPath, "utf-8");
				const creds = JSON.parse(data);
				log$2.info(`Migrating credentials from gog-credentials/ to auth-credentials/google/ for ${email}`);
				await fs.mkdir(newDir, {
					recursive: true,
					mode: 448
				});
				await fs.writeFile(newPath, data, { mode: 384 });
				try {
					await fs.access(newPath);
					await fs.unlink(oldPath);
					log$2.info(`Migration complete: deleted legacy credential at ${oldPath}`);
				} catch {
					log$2.warn(`Migration: new file written but could not delete old file at ${oldPath}`);
				}
				creds.filePath = newPath;
				return creds;
			} catch {}
			return null;
		}
	};
}

//#endregion
//#region src/hooks/gog-oauth-notifications.ts
/**
* OAuth notification system for async auth flow updates.
*
* Sends notifications directly via routeReply (no LLM round-trip).
*/
const log$1 = createSubsystemLogger("gog-oauth/notifications");
/**
* Send an OAuth notification directly to the originating channel.
*/
async function sendOAuthNotification(notification) {
	log$1.info(`Sending OAuth ${notification.type} notification for ${notification.email} (session=${notification.sessionKey})`);
	const sessionEntry = loadSessionStore(resolveDefaultSessionStorePath(notification.agentId))[notification.sessionKey];
	if (!sessionEntry) {
		log$1.error(`Cannot send OAuth notification: session ${notification.sessionKey} not found`);
		return;
	}
	const channel = sessionEntry.lastChannel ?? sessionEntry.deliveryContext?.channel;
	const to = sessionEntry.lastTo ?? sessionEntry.deliveryContext?.to;
	const accountId = sessionEntry.lastAccountId ?? sessionEntry.deliveryContext?.accountId;
	const threadId = sessionEntry.lastThreadId ?? sessionEntry.deliveryContext?.threadId;
	if (!channel || !to) {
		log$1.error(`Cannot route OAuth notification for session ${notification.sessionKey}: no channel/to (lastChannel=${sessionEntry.lastChannel}, deliveryContext.channel=${sessionEntry.deliveryContext?.channel})`);
		return;
	}
	log$1.info(`Routing OAuth notification to ${channel}:${to}`);
	const cfg = loadConfig();
	const maxAttempts = 3;
	for (let attempt = 1; attempt <= maxAttempts; attempt++) {
		const result = await routeReply({
			payload: { text: notification.message },
			channel,
			to,
			sessionKey: notification.sessionKey,
			accountId,
			threadId,
			cfg
		});
		if (result.ok) {
			log$1.info(`OAuth notification delivered to ${channel}:${to}`);
			return;
		}
		log$1.warn(`OAuth notification attempt ${attempt}/${maxAttempts} failed: ${result.error ?? "unknown error"}`);
		if (attempt < maxAttempts) await new Promise((r) => setTimeout(r, 2e3 * Math.pow(2, attempt - 1)));
	}
	log$1.error(`OAuth notification delivery failed after ${maxAttempts} attempts`);
}
/**
* Notify user of successful OAuth authentication
*/
async function notifyAuthSuccess(sessionKey, agentId, email, services, keyringSyncError) {
	let message = `✓ Google authentication complete for ${email}! You can now use ${services.length > 0 ? services.join(", ") : "Gmail"} features.`;
	if (keyringSyncError) message += `\n⚠ Warning: keyring sync failed (${keyringSyncError}). gog_exec will retry sync automatically on each command.`;
	await sendOAuthNotification({
		type: "success",
		email,
		sessionKey,
		agentId,
		message
	});
}
/**
* Notify user of OAuth authentication timeout
*/
async function notifyAuthTimeout(sessionKey, agentId, email) {
	await sendOAuthNotification({
		type: "timeout",
		email,
		sessionKey,
		agentId,
		message: `⏱ Google authorization for ${email} timed out (5 minutes). Would you like to try again?`
	});
}
/**
* Notify user of OAuth authentication error
*/
async function notifyAuthError(sessionKey, agentId, email, error) {
	let message;
	if (error === "access_denied") message = `✗ Google authorization for ${email} was declined. Let me know if you'd like to try again.`;
	else message = `✗ Google authorization for ${email} failed: ${error}. Please try again.`;
	await sendOAuthNotification({
		type: "error",
		email,
		sessionKey,
		agentId,
		message
	});
}

//#endregion
//#region src/hooks/gog-oauth-server.ts
/**
* OAuth callback server for non-blocking Google authentication
* Handles localhost OAuth redirects and delegates token exchange
* and credential storage to the AuthProvider.
*/
var gog_oauth_server_exports = /* @__PURE__ */ __exportAll({
	addPendingFlow: () => addPendingFlow,
	generateState: () => generateState,
	getPendingFlow: () => getPendingFlow,
	getRedirectUri: () => getRedirectUri,
	getServerPort: () => getServerPort,
	removePendingFlow: () => removePendingFlow,
	startGogOAuthServer: () => startGogOAuthServer
});
const log = createSubsystemLogger("gog-oauth");
/**
* Default OAuth server configuration
*/
const DEFAULT_CONFIG = {
	enabled: true,
	port: DEFAULT_OAUTH_CALLBACK_PORT,
	bind: "127.0.0.1",
	callbackPath: "/oauth-callback",
	timeoutMinutes: 5
};
/**
* In-memory storage for pending OAuth flows
* Key: state token, Value: flow data
*/
const pendingFlows = /* @__PURE__ */ new Map();
/**
* Server instance
*/
let server = null;
let actualPort = null;
let cleanupInterval = null;
let configuredExternalRedirectUri;
/**
* Get the redirect URI for OAuth flows.
* Desktop ("installed") clients only support localhost redirects -- Google rejects
* external URIs with redirect_uri_mismatch. When a Desktop client is detected,
* we always return the localhost URI regardless of env/config settings.
* For "web" or "unknown" clients: env var > config > localhost fallback.
*/
function getRedirectUri() {
	const localhostUri = `http://localhost:${actualPort ?? DEFAULT_CONFIG.port}${DEFAULT_CONFIG.callbackPath}`;
	if (getGoogleClientType() === "installed") {
		const externalUri = process.env.MINION_GOG_OAUTH_REDIRECT_URI || configuredExternalRedirectUri;
		if (externalUri) log.warn(`Desktop client detected — ignoring external redirect URI "${externalUri}" (installed clients only support localhost redirects). Using: ${localhostUri}`);
		return localhostUri;
	}
	const envUri = process.env.MINION_GOG_OAUTH_REDIRECT_URI;
	if (envUri) return envUri;
	if (configuredExternalRedirectUri) return configuredExternalRedirectUri;
	return localhostUri;
}
/**
* Generate cryptographic state token for CSRF protection
*/
function generateState() {
	return crypto.randomBytes(32).toString("hex");
}
/**
* Add a pending OAuth flow.
* Cancels any existing flow for the same session to enforce one-at-a-time.
*/
function addPendingFlow(flow) {
	for (const [state, existing] of pendingFlows.entries()) if (existing.sessionKey === flow.sessionKey) {
		log.info(`Cancelling previous auth flow for session ${flow.sessionKey} (state=${state})`);
		pendingFlows.delete(state);
	}
	pendingFlows.set(flow.state, flow);
}
/**
* Get a pending OAuth flow by state
*/
function getPendingFlow(state) {
	return pendingFlows.get(state);
}
/**
* Remove a pending OAuth flow
*/
function removePendingFlow(state) {
	pendingFlows.delete(state);
}
/**
* Clean up expired OAuth flows
*/
function cleanupExpiredFlows() {
	const now = Date.now();
	const expired = [];
	for (const [state, flow] of pendingFlows.entries()) if (now > flow.expiresAt) {
		expired.push(state);
		notifyAuthTimeout(flow.sessionKey, flow.agentId, flow.email).catch((err) => {
			log.error(`Failed to send timeout notification: ${String(err)}`);
		});
	}
	for (const state of expired) pendingFlows.delete(state);
}
/**
* Resolve the appropriate AuthProvider for a pending flow.
* Currently only Google is supported; future providers can be dispatched
* based on flow.providerId.
*/
function resolveProvider(_flow) {
	return createGoogleAuthProvider();
}
/**
* Handle OAuth callback request
*/
async function handleCallback(params) {
	if (params.error) {
		const state = params.state;
		if (state) {
			const flow = getPendingFlow(state);
			if (flow) {
				removePendingFlow(state);
				await notifyAuthError(flow.sessionKey, flow.agentId, flow.email, params.error);
			}
		}
		return {
			status: 400,
			message: `Authorization failed: ${params.error_description || params.error}`
		};
	}
	if (!params.code || !params.state) return {
		status: 400,
		message: "Missing code or state parameter"
	};
	const flow = getPendingFlow(params.state);
	if (!flow) {
		log.warn(`Invalid or expired state token: ${params.state}`);
		return {
			status: 400,
			message: "Invalid or expired authorization request"
		};
	}
	if (Date.now() > flow.expiresAt) {
		removePendingFlow(params.state);
		await notifyAuthTimeout(flow.sessionKey, flow.agentId, flow.email);
		return {
			status: 400,
			message: "Authorization request expired (5 minutes)"
		};
	}
	removePendingFlow(params.state);
	try {
		const provider = resolveProvider(flow);
		const redirectUri = getRedirectUri();
		const tokens = await provider.exchangeCode(params.code, redirectUri);
		const credPath = await provider.storeCredentials({
			tokens,
			email: flow.email,
			services: flow.services,
			sessionKey: flow.sessionKey,
			agentId: flow.agentId
		});
		await updateSessionStore(resolveDefaultSessionStorePath(flow.agentId), (store) => {
			const session = store[flow.sessionKey];
			if (session) {
				session.gogCredentialsFile = credPath;
				session.gogAuthEmail = flow.email;
				delete session.gogAuthPending;
				session.updatedAt = Date.now();
			}
		});
		const grantedServices = tokens.scope ? getServicesFromScopes(tokens.scope) : flow.services;
		await notifyAuthSuccess(flow.sessionKey, flow.agentId, flow.email, grantedServices);
		return {
			status: 200,
			message: "Authentication successful! You can close this window.",
			services: grantedServices
		};
	} catch (error) {
		traceGatewayEvent({
			traceId: "oauth_er",
			level: "WARN",
			stage: "OAUTH_TOKEN_ERROR",
			data: {
				email: flow.email,
				agentId: flow.agentId,
				error: (error instanceof Error ? error.message : String(error)).slice(0, 200)
			}
		});
		log.error(`Token exchange error: ${error instanceof Error ? error.message : String(error)}`);
		await notifyAuthError(flow.sessionKey, flow.agentId, flow.email, error instanceof Error ? error.message : "Unknown error");
		return {
			status: 500,
			message: "Failed to complete authentication. Please try again."
		};
	}
}
const I18N = {
	en: {
		title: "OAuth – Minion Hub",
		authenticated: "Authenticated",
		error: "Error",
		successMessage: "Authentication successful! You can close this window.",
		errorMessage: "Failed to complete authentication. Please try again."
	},
	es: {
		title: "OAuth – Minion Hub",
		authenticated: "Autenticado",
		error: "Error",
		successMessage: "¡Autenticación exitosa! Puedes cerrar esta ventana.",
		errorMessage: "No se pudo completar la autenticación. Inténtalo de nuevo."
	}
};
/** Parse Accept-Language header into a supported locale. */
function parseLocale(header) {
	if (!header) return "en";
	const supported = Object.keys(I18N);
	for (const part of header.split(",")) {
		const lang = part.split(";")[0].trim().toLowerCase().slice(0, 2);
		if (supported.includes(lang)) return lang;
	}
	return "en";
}
/** Build the HTML response page shown to the user after the OAuth callback. */
function buildCallbackHtml(result, locale = "en") {
	const isSuccess = result.status === 200;
	const t = I18N[locale];
	const accent = isSuccess ? "#e8547a" : "#ef4444";
	const heading = isSuccess ? t.authenticated : t.error;
	const message = isSuccess ? t.successMessage : t.errorMessage;
	const icon = isSuccess ? `<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="${accent}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>` : `<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="${accent}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>`;
	const services = result.services ?? [];
	const servicesBadges = services.length > 0 ? `<div class="services">${services.map((s) => `<span class="svc">${s}</span>`).join("")}</div>` : "";
	return `<!DOCTYPE html><html lang="${locale}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>${t.title}</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
display:flex;align-items:center;justify-content:center;height:100vh;
background:#09090b;color:#fafafa;overflow:hidden}
body::before{content:"";position:fixed;inset:0;
background:radial-gradient(ellipse 80% 60% at 50% 40%,rgba(232,84,122,.08),transparent 70%);
pointer-events:none}
.card{position:relative;background:#0c0c0e;border:1px solid #27272a;
padding:2.5rem 2rem;text-align:center;max-width:380px;width:90%}
.card::before{content:"";position:absolute;inset:-1px;
background:linear-gradient(135deg,${accent}33,transparent 50%,${accent}11);
z-index:-1;padding:1px;
-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);
-webkit-mask-composite:xor;mask-composite:exclude}
.icon{margin:0 auto 1rem;width:48px;height:48px;display:flex;align-items:center;
justify-content:center;background:${accent}15;border:1px solid ${accent}30;border-radius:50%}
h1{font-size:1.125rem;font-weight:700;letter-spacing:-.01em;color:#fafafa;margin-bottom:.5rem}
p{font-size:.8125rem;color:#a1a1aa;line-height:1.6}
.services{display:flex;flex-wrap:wrap;justify-content:center;gap:.375rem;margin-top:.75rem}
.svc{font-size:.6875rem;font-weight:600;padding:.25rem .625rem;
background:${accent}12;border:1px solid ${accent}25;color:${accent};
text-transform:capitalize;letter-spacing:.03em}
.brand{margin-top:1.5rem;padding-top:1rem;border-top:1px solid #27272a;
display:flex;align-items:center;justify-content:center;gap:.375rem}
.brand-name{font-weight:900;font-size:.6875rem;letter-spacing:.06em;text-transform:uppercase;color:${accent}}
.brand-sub{font-weight:600;font-size:.6875rem;color:#71717a}
.pulse{animation:pulse 2s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.6}}
</style></head>
<body>
<div class="card">
<div class="icon${isSuccess ? " pulse" : ""}">${icon}</div>
<h1>${heading}</h1>
<p>${message}</p>
${servicesBadges}
<div class="brand"><span class="brand-name">MINION</span><span class="brand-sub">hub</span></div>
</div>
</body></html>`;
}
/**
* Handle HTTP request
*/
async function handleRequest(req, res, config) {
	const url = new URL$1(req.url || "/", `http://${req.headers.host}`);
	if (url.pathname === "/health") {
		res.statusCode = 200;
		res.setHeader("Content-Type", "text/plain");
		res.end("OK");
		return;
	}
	if (url.pathname === config.callbackPath) {
		const result = await handleCallback({
			code: url.searchParams.get("code") || void 0,
			state: url.searchParams.get("state") || void 0,
			error: url.searchParams.get("error") || void 0,
			error_description: url.searchParams.get("error_description") || void 0
		});
		const locale = parseLocale(req.headers["accept-language"]);
		res.statusCode = result.status;
		res.setHeader("Content-Type", "text/html");
		res.end(buildCallbackHtml(result, locale));
		return;
	}
	res.statusCode = 404;
	res.setHeader("Content-Type", "text/plain");
	res.end("Not Found");
}
/**
* Try to start server on a specific port
*/
function tryStartServer(port, bind, config) {
	return new Promise((resolve, reject) => {
		const srv = http.createServer((req, res) => {
			handleRequest(req, res, config).catch((err) => {
				log.error(`Request handler error: ${err instanceof Error ? err.message : String(err)}`);
				res.statusCode = 500;
				res.end("Internal Server Error");
			});
		});
		srv.on("error", (err) => {
			if (err.code === "EADDRINUSE") reject(/* @__PURE__ */ new Error(`Port ${port} is already in use`));
			else reject(err);
		});
		srv.listen(port, bind, () => {
			resolve(srv);
		});
	});
}
/**
* Register Google OAuth client credentials with the gog CLI keyring (best-effort).
*/
async function registerGogClientCredentials(credFile) {
	try {
		if ((await runCommandWithTimeout(["which", "gog"], { timeoutMs: 2e3 })).code !== 0) return;
		const result = await runCommandWithTimeout([
			"gog",
			"auth",
			"credentials",
			credFile
		], { timeoutMs: 1e4 });
		if (result.code === 0) log.info("Registered Google client credentials with gog CLI");
		else log.warn(`gog auth credentials failed (code=${result.code}): ${result.stderr || result.stdout}`);
	} catch (err) {
		log.warn(`Failed to register gog client credentials: ${String(err)}`);
	}
}
/**
* Ensure GOG_KEYRING_BACKEND and GOG_KEYRING_PASSWORD are set in process.env
* and persisted to ~/.minion/.env.
*/
function ensureGogKeyringEnv() {
	let dirty = false;
	if (!process.env.GOG_KEYRING_BACKEND) {
		process.env.GOG_KEYRING_BACKEND = "file";
		upsertSharedEnvVar({
			key: "GOG_KEYRING_BACKEND",
			value: "file"
		});
		dirty = true;
	}
	if (!process.env.GOG_KEYRING_PASSWORD) {
		const password = crypto.randomBytes(32).toString("hex");
		process.env.GOG_KEYRING_PASSWORD = password;
		upsertSharedEnvVar({
			key: "GOG_KEYRING_PASSWORD",
			value: password
		});
		dirty = true;
	}
	if (dirty) log.info("Initialized GOG keyring credentials and persisted to .env");
}
/**
* Start the OAuth callback server with port fallback
*/
async function startGogOAuthServer(config, agentDir) {
	ensureGogKeyringEnv();
	const { externalRedirectUri, googleClientCredentialsFile, ...rest } = config;
	const fullConfig = {
		...DEFAULT_CONFIG,
		...rest
	};
	configuredExternalRedirectUri = externalRedirectUri;
	const envCredFile = process.env.MINION_GOOGLE_CLIENT_CREDENTIALS_FILE;
	const credFile = envCredFile || googleClientCredentialsFile;
	if (envCredFile) logAcceptedEnvOption({
		key: "MINION_GOOGLE_CLIENT_CREDENTIALS_FILE",
		description: "Google OAuth client credentials file"
	});
	if (credFile) {
		setGoogleClientCredentialsFile(credFile);
		registerGogClientCredentials(credFile);
	}
	if (!fullConfig.enabled) throw new Error("OAuth server is disabled in configuration");
	const ports = [fullConfig.port, ...Array.from({ length: 5 }, (_, i) => DEFAULT_OAUTH_CALLBACK_PORT + 1 + i)];
	let lastError = null;
	for (const port of ports) try {
		server = await tryStartServer(port, fullConfig.bind, fullConfig);
		actualPort = port;
		log.info(`Server listening on ${fullConfig.bind}:${port}`);
		cleanupInterval = setInterval(cleanupExpiredFlows, 6e4);
		return {
			port,
			stop: async () => {
				if (cleanupInterval) {
					clearInterval(cleanupInterval);
					cleanupInterval = null;
				}
				return new Promise((resolve) => {
					if (server) server.close(() => {
						server = null;
						actualPort = null;
						pendingFlows.clear();
						resolve();
					});
					else resolve();
				});
			}
		};
	} catch (error) {
		lastError = error;
	}
	throw new Error(`Failed to start OAuth server on any port (${ports.join(", ")}): ${lastError?.message}`);
}
/**
* Get the current server port (if running)
*/
function getServerPort() {
	return actualPort;
}

//#endregion
export { gog_oauth_server_exports as a, getServerPort as i, generateState as n, createGoogleAuthProvider as o, getRedirectUri as r, addPendingFlow as t };