Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Fetch any URL via Chrome CDP and convert the rendered page to clean markdown with YouTube transcript support.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/lib/browser/cookie-sidecar.ts
1import { readFile, writeFile, mkdir } from "node:fs/promises";2import { dirname, join } from "node:path";3import { resolveChromeProfileDir } from "./profile";4import type { TargetSession } from "./cdp-client";56export interface CdpCookie {7name: string;8value: string;9domain: string;10path: string;11expires: number;12size: number;13httpOnly: boolean;14secure: boolean;15session: boolean;16sameSite?: string;17priority?: string;18sameParty?: boolean;19sourceScheme?: string;20sourcePort?: number;21partitionKey?: string;22}2324interface SidecarData {25savedAt: string;26cookies: CdpCookie[];27}2829export interface CookieSidecarConfig {30urls: readonly string[];31filename: string;32requiredCookieNames: readonly string[];33filterCookie?: (cookie: CdpCookie) => boolean;34}3536function sidecarPath(filename: string, profileDir?: string): string {37return join(resolveChromeProfileDir(profileDir), filename);38}3940function hasRequired(cookies: CdpCookie[], names: readonly string[]): boolean {41return names.every((name) =>42cookies.some((c) => c.name === name && Boolean(c.value)),43);44}4546async function getCookies(session: TargetSession, urls: readonly string[]): Promise<CdpCookie[]> {47const { cookies } = await session.send<{ cookies: CdpCookie[] }>(48"Network.getCookies",49{ urls: [...urls] },50);51return cookies ?? [];52}5354export async function exportCookies(55session: TargetSession,56config: CookieSidecarConfig,57profileDir?: string,58): Promise<boolean> {59const all = await getCookies(session, config.urls);60const filtered = config.filterCookie ? all.filter(config.filterCookie) : all;61if (!hasRequired(filtered, config.requiredCookieNames)) return false;6263const filePath = sidecarPath(config.filename, profileDir);64await mkdir(dirname(filePath), { recursive: true });65const data: SidecarData = { savedAt: new Date().toISOString(), cookies: filtered };66await writeFile(filePath, JSON.stringify(data, null, 2));67return true;68}6970export async function restoreCookies(71session: TargetSession,72config: CookieSidecarConfig,73profileDir?: string,74): Promise<boolean> {75const live = await getCookies(session, config.urls);76if (hasRequired(live, config.requiredCookieNames)) return false;7778const filePath = sidecarPath(config.filename, profileDir);79const raw = await readFile(filePath, "utf8");80const data = JSON.parse(raw) as SidecarData;81if (!data.cookies?.length) return false;8283const now = Date.now() / 1000;84const valid = data.cookies.filter((c) => c.session || !c.expires || c.expires > now);85if (!hasRequired(valid, config.requiredCookieNames)) return false;8687await session.send("Network.setCookies", {88cookies: valid.map((c) => ({89name: c.name,90value: c.value,91domain: c.domain,92path: c.path,93httpOnly: c.httpOnly,94secure: c.secure,95sameSite: c.sameSite,96expires: c.expires,97})),98});99return true;100}101