Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Generate text and images via the reverse-engineered Gemini Web API with multi-turn conversation support.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/gemini-webapi/utils/load-browser-cookies.ts
1import process from 'node:process';23import {4CdpConnection,5discoverRunningChromeDebugPort,6findChromeExecutable as findChromeExecutableBase,7findExistingChromeDebugPort,8gracefulKillChrome,9getFreePort,10launchChrome as launchChromeBase,11openPageSession,12sleep,13waitForChromeDebugPort,14type PlatformCandidates,15} from 'baoyu-chrome-cdp';1617import { Endpoint, Headers } from '../constants.js';18import { logger } from './logger.js';19import { cookie_header, fetch_with_timeout } from './http.js';20import { read_cookie_file, type CookieMap, write_cookie_file } from './cookie-file.js';21import { resolveGeminiWebChromeProfileDir, resolveGeminiWebCookiePath } from './paths.js';2223const GEMINI_APP_URL = 'https://gemini.google.com/app';2425const CHROME_CANDIDATES_FULL: PlatformCandidates = {26darwin: [27'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',28'/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',29'/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta',30'/Applications/Chromium.app/Contents/MacOS/Chromium',31'/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',32],33win32: [34'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',35'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',36'C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe',37'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe',38],39default: [40'/usr/bin/google-chrome',41'/usr/bin/google-chrome-stable',42'/usr/bin/chromium',43'/usr/bin/chromium-browser',44'/snap/bin/chromium',45'/usr/bin/microsoft-edge',46],47};4849async function get_free_port(): Promise<number> {50return await getFreePort('GEMINI_WEB_DEBUG_PORT');51}5253function find_chrome_executable(): string | null {54return findChromeExecutableBase({55candidates: CHROME_CANDIDATES_FULL,56envNames: ['GEMINI_WEB_CHROME_PATH'],57}) ?? null;58}5960async function find_existing_chrome_debug_port(profileDir: string): Promise<number | null> {61return await findExistingChromeDebugPort({ profileDir });62}6364async function launch_chrome(profileDir: string, port: number) {65const chromePath = find_chrome_executable();66if (!chromePath) throw new Error('Chrome executable not found.');6768return await launchChromeBase({69chromePath,70profileDir,71port,72url: GEMINI_APP_URL,73extraArgs: ['--disable-popup-blocking'],74});75}7677async function is_gemini_session_ready(cookies: CookieMap, verbose: boolean): Promise<boolean> {78if (!cookies['__Secure-1PSID']) return false;7980try {81const res = await fetch_with_timeout(Endpoint.INIT, {82method: 'GET',83headers: { ...Headers.GEMINI, Cookie: cookie_header(cookies) },84redirect: 'follow',85timeout_ms: 30_000,86});8788if (!res.ok) {89if (verbose) logger.debug(`Gemini init check failed: ${res.status} ${res.statusText}`);90return false;91}9293const text = await res.text();94return /\"SNlM0e\":\"(.*?)\"/.test(text);95} catch (e) {96if (verbose) logger.debug(`Gemini init check error: ${e instanceof Error ? e.message : String(e)}`);97return false;98}99}100101async function fetch_cookies_from_existing_chrome(102timeoutMs: number,103verbose: boolean,104): Promise<CookieMap | null> {105const discovered = await discoverRunningChromeDebugPort();106if (discovered === null) return null;107108if (verbose) logger.info(`Found reusable Chrome debugging session on port ${discovered.port}. Connecting via WebSocket...`);109110let cdp: CdpConnection | null = null;111let targetId: string | null = null;112let createdTarget = false;113try {114const connectStart = Date.now();115const connectTimeout = 30_000;116let lastConnErr: unknown = null;117while (Date.now() - connectStart < connectTimeout) {118try {119cdp = await CdpConnection.connect(discovered.wsUrl, 5_000);120break;121} catch (e) {122lastConnErr = e;123if (verbose) logger.debug(`WebSocket connect attempt failed: ${e instanceof Error ? e.message : String(e)}, retrying...`);124await sleep(1000);125}126}127if (!cdp) {128if (verbose) logger.debug(`Could not connect to Chrome after ${connectTimeout / 1000}s: ${lastConnErr instanceof Error ? lastConnErr.message : String(lastConnErr)}`);129return null;130}131132const page = await openPageSession({133cdp,134reusing: false,135url: GEMINI_APP_URL,136matchTarget: (target) => target.type === 'page' && target.url.includes('gemini.google.com'),137enableNetwork: true,138activateTarget: false,139});140const { sessionId } = page;141targetId = page.targetId;142createdTarget = page.createdTarget;143144if (verbose) logger.debug(createdTarget ? 'No Gemini tab found, creating new tab...' : 'Found existing Gemini tab, attaching...');145146const start = Date.now();147let last: CookieMap = {};148149while (Date.now() - start < timeoutMs) {150const { cookies } = await cdp.send<{ cookies: Array<{ name: string; value: string }> }>(151'Network.getCookies',152{ urls: ['https://gemini.google.com/', 'https://accounts.google.com/', 'https://www.google.com/'] },153{ sessionId, timeoutMs: 10_000 },154);155156const cookieMap: CookieMap = {};157for (const cookie of cookies) {158if (cookie?.name && typeof cookie.value === 'string') cookieMap[cookie.name] = cookie.value;159}160161last = cookieMap;162if (await is_gemini_session_ready(cookieMap, verbose)) return cookieMap;163164await sleep(1000);165}166167if (verbose) logger.debug(`Existing Chrome did not yield valid cookies. Last keys: ${Object.keys(last).join(', ')}`);168return null;169} catch (e) {170if (verbose) logger.debug(`Failed to connect to existing Chrome debugging session: ${e instanceof Error ? e.message : String(e)}`);171return null;172} finally {173if (cdp) {174if (createdTarget && targetId) {175try { await cdp.send('Target.closeTarget', { targetId }, { timeoutMs: 5_000 }); } catch {}176}177cdp.close();178}179}180}181182async function fetch_google_cookies_via_cdp(183profileDir: string,184timeoutMs: number,185verbose: boolean,186): Promise<CookieMap> {187const existingPort = await find_existing_chrome_debug_port(profileDir);188const reusing = existingPort !== null;189const port = existingPort ?? await get_free_port();190const chrome = reusing ? null : await launch_chrome(profileDir, port);191192let cdp: CdpConnection | null = null;193let targetId: string | null = null;194try {195const wsUrl = await waitForChromeDebugPort(port, 30_000, { includeLastError: true });196cdp = await CdpConnection.connect(wsUrl, 15_000);197198if (verbose) {199logger.info(reusing200? `Reusing existing Chrome on port ${port}. Waiting for a valid Gemini session...`201: 'Chrome opened. If needed, complete Google login in the window. Waiting for a valid Gemini session...');202}203204const page = await openPageSession({205cdp,206reusing,207url: GEMINI_APP_URL,208matchTarget: (target) => target.type === 'page' && target.url.includes('gemini.google.com'),209enableNetwork: true,210});211const { sessionId } = page;212targetId = page.targetId;213214const start = Date.now();215let last: CookieMap = {};216217while (Date.now() - start < timeoutMs) {218const { cookies } = await cdp.send<{ cookies: Array<{ name: string; value: string }> }>(219'Network.getCookies',220{ urls: ['https://gemini.google.com/', 'https://accounts.google.com/', 'https://www.google.com/'] },221{ sessionId, timeoutMs: 10_000 },222);223224const cookieMap: CookieMap = {};225for (const cookie of cookies) {226if (cookie?.name && typeof cookie.value === 'string') cookieMap[cookie.name] = cookie.value;227}228229last = cookieMap;230if (await is_gemini_session_ready(cookieMap, verbose)) {231return cookieMap;232}233234await sleep(1000);235}236237throw new Error(`Timed out waiting for a valid Gemini session. Last keys: ${Object.keys(last).join(', ')}`);238} finally {239if (cdp) {240if (reusing && targetId) {241try {242await cdp.send('Target.closeTarget', { targetId }, { timeoutMs: 5_000 });243} catch {}244}245cdp.close();246}247248if (chrome) await gracefulKillChrome(chrome, port);249}250}251252export async function load_browser_cookies(domain_name: string = '', verbose: boolean = true): Promise<Record<string, CookieMap>> {253const force = process.env.GEMINI_WEB_LOGIN?.trim() || process.env.GEMINI_WEB_FORCE_LOGIN?.trim();254if (!force) {255const cached = await read_cookie_file();256if (cached) return { chrome: cached };257}258259const hasExplicitProfile = !!(process.env.GEMINI_WEB_CHROME_PROFILE_DIR?.trim() || process.env.BAOYU_CHROME_PROFILE_DIR?.trim());260const existingCookies = hasExplicitProfile ? null : await fetch_cookies_from_existing_chrome(30_000, verbose);261if (existingCookies) {262const filtered: CookieMap = {};263for (const [key, value] of Object.entries(existingCookies)) {264if (typeof value === 'string' && value.length > 0) filtered[key] = value;265}266267await write_cookie_file(filtered, resolveGeminiWebCookiePath(), 'cdp-existing');268void domain_name;269return { chrome: filtered };270}271272const profileDir = process.env.GEMINI_WEB_CHROME_PROFILE_DIR?.trim() || resolveGeminiWebChromeProfileDir();273const cookies = await fetch_google_cookies_via_cdp(profileDir, 120_000, verbose);274275const filtered: CookieMap = {};276for (const [key, value] of Object.entries(cookies)) {277if (typeof value === 'string' && value.length > 0) filtered[key] = value;278}279280await write_cookie_file(filtered, resolveGeminiWebCookiePath(), 'cdp');281void domain_name;282return { chrome: filtered };283}284285export const loadBrowserCookies = load_browser_cookies;286