import { createHash } from "node:crypto"; import type { OcxProviderConfig } from "../types"; import { parseGoogleCookieJar, validateAiStudioCookies, } from "./google-aistudio-auth"; import { cookieHeaderFromSession, loadAiStudioSession, type AiStudioSessionData, } from "./aistudio-session-sync"; export type AiStudioCredentialResolution = | { kind: "ready"; cookieHeader: string; source: "provider-api-key" | "provider-header" | "session"; fingerprint: string } | { kind: "missing"; reason: string } | { kind: "invalid"; reason: string }; const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/; export const AI_STUDIO_WEB_BASE_URL = "https://alkalimakersuite-pa.clients6.google.com"; export function isCanonicalAiStudioWebBaseUrl(value: unknown): boolean { if (value !== undefined && typeof value !== "string") return false; const candidate = typeof value === "string" && value.trim() ? value.trim() : AI_STUDIO_WEB_BASE_URL; try { const url = new URL(candidate); return url.protocol === "https:" && url.hostname.toLowerCase() === "alkalimakersuite-pa.clients6.google.com" && url.port === "" && url.username === "" && url.password === "" && (url.pathname === "" || url.pathname === "/") && url.search === "" && url.hash === ""; } catch { return false; } } function validCookieHeader(value: unknown): string | undefined { if (typeof value !== "string") return undefined; const cookieHeader = value.trim(); if (!cookieHeader || CONTROL_CHARACTERS.test(cookieHeader)) return undefined; const jar = parseGoogleCookieJar(cookieHeader); return validateAiStudioCookies(jar).valid ? jar.cookieHeader : undefined; } function ready(cookieHeader: string, source: "provider-api-key" | "provider-header" | "session"): AiStudioCredentialResolution { return { kind: "ready", cookieHeader, source, fingerprint: createHash("sha256").update(cookieHeader).digest("hex"), }; } export function resolveAiStudioCredentials( provider: OcxProviderConfig, session?: AiStudioSessionData | null, requestUrl?: string | URL, ): AiStudioCredentialResolution { if (!isCanonicalAiStudioWebBaseUrl(provider.baseUrl)) { return { kind: "invalid", reason: "AI Studio web credentials require the canonical Google endpoint." }; } const invalidSources: string[] = []; const apiKey = validCookieHeader(provider.apiKey); if (apiKey) return ready(apiKey, "provider-api-key"); if ((typeof provider.apiKey === "string" && provider.apiKey.trim()) || (provider.apiKey !== undefined && provider.apiKey !== null && typeof provider.apiKey !== "string")) { invalidSources.push("provider apiKey"); } const cookieEntry = Object.entries(provider.headers ?? {}).find(([name]) => name.toLowerCase() === "cookie"); const headerCookie = validCookieHeader(cookieEntry?.[1]); if (headerCookie) return ready(headerCookie, "provider-header"); if ((typeof cookieEntry?.[1] === "string" && cookieEntry[1].trim()) || (cookieEntry !== undefined && typeof cookieEntry[1] !== "string")) { invalidSources.push("provider Cookie header"); } const savedSession = session === undefined ? loadAiStudioSession() : session; const sessionCookie = validCookieHeader(cookieHeaderFromSession(savedSession, requestUrl ?? AI_STUDIO_WEB_BASE_URL)); if (sessionCookie) return ready(sessionCookie, "session"); if (savedSession) invalidSources.push("saved session"); return invalidSources.length > 0 ? { kind: "invalid", reason: "AI Studio credentials are malformed or missing SAPISID." } : { kind: "missing", reason: "AI Studio credentials are missing." }; }