/** * End-to-end check: does a stored Tryst cookie constitute a live login? * * pnpm --filter @cocottetech/bookings-tryst-adapter e2e:cookie-login * * The cookie is a secret and is read from OUTSIDE the repo so the auto-commit * service never sees it. Provide it one of two ways: * * TRYST_COOKIE="_tryst_session=...; other=..." pnpm ... e2e:cookie-login * # or drop it in a file and point at it / use the default path: * TRYST_COOKIE_FILE=/abs/path/to/cookie.txt pnpm ... e2e:cookie-login * * Default file path: ~/.cache/cocotte/tryst-cookie.txt * * Optional env: * HEADFUL=1 run the browser headful (watch it) * TRYST_USER_AGENT=... override the browser UA (match the cookie's device) * * This script does NOT touch platform.db — it verifies the login only. Wiring * the cookie through the credentials vault is a separate, prod-DB-gated step. */ import { readFileSync } from 'node:fs'; import { homedir } from 'node:os'; import { join } from 'node:path'; import { parseCookieBlob, cookieNames } from '../src/adapter/cookie-blob.js'; import { verifyTrystSession } from '../src/adapter/tryst-session.js'; const DEFAULT_COOKIE_FILE = join(homedir(), '.cache', 'cocotte', 'tryst-cookie.txt'); /** Line to stdout. CLI tooling — stdout is the deliverable, not app logging. */ function out(line = ''): void { process.stdout.write(`${line}\n`); } /** Line to stderr. */ function err(line = ''): void { process.stderr.write(`${line}\n`); } function loadCookieBlob(): string { const inline = process.env['TRYST_COOKIE']; if (inline && inline.trim().length > 0) { return inline; } const file = process.env['TRYST_COOKIE_FILE'] ?? DEFAULT_COOKIE_FILE; try { return readFileSync(file, 'utf8'); } catch { printNoCookieHelp(file); process.exit(2); } } function printNoCookieHelp(triedFile: string): void { for (const line of [ '', 'No Tryst cookie found.', ' · TRYST_COOKIE env var: not set', ` · cookie file: not readable at ${triedFile}`, '', 'How to get the cookie (the session cookie is HttpOnly — use DevTools, not document.cookie):', ' 1. Sign in to https://app.tryst.link in your browser.', ' 2. Open DevTools → Network tab → click any request to app.tryst.link.', ' 3. Under Request Headers, copy the entire "Cookie:" value.', ' 4. Pass it via TRYST_COOKIE="..." or save it to the file path above.', '', ]) { err(line); } } async function main(): Promise { const blob = loadCookieBlob(); let cookies; try { cookies = parseCookieBlob(blob); } catch (parseErr) { err(`Could not parse the cookie blob: ${(parseErr as Error).message}`); process.exit(2); } out(`Parsed ${cookies.length} cookie(s): ${cookieNames(cookies).join(', ')}`); out('Launching stealth Chromium and navigating to app.tryst.link …'); out(); const screenshotPath = join('/tmp', `tryst-cookie-login-${Date.now()}.png`); const userAgent = process.env['TRYST_USER_AGENT']; const res = await verifyTrystSession(cookies, { headless: process.env['HEADFUL'] !== '1', ...(userAgent ? { userAgent } : {}), screenshotPath, }); out(res.loggedIn ? '✓ LOGGED IN' : '✗ NOT LOGGED IN'); out(` final URL : ${res.finalUrl || '(none)'}`); out(` page title: ${res.pageTitle || '(none)'}`); if (res.handle) out(` account : ${res.handle}`); out(` reason : ${res.reason}`); if (res.error) out(` error : ${res.error}`); if (res.screenshotPath) out(` screenshot: ${res.screenshotPath}`); process.exit(res.loggedIn ? 0 : 1); } void main();