v2.1: Bundle Bird X search - no external CLI needed

Vendor Bird's Twitter GraphQL search client directly into /last30days,
eliminating the dependency on `npm install -g @steipete/bird`. X search
now works out of the box with just Node.js 22+ and browser cookies.

- Add vendored bird-search.mjs wrapper (search-only subset of Bird v0.8.0)
- Vendor @steipete/sweet-cookie for browser cookie extraction
- Update bird_x.py to call vendored Node.js module instead of `bird` binary
- Update README.md and SKILL.md for v2.1 (remove Bird CLI install steps)
- Include Bird's MIT LICENSE in vendor directory

The fallback chain is: vendored search -> xAI API key -> web-only mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt Van Horn
2026-02-07 17:16:04 -08:00
parent 0ec338c630
commit 31313c69ac
146 changed files with 4860 additions and 59 deletions
@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2025 Peter Steinberger
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,29 @@
# @steipete/sweet-cookie
Inline-first browser cookie extraction for local tooling (no native addons).
Supports:
- Inline payloads (JSON / base64 / file) — most reliable path.
- Local browser reads (best effort): Chrome, Edge, Firefox, Safari (macOS).
Install:
```bash
npm i @steipete/sweet-cookie
```
Usage:
```ts
import { getCookies, toCookieHeader } from '@steipete/sweet-cookie';
const { cookies, warnings } = await getCookies({
url: 'https://example.com/',
names: ['session', 'csrf'],
browsers: ['chrome', 'edge', 'firefox', 'safari'],
});
for (const w of warnings) console.warn(w);
const cookieHeader = toCookieHeader(cookies, { dedupeByName: true });
```
Docs + extension exporter: see the repo root README.
@@ -0,0 +1,3 @@
export { getCookies, toCookieHeader } from './public.js';
export type { BrowserName, Cookie, CookieHeaderOptions, CookieMode, CookieSameSite, GetCookiesOptions, GetCookiesResult, } from './types.js';
//# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzD,YAAY,EACX,WAAW,EACX,MAAM,EACN,mBAAmB,EACnB,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,gBAAgB,GAChB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
export { getCookies, toCookieHeader } from './public.js';
//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,8 @@
import type { GetCookiesResult } from '../types.js';
export declare function getCookiesFromChrome(options: {
profile?: string;
timeoutMs?: number;
includeExpired?: boolean;
debug?: boolean;
}, origins: string[], allowlistNames: Set<string> | null): Promise<GetCookiesResult>;
//# sourceMappingURL=chrome.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"chrome.d.ts","sourceRoot":"","sources":["../../src/providers/chrome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAK5D,wBAAsB,oBAAoB,CACzC,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EAC5F,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAChC,OAAO,CAAC,gBAAgB,CAAC,CA0B3B"}
@@ -0,0 +1,27 @@
import { getCookiesFromChromeSqliteLinux } from './chromeSqliteLinux.js';
import { getCookiesFromChromeSqliteMac } from './chromeSqliteMac.js';
import { getCookiesFromChromeSqliteWindows } from './chromeSqliteWindows.js';
export async function getCookiesFromChrome(options, origins, allowlistNames) {
const warnings = [];
// Platform dispatch only. All real logic lives in the per-OS providers.
if (process.platform === 'darwin') {
const r = await getCookiesFromChromeSqliteMac(options, origins, allowlistNames);
warnings.push(...r.warnings);
const cookies = r.cookies;
return { cookies, warnings };
}
if (process.platform === 'linux') {
const r = await getCookiesFromChromeSqliteLinux(options, origins, allowlistNames);
warnings.push(...r.warnings);
const cookies = r.cookies;
return { cookies, warnings };
}
if (process.platform === 'win32') {
const r = await getCookiesFromChromeSqliteWindows(options, origins, allowlistNames);
warnings.push(...r.warnings);
const cookies = r.cookies;
return { cookies, warnings };
}
return { cookies: [], warnings };
}
//# sourceMappingURL=chrome.js.map
@@ -0,0 +1 @@
{"version":3,"file":"chrome.js","sourceRoot":"","sources":["../../src/providers/chrome.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,6BAA6B,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,iCAAiC,EAAE,MAAM,0BAA0B,CAAC;AAE7E,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACzC,OAA4F,EAC5F,OAAiB,EACjB,cAAkC;IAElC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,wEAAwE;IACxE,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACnC,MAAM,CAAC,GAAG,MAAM,6BAA6B,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;QAChF,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAa,CAAC,CAAC,OAAO,CAAC;QACpC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IAC9B,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAClC,MAAM,CAAC,GAAG,MAAM,+BAA+B,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;QAClF,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAa,CAAC,CAAC,OAAO,CAAC;QACpC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IAC9B,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAClC,MAAM,CAAC,GAAG,MAAM,iCAAiC,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;QACpF,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAa,CAAC,CAAC,OAAO,CAAC;QACpC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IAC9B,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,11 @@
export declare function deriveAes128CbcKeyFromPassword(password: string, options: {
iterations: number;
}): Buffer;
export declare function decryptChromiumAes128CbcCookieValue(encryptedValue: Uint8Array, keyCandidates: readonly Buffer[], options: {
stripHashPrefix: boolean;
treatUnknownPrefixAsPlaintext?: boolean;
}): string | null;
export declare function decryptChromiumAes256GcmCookieValue(encryptedValue: Uint8Array, key: Buffer, options: {
stripHashPrefix: boolean;
}): string | null;
//# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../../src/providers/chromeSqlite/crypto.ts"],"names":[],"mappings":"AAIA,wBAAgB,8BAA8B,CAC7C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GAC7B,MAAM,CAIR;AAED,wBAAgB,mCAAmC,CAClD,cAAc,EAAE,UAAU,EAC1B,aAAa,EAAE,SAAS,MAAM,EAAE,EAChC,OAAO,EAAE;IAAE,eAAe,EAAE,OAAO,CAAC;IAAC,6BAA6B,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5E,MAAM,GAAG,IAAI,CA2Bf;AAED,wBAAgB,mCAAmC,CAClD,cAAc,EAAE,UAAU,EAC1B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,eAAe,EAAE,OAAO,CAAA;CAAE,GACnC,MAAM,GAAG,IAAI,CAyBf"}
@@ -0,0 +1,100 @@
import { createDecipheriv, pbkdf2Sync } from 'node:crypto';
const UTF8_DECODER = new TextDecoder('utf-8', { fatal: true });
export function deriveAes128CbcKeyFromPassword(password, options) {
// Chromium derives the AES-128-CBC key from "Chrome Safe Storage" using PBKDF2.
// The salt/length/digest are fixed by Chromium ("saltysalt", 16 bytes, sha1).
return pbkdf2Sync(password, 'saltysalt', options.iterations, 16, 'sha1');
}
export function decryptChromiumAes128CbcCookieValue(encryptedValue, keyCandidates, options) {
const buf = Buffer.from(encryptedValue);
if (buf.length < 3)
return null;
// Chromium prefixes encrypted cookies with `v10`, `v11`, ... (three bytes).
const prefix = buf.subarray(0, 3).toString('utf8');
const hasVersionPrefix = /^v\d\d$/.test(prefix);
if (!hasVersionPrefix) {
// Some platforms (notably macOS) can store plaintext values in `encrypted_value`.
// Callers decide whether unknown prefixes should be treated as plaintext.
if (options.treatUnknownPrefixAsPlaintext === false)
return null;
return decodeCookieValueBytes(buf, false);
}
const ciphertext = buf.subarray(3);
if (!ciphertext.length)
return '';
for (const key of keyCandidates) {
// Try multiple candidates because Linux may fall back to empty passwords depending on keyring state.
const decrypted = tryDecryptAes128Cbc(ciphertext, key);
if (!decrypted)
continue;
const decoded = decodeCookieValueBytes(decrypted, options.stripHashPrefix);
if (decoded !== null)
return decoded;
}
return null;
}
export function decryptChromiumAes256GcmCookieValue(encryptedValue, key, options) {
const buf = Buffer.from(encryptedValue);
if (buf.length < 3)
return null;
const prefix = buf.subarray(0, 3).toString('utf8');
if (!/^v\d\d$/.test(prefix))
return null;
// AES-256-GCM layout:
// - 12-byte nonce
// - ciphertext
// - 16-byte authentication tag
const payload = buf.subarray(3);
if (payload.length < 12 + 16)
return null;
const nonce = payload.subarray(0, 12);
const authenticationTag = payload.subarray(payload.length - 16);
const ciphertext = payload.subarray(12, payload.length - 16);
try {
const decipher = createDecipheriv('aes-256-gcm', key, nonce);
decipher.setAuthTag(authenticationTag);
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
return decodeCookieValueBytes(plaintext, options.stripHashPrefix);
}
catch {
return null;
}
}
function tryDecryptAes128Cbc(ciphertext, key) {
try {
// Chromium's legacy AES-128-CBC uses an IV of 16 spaces.
const iv = Buffer.alloc(16, 0x20);
const decipher = createDecipheriv('aes-128-cbc', key, iv);
decipher.setAutoPadding(false);
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
return removePkcs7Padding(plaintext);
}
catch {
return null;
}
}
function removePkcs7Padding(value) {
if (!value.length)
return value;
const padding = value[value.length - 1];
if (!padding || padding > 16)
return value;
return value.subarray(0, value.length - padding);
}
function decodeCookieValueBytes(value, stripHashPrefix) {
// Chromium >= 24 prepends a 32-byte hash to cookie values.
const bytes = stripHashPrefix && value.length >= 32 ? value.subarray(32) : value;
try {
return stripLeadingControlChars(UTF8_DECODER.decode(bytes));
}
catch {
return null;
}
}
function stripLeadingControlChars(value) {
let i = 0;
while (i < value.length && value.charCodeAt(i) < 0x20)
i += 1;
return value.slice(i);
}
//# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../../src/providers/chromeSqlite/crypto.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
export type LinuxKeyringBackend = 'gnome' | 'kwallet' | 'basic';
/**
* Read the "Safe Storage" password from a Linux keyring.
*
* Chromium browsers typically store their cookie encryption password under:
* - service: "<Browser> Safe Storage"
* - account: "<Browser>"
*
* We keep this logic in JS (no native deps) and return an empty password on failure
* (Chromium may still have v10 cookies, and callers can use inline/export escape hatches).
*/
export declare function getLinuxChromiumSafeStoragePassword(options: {
backend?: LinuxKeyringBackend;
app: 'chrome' | 'edge';
}): Promise<{
password: string;
warnings: string[];
}>;
export declare function getLinuxChromeSafeStoragePassword(options?: {
backend?: LinuxKeyringBackend;
}): Promise<{
password: string;
warnings: string[];
}>;
//# sourceMappingURL=linuxKeyring.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"linuxKeyring.d.ts","sourceRoot":"","sources":["../../../src/providers/chromeSqlite/linuxKeyring.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAEhE;;;;;;;;;GASG;AACH,wBAAsB,mCAAmC,CAAC,OAAO,EAAE;IAClE,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,GAAG,EAAE,QAAQ,GAAG,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA8DpD;AAED,wBAAsB,iCAAiC,CACtD,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,mBAAmB,CAAA;CAAO,GAC7C,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAInD"}
@@ -0,0 +1,104 @@
import { execCapture } from '../../util/exec.js';
/**
* Read the "Safe Storage" password from a Linux keyring.
*
* Chromium browsers typically store their cookie encryption password under:
* - service: "<Browser> Safe Storage"
* - account: "<Browser>"
*
* We keep this logic in JS (no native deps) and return an empty password on failure
* (Chromium may still have v10 cookies, and callers can use inline/export escape hatches).
*/
export async function getLinuxChromiumSafeStoragePassword(options) {
const warnings = [];
// Escape hatch: if callers already know the password (or want deterministic CI behavior),
// they can bypass keyring probing entirely.
const overrideKey = options.app === 'edge'
? 'SWEET_COOKIE_EDGE_SAFE_STORAGE_PASSWORD'
: 'SWEET_COOKIE_CHROME_SAFE_STORAGE_PASSWORD';
const override = readEnv(overrideKey);
if (override !== undefined)
return { password: override, warnings };
const backend = options.backend ?? parseLinuxKeyringBackend() ?? chooseLinuxKeyringBackend();
// `basic` means "don't try keyrings" (Chrome will fall back to older/less-secure schemes on some setups).
if (backend === 'basic')
return { password: '', warnings };
const service = options.app === 'edge' ? 'Microsoft Edge Safe Storage' : 'Chrome Safe Storage';
const account = options.app === 'edge' ? 'Microsoft Edge' : 'Chrome';
const folder = `${account} Keys`;
if (backend === 'gnome') {
// GNOME keyring: `secret-tool` is the simplest way to read libsecret entries.
const res = await execCapture('secret-tool', ['lookup', 'service', service, 'account', account], { timeoutMs: 3_000 });
if (res.code === 0)
return { password: res.stdout.trim(), warnings };
warnings.push('Failed to read Linux keyring via secret-tool; v11 cookies may be unavailable.');
return { password: '', warnings };
}
// KDE keyring: query KWallet via `kwallet-query`, but the wallet name differs across KDE versions.
const kdeVersion = (readEnv('KDE_SESSION_VERSION') ?? '').trim();
const serviceName = kdeVersion === '6'
? 'org.kde.kwalletd6'
: kdeVersion === '5'
? 'org.kde.kwalletd5'
: 'org.kde.kwalletd';
const walletPath = kdeVersion === '6'
? '/modules/kwalletd6'
: kdeVersion === '5'
? '/modules/kwalletd5'
: '/modules/kwalletd';
const wallet = await getKWalletNetworkWallet(serviceName, walletPath);
const passwordRes = await execCapture('kwallet-query', ['--read-password', service, '--folder', folder, wallet], { timeoutMs: 3_000 });
if (passwordRes.code !== 0) {
warnings.push('Failed to read Linux keyring via kwallet-query; v11 cookies may be unavailable.');
return { password: '', warnings };
}
if (passwordRes.stdout.toLowerCase().startsWith('failed to read'))
return { password: '', warnings };
return { password: passwordRes.stdout.trim(), warnings };
}
export async function getLinuxChromeSafeStoragePassword(options = {}) {
const args = { app: 'chrome' };
if (options.backend !== undefined)
args.backend = options.backend;
return await getLinuxChromiumSafeStoragePassword(args);
}
function parseLinuxKeyringBackend() {
const raw = readEnv('SWEET_COOKIE_LINUX_KEYRING');
if (!raw)
return undefined;
const normalized = raw.toLowerCase();
if (normalized === 'gnome')
return 'gnome';
if (normalized === 'kwallet')
return 'kwallet';
if (normalized === 'basic')
return 'basic';
return undefined;
}
function chooseLinuxKeyringBackend() {
const xdg = readEnv('XDG_CURRENT_DESKTOP') ?? '';
const isKde = xdg.split(':').some((p) => p.trim().toLowerCase() === 'kde') || !!readEnv('KDE_FULL_SESSION');
return isKde ? 'kwallet' : 'gnome';
}
async function getKWalletNetworkWallet(serviceName, walletPath) {
const res = await execCapture('dbus-send', [
'--session',
'--print-reply=literal',
`--dest=${serviceName}`,
walletPath,
'org.kde.KWallet.networkWallet',
], { timeoutMs: 3_000 });
const fallback = 'kdewallet';
if (res.code !== 0)
return fallback;
const raw = res.stdout.trim();
if (!raw)
return fallback;
return raw.replaceAll('"', '').trim() || fallback;
}
function readEnv(key) {
const value = process.env[key];
const trimmed = typeof value === 'string' ? value.trim() : '';
return trimmed.length ? trimmed : undefined;
}
//# sourceMappingURL=linuxKeyring.js.map
@@ -0,0 +1 @@
{"version":3,"file":"linuxKeyring.js","sourceRoot":"","sources":["../../../src/providers/chromeSqlite/linuxKeyring.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
import type { GetCookiesResult } from '../../types.js';
export declare function getCookiesFromChromeSqliteDb(options: {
dbPath: string;
profile?: string;
includeExpired?: boolean;
debug?: boolean;
}, origins: string[], allowlistNames: Set<string> | null, decrypt: (encryptedValue: Uint8Array, options: {
stripHashPrefix: boolean;
}) => string | null): Promise<GetCookiesResult>;
//# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/providers/chromeSqlite/shared.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAA0B,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAkB/E,wBAAsB,4BAA4B,CACjD,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EACxF,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAClC,OAAO,EAAE,CAAC,cAAc,EAAE,UAAU,EAAE,OAAO,EAAE;IAAE,eAAe,EAAE,OAAO,CAAA;CAAE,KAAK,MAAM,GAAG,IAAI,GAC3F,OAAO,CAAC,gBAAgB,CAAC,CAsD3B"}
@@ -0,0 +1,293 @@
import { copyFileSync, existsSync, mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { normalizeExpiration } from '../../util/expire.js';
import { hostMatchesCookieDomain } from '../../util/hostMatch.js';
import { importNodeSqlite, supportsReadBigInts } from '../../util/nodeSqlite.js';
import { isBunRuntime } from '../../util/runtime.js';
export async function getCookiesFromChromeSqliteDb(options, origins, allowlistNames, decrypt) {
const warnings = [];
// Chrome can keep its cookie DB locked and/or rely on WAL sidecars.
// Copying to a temp dir gives us a stable snapshot that both node:sqlite and bun:sqlite can open.
const tempDir = mkdtempSync(path.join(tmpdir(), 'sweet-cookie-chrome-'));
const tempDbPath = path.join(tempDir, 'Cookies');
try {
copyFileSync(options.dbPath, tempDbPath);
// If WAL is enabled, the latest writes might live in `Cookies-wal`/`Cookies-shm`.
// Copy them too when present so our snapshot reflects the current browser state.
copySidecar(options.dbPath, `${tempDbPath}-wal`, '-wal');
copySidecar(options.dbPath, `${tempDbPath}-shm`, '-shm');
}
catch (error) {
rmSync(tempDir, { recursive: true, force: true });
warnings.push(`Failed to copy Chrome cookie DB: ${error instanceof Error ? error.message : String(error)}`);
return { cookies: [], warnings };
}
try {
const hosts = origins.map((o) => new URL(o).hostname);
const where = buildHostWhereClause(hosts, 'host_key');
const metaVersion = await readChromiumMetaVersion(tempDbPath);
// Chromium >= 24 stores a 32-byte hash prefix in decrypted cookie values.
// We detect this via the `meta` table version and strip it when present.
const stripHashPrefix = metaVersion >= 24;
const rowsResult = await readChromeRows(tempDbPath, where);
if (!rowsResult.ok) {
warnings.push(rowsResult.error);
return { cookies: [], warnings };
}
const collectOptions = {};
if (options.profile)
collectOptions.profile = options.profile;
if (options.includeExpired !== undefined)
collectOptions.includeExpired = options.includeExpired;
const cookies = collectChromeCookiesFromRows(rowsResult.rows, collectOptions, hosts, allowlistNames, (encryptedValue) => decrypt(encryptedValue, { stripHashPrefix }), warnings);
return { cookies: dedupeCookies(cookies), warnings };
}
finally {
rmSync(tempDir, { recursive: true, force: true });
}
}
function collectChromeCookiesFromRows(rows, options, hosts, allowlistNames, decrypt, warnings) {
const cookies = [];
const now = Math.floor(Date.now() / 1000);
let warnedEncryptedType = false;
for (const row of rows) {
const name = typeof row.name === 'string' ? row.name : null;
if (!name)
continue;
if (allowlistNames && allowlistNames.size > 0 && !allowlistNames.has(name))
continue;
const hostKey = typeof row.host_key === 'string' ? row.host_key : null;
if (!hostKey)
continue;
if (!hostMatchesAny(hosts, hostKey))
continue;
const rowPath = typeof row.path === 'string' ? row.path : '';
const valueString = typeof row.value === 'string' ? row.value : null;
let value = valueString;
if (value === null || value.length === 0) {
// Many modern Chromium cookies keep `value` empty and only store `encrypted_value`.
// We decrypt on demand and drop rows we can't interpret.
const encryptedBytes = getEncryptedBytes(row);
if (!encryptedBytes) {
if (!warnedEncryptedType && row.encrypted_value !== undefined) {
warnings.push('Chrome cookie encrypted_value is in an unsupported type.');
warnedEncryptedType = true;
}
continue;
}
value = decrypt(encryptedBytes);
}
if (value === null)
continue;
const expiresRaw = typeof row.expires_utc === 'number' || typeof row.expires_utc === 'bigint'
? row.expires_utc
: tryParseInt(row.expires_utc);
const expires = normalizeExpiration(expiresRaw ?? undefined);
if (!options.includeExpired) {
if (expires && expires < now)
continue;
}
const secure = row.is_secure === 1 ||
row.is_secure === 1n ||
row.is_secure === '1' ||
row.is_secure === true;
const httpOnly = row.is_httponly === 1 ||
row.is_httponly === 1n ||
row.is_httponly === '1' ||
row.is_httponly === true;
const sameSite = normalizeChromiumSameSite(row.samesite);
const source = { browser: 'chrome' };
if (options.profile)
source.profile = options.profile;
const cookie = {
name,
value,
domain: hostKey.startsWith('.') ? hostKey.slice(1) : hostKey,
path: rowPath || '/',
secure,
httpOnly,
source,
};
if (expires !== undefined)
cookie.expires = expires;
if (sameSite !== undefined)
cookie.sameSite = sameSite;
cookies.push(cookie);
}
return cookies;
}
function tryParseInt(value) {
if (typeof value === 'bigint') {
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : null;
}
if (typeof value !== 'string')
return null;
const parsed = Number.parseInt(value, 10);
return Number.isFinite(parsed) ? parsed : null;
}
function normalizeChromiumSameSite(value) {
if (typeof value === 'bigint') {
const parsed = Number(value);
return Number.isFinite(parsed) ? normalizeChromiumSameSite(parsed) : undefined;
}
if (typeof value === 'number') {
if (value === 2)
return 'Strict';
if (value === 1)
return 'Lax';
if (value === 0)
return 'None';
return undefined;
}
if (typeof value === 'string') {
const parsed = Number.parseInt(value, 10);
if (Number.isFinite(parsed))
return normalizeChromiumSameSite(parsed);
const normalized = value.toLowerCase();
if (normalized === 'strict')
return 'Strict';
if (normalized === 'lax')
return 'Lax';
if (normalized === 'none' || normalized === 'no_restriction')
return 'None';
}
return undefined;
}
function getEncryptedBytes(row) {
const raw = row.encrypted_value;
if (raw instanceof Uint8Array)
return raw;
return null;
}
async function readChromiumMetaVersion(dbPath) {
const sql = `SELECT value FROM meta WHERE key = 'version'`;
const result = isBunRuntime()
? await queryNodeOrBun({ kind: 'bun', dbPath, sql })
: await queryNodeOrBun({ kind: 'node', dbPath, sql });
if (!result.ok)
return 0;
const first = result.rows[0];
const value = first?.value;
if (typeof value === 'number')
return Math.floor(value);
if (typeof value === 'bigint') {
const parsed = Number(value);
return Number.isFinite(parsed) ? Math.floor(parsed) : 0;
}
if (typeof value === 'string') {
const parsed = Number.parseInt(value, 10);
return Number.isFinite(parsed) ? parsed : 0;
}
return 0;
}
async function readChromeRows(dbPath, where) {
const sqliteKind = isBunRuntime() ? 'bun' : 'node';
const sqliteLabel = sqliteKind === 'bun' ? 'bun:sqlite' : 'node:sqlite';
const sql = `SELECT name, value, host_key, path, expires_utc, samesite, encrypted_value, ` +
`is_secure AS is_secure, is_httponly AS is_httponly ` +
`FROM cookies WHERE (${where}) ORDER BY expires_utc DESC;`;
const result = await queryNodeOrBun({ kind: sqliteKind, dbPath, sql });
if (result.ok)
return { ok: true, rows: result.rows };
// Intentionally strict: only support modern Chromium cookie DB schemas.
// If this fails, assume the local Chrome/Chromium is too old or uses a non-standard schema.
return {
ok: false,
error: `${sqliteLabel} failed reading Chrome cookies (requires modern Chromium, e.g. Chrome >= 100): ${result.error}`,
};
}
async function queryNodeOrBun(options) {
try {
if (options.kind === 'node') {
// Node's `node:sqlite` is synchronous and returns plain JS values. Keep it boxed in a
// small scope so callers don't need to care about runtime differences.
const { DatabaseSync } = await importNodeSqlite();
const dbOptions = { readOnly: true };
if (supportsReadBigInts()) {
dbOptions.readBigInts = true;
}
const db = new DatabaseSync(options.dbPath, dbOptions);
try {
const rows = db.prepare(options.sql).all();
return { ok: true, rows };
}
finally {
db.close();
}
}
// Bun's sqlite API has a different surface (`Database` + `.query().all()`).
const { Database } = await import('bun:sqlite');
const db = new Database(options.dbPath, { readonly: true });
try {
const rows = db.query(options.sql).all();
return { ok: true, rows };
}
finally {
db.close();
}
}
catch (error) {
return { ok: false, error: error instanceof Error ? error.message : String(error) };
}
}
function copySidecar(sourceDbPath, target, suffix) {
const sidecar = `${sourceDbPath}${suffix}`;
if (!existsSync(sidecar))
return;
try {
copyFileSync(sidecar, target);
}
catch {
// ignore
}
}
function buildHostWhereClause(hosts, column) {
const clauses = [];
for (const host of hosts) {
// Chrome cookies often live on parent domains (e.g. .google.com for gemini.google.com).
// Include parent domains so the SQL filter doesn't drop valid session cookies.
for (const candidate of expandHostCandidates(host)) {
const escaped = sqlLiteral(candidate);
const escapedDot = sqlLiteral(`.${candidate}`);
const escapedLike = sqlLiteral(`%.${candidate}`);
clauses.push(`${column} = ${escaped}`);
clauses.push(`${column} = ${escapedDot}`);
clauses.push(`${column} LIKE ${escapedLike}`);
}
}
return clauses.length ? clauses.join(' OR ') : '1=0';
}
function sqlLiteral(value) {
const escaped = value.replaceAll("'", "''");
return `'${escaped}'`;
}
function expandHostCandidates(host) {
const parts = host.split('.').filter(Boolean);
if (parts.length <= 1)
return [host];
const candidates = new Set();
candidates.add(host);
// Include parent domains down to two labels (avoid TLD-only fragments).
for (let i = 1; i <= parts.length - 2; i += 1) {
const candidate = parts.slice(i).join('.');
if (candidate)
candidates.add(candidate);
}
return Array.from(candidates);
}
function hostMatchesAny(hosts, cookieHost) {
const cookieDomain = cookieHost.startsWith('.') ? cookieHost.slice(1) : cookieHost;
return hosts.some((host) => hostMatchesCookieDomain(host, cookieDomain));
}
function dedupeCookies(cookies) {
const merged = new Map();
for (const cookie of cookies) {
const key = `${cookie.name}|${cookie.domain ?? ''}|${cookie.path ?? ''}`;
if (!merged.has(key))
merged.set(key, cookie);
}
return Array.from(merged.values());
}
//# sourceMappingURL=shared.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,10 @@
export declare function dpapiUnprotect(data: Buffer, options?: {
timeoutMs?: number;
}): Promise<{
ok: true;
value: Buffer;
} | {
ok: false;
error: string;
}>;
//# sourceMappingURL=windowsDpapi.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"windowsDpapi.d.ts","sourceRoot":"","sources":["../../../src/providers/chromeSqlite/windowsDpapi.ts"],"names":[],"mappings":"AAEA,wBAAsB,cAAc,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CA+BrE"}
@@ -0,0 +1,26 @@
import { execCapture } from '../../util/exec.js';
export async function dpapiUnprotect(data, options = {}) {
const timeoutMs = options.timeoutMs ?? 5_000;
// There is no cross-platform JS API for Windows DPAPI, and we explicitly avoid native addons.
// PowerShell can call ProtectedData.Unprotect for the current user, which matches Chrome's behavior.
const inputB64 = data.toString('base64');
const prelude = 'try { Add-Type -AssemblyName System.Security.Cryptography.ProtectedData -ErrorAction Stop } catch { try { Add-Type -AssemblyName System.Security -ErrorAction Stop } catch {} };';
const script = prelude +
`$in=[Convert]::FromBase64String('${inputB64}');` +
`$out=[System.Security.Cryptography.ProtectedData]::Unprotect($in,$null,[System.Security.Cryptography.DataProtectionScope]::CurrentUser);` +
`[Convert]::ToBase64String($out)`;
const res = await execCapture('powershell', ['-NoProfile', '-NonInteractive', '-Command', script], {
timeoutMs,
});
if (res.code !== 0) {
return { ok: false, error: res.stderr.trim() || `powershell exit ${res.code}` };
}
try {
const out = Buffer.from(res.stdout.trim(), 'base64');
return { ok: true, value: out };
}
catch (error) {
return { ok: false, error: error instanceof Error ? error.message : String(error) };
}
}
//# sourceMappingURL=windowsDpapi.js.map
@@ -0,0 +1 @@
{"version":3,"file":"windowsDpapi.js","sourceRoot":"","sources":["../../../src/providers/chromeSqlite/windowsDpapi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,IAAY,EACZ,UAAkC,EAAE;IAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAE7C,8FAA8F;IAC9F,qGAAqG;IACrG,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,OAAO,GACZ,kLAAkL,CAAC;IACpL,MAAM,MAAM,GACX,OAAO;QACP,oCAAoC,QAAQ,KAAK;QACjD,0IAA0I;QAC1I,iCAAiC,CAAC;IAEnC,MAAM,GAAG,GAAG,MAAM,WAAW,CAC5B,YAAY,EACZ,CAAC,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,CAAC,EACrD;QACC,SAAS;KACT,CACD,CAAC;IACF,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,mBAAmB,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;IACjF,CAAC;IAED,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC;QACrD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IACrF,CAAC;AACF,CAAC"}
@@ -0,0 +1,7 @@
import type { GetCookiesResult } from '../types.js';
export declare function getCookiesFromChromeSqliteLinux(options: {
profile?: string;
includeExpired?: boolean;
debug?: boolean;
}, origins: string[], allowlistNames: Set<string> | null): Promise<GetCookiesResult>;
//# sourceMappingURL=chromeSqliteLinux.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"chromeSqliteLinux.d.ts","sourceRoot":"","sources":["../../src/providers/chromeSqliteLinux.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AASpD,wBAAsB,+BAA+B,CACpD,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EACxE,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAChC,OAAO,CAAC,gBAAgB,CAAC,CAkD3B"}
@@ -0,0 +1,51 @@
import { decryptChromiumAes128CbcCookieValue, deriveAes128CbcKeyFromPassword, } from './chromeSqlite/crypto.js';
import { getLinuxChromeSafeStoragePassword } from './chromeSqlite/linuxKeyring.js';
import { getCookiesFromChromeSqliteDb } from './chromeSqlite/shared.js';
import { resolveChromiumCookiesDbLinux } from './chromium/linuxPaths.js';
export async function getCookiesFromChromeSqliteLinux(options, origins, allowlistNames) {
const args = {
configDirName: 'google-chrome',
};
if (options.profile !== undefined)
args.profile = options.profile;
const dbPath = resolveChromiumCookiesDbLinux(args);
if (!dbPath) {
return { cookies: [], warnings: ['Chrome cookies database not found.'] };
}
const { password, warnings: keyringWarnings } = await getLinuxChromeSafeStoragePassword();
// Linux uses multiple schemes depending on distro/keyring availability.
// - v10 often uses the hard-coded "peanuts" password
// - v11 uses "Chrome Safe Storage" from the keyring (may be empty/unavailable)
const v10Key = deriveAes128CbcKeyFromPassword('peanuts', { iterations: 1 });
const emptyKey = deriveAes128CbcKeyFromPassword('', { iterations: 1 });
const v11Key = deriveAes128CbcKeyFromPassword(password, { iterations: 1 });
const decrypt = (encryptedValue, opts) => {
const prefix = Buffer.from(encryptedValue).subarray(0, 3).toString('utf8');
if (prefix === 'v10') {
return decryptChromiumAes128CbcCookieValue(encryptedValue, [v10Key, emptyKey], {
stripHashPrefix: opts.stripHashPrefix,
treatUnknownPrefixAsPlaintext: false,
});
}
if (prefix === 'v11') {
return decryptChromiumAes128CbcCookieValue(encryptedValue, [v11Key, emptyKey], {
stripHashPrefix: opts.stripHashPrefix,
treatUnknownPrefixAsPlaintext: false,
});
}
return null;
};
const dbOptions = {
dbPath,
};
if (options.profile)
dbOptions.profile = options.profile;
if (options.includeExpired !== undefined)
dbOptions.includeExpired = options.includeExpired;
if (options.debug !== undefined)
dbOptions.debug = options.debug;
const result = await getCookiesFromChromeSqliteDb(dbOptions, origins, allowlistNames, decrypt);
result.warnings.unshift(...keyringWarnings);
return result;
}
//# sourceMappingURL=chromeSqliteLinux.js.map
@@ -0,0 +1 @@
{"version":3,"file":"chromeSqliteLinux.js","sourceRoot":"","sources":["../../src/providers/chromeSqliteLinux.ts"],"names":[],"mappings":"AACA,OAAO,EACN,mCAAmC,EACnC,8BAA8B,GAC9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,iCAAiC,EAAE,MAAM,gCAAgC,CAAC;AACnF,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AAEzE,MAAM,CAAC,KAAK,UAAU,+BAA+B,CACpD,OAAwE,EACxE,OAAiB,EACjB,cAAkC;IAElC,MAAM,IAAI,GAAwD;QACjE,aAAa,EAAE,eAAe;KAC9B,CAAC;IACF,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS;QAAE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAClE,MAAM,MAAM,GAAG,6BAA6B,CAAC,IAAI,CAAC,CAAC;IACnD,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,oCAAoC,CAAC,EAAE,CAAC;IAC1E,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM,iCAAiC,EAAE,CAAC;IAE1F,wEAAwE;IACxE,qDAAqD;IACrD,+EAA+E;IAC/E,MAAM,MAAM,GAAG,8BAA8B,CAAC,SAAS,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5E,MAAM,QAAQ,GAAG,8BAA8B,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,8BAA8B,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;IAE3E,MAAM,OAAO,GAAG,CACf,cAA0B,EAC1B,IAAkC,EAClB,EAAE;QAClB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC3E,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACtB,OAAO,mCAAmC,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE;gBAC9E,eAAe,EAAE,IAAI,CAAC,eAAe;gBACrC,6BAA6B,EAAE,KAAK;aACpC,CAAC,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACtB,OAAO,mCAAmC,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE;gBAC9E,eAAe,EAAE,IAAI,CAAC,eAAe;gBACrC,6BAA6B,EAAE,KAAK;aACpC,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC,CAAC;IAEF,MAAM,SAAS,GACd;QACC,MAAM;KACN,CAAC;IACH,IAAI,OAAO,CAAC,OAAO;QAAE,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACzD,IAAI,OAAO,CAAC,cAAc,KAAK,SAAS;QAAE,SAAS,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAC5F,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,SAAS,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAEjE,MAAM,MAAM,GAAG,MAAM,4BAA4B,CAAC,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;IAC/F,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,eAAe,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC;AACf,CAAC"}
@@ -0,0 +1,7 @@
import type { GetCookiesResult } from '../types.js';
export declare function getCookiesFromChromeSqliteMac(options: {
profile?: string;
includeExpired?: boolean;
debug?: boolean;
}, origins: string[], allowlistNames: Set<string> | null): Promise<GetCookiesResult>;
//# sourceMappingURL=chromeSqliteMac.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"chromeSqliteMac.d.ts","sourceRoot":"","sources":["../../src/providers/chromeSqliteMac.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AASpD,wBAAsB,6BAA6B,CAClD,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EACxE,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAChC,OAAO,CAAC,gBAAgB,CAAC,CA6C3B"}
@@ -0,0 +1,60 @@
import { homedir } from 'node:os';
import path from 'node:path';
import { decryptChromiumAes128CbcCookieValue, deriveAes128CbcKeyFromPassword, } from './chromeSqlite/crypto.js';
import { getCookiesFromChromeSqliteDb } from './chromeSqlite/shared.js';
import { readKeychainGenericPasswordFirst } from './chromium/macosKeychain.js';
import { resolveCookiesDbFromProfileOrRoots } from './chromium/paths.js';
export async function getCookiesFromChromeSqliteMac(options, origins, allowlistNames) {
const dbPath = resolveChromeCookiesDb(options.profile);
if (!dbPath) {
return { cookies: [], warnings: ['Chrome cookies database not found.'] };
}
const warnings = [];
// On macOS, Chrome stores its "Safe Storage" secret in Keychain.
// `security find-generic-password` is stable and avoids any native Node keychain modules.
const passwordResult = await readKeychainGenericPasswordFirst({
account: 'Chrome',
services: ['Chrome Safe Storage'],
timeoutMs: 3_000,
label: 'Chrome Safe Storage',
});
if (!passwordResult.ok) {
warnings.push(passwordResult.error);
return { cookies: [], warnings };
}
const chromePassword = passwordResult.password.trim();
if (!chromePassword) {
warnings.push('macOS Keychain returned an empty Chrome Safe Storage password.');
return { cookies: [], warnings };
}
// Chromium uses PBKDF2(password, "saltysalt", 1003, 16, sha1) for AES-128-CBC cookie values on macOS.
const key = deriveAes128CbcKeyFromPassword(chromePassword, { iterations: 1003 });
const decrypt = (encryptedValue, opts) => decryptChromiumAes128CbcCookieValue(encryptedValue, [key], {
stripHashPrefix: opts.stripHashPrefix,
treatUnknownPrefixAsPlaintext: true,
});
const dbOptions = {
dbPath,
};
if (options.profile)
dbOptions.profile = options.profile;
if (options.includeExpired !== undefined)
dbOptions.includeExpired = options.includeExpired;
if (options.debug !== undefined)
dbOptions.debug = options.debug;
const result = await getCookiesFromChromeSqliteDb(dbOptions, origins, allowlistNames, decrypt);
result.warnings.unshift(...warnings);
return result;
}
function resolveChromeCookiesDb(profile) {
const home = homedir();
/* c8 ignore next */
const roots = process.platform === 'darwin'
? [path.join(home, 'Library', 'Application Support', 'Google', 'Chrome')]
: [];
const args = { roots };
if (profile !== undefined)
args.profile = profile;
return resolveCookiesDbFromProfileOrRoots(args);
}
//# sourceMappingURL=chromeSqliteMac.js.map
@@ -0,0 +1 @@
{"version":3,"file":"chromeSqliteMac.js","sourceRoot":"","sources":["../../src/providers/chromeSqliteMac.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
import type { GetCookiesResult } from '../types.js';
export declare function getCookiesFromChromeSqliteWindows(options: {
profile?: string;
includeExpired?: boolean;
debug?: boolean;
}, origins: string[], allowlistNames: Set<string> | null): Promise<GetCookiesResult>;
//# sourceMappingURL=chromeSqliteWindows.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"chromeSqliteWindows.d.ts","sourceRoot":"","sources":["../../src/providers/chromeSqliteWindows.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAMpD,wBAAsB,iCAAiC,CACtD,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EACxE,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAChC,OAAO,CAAC,gBAAgB,CAAC,CAmC3B"}
@@ -0,0 +1,38 @@
import path from 'node:path';
import { decryptChromiumAes256GcmCookieValue } from './chromeSqlite/crypto.js';
import { getCookiesFromChromeSqliteDb } from './chromeSqlite/shared.js';
import { getWindowsChromiumMasterKey } from './chromium/windowsMasterKey.js';
import { resolveChromiumPathsWindows } from './chromium/windowsPaths.js';
export async function getCookiesFromChromeSqliteWindows(options, origins, allowlistNames) {
const resolveArgs = {
localAppDataVendorPath: path.join('Google', 'Chrome', 'User Data'),
};
if (options.profile !== undefined)
resolveArgs.profile = options.profile;
const { dbPath, userDataDir } = resolveChromiumPathsWindows(resolveArgs);
if (!dbPath || !userDataDir) {
return { cookies: [], warnings: ['Chrome cookies database not found.'] };
}
// On Windows, Chrome stores an AES key in `Local State` encrypted with DPAPI (CurrentUser).
// That master key is then used for AES-256-GCM cookie values (`v10`/`v11`/`v20` prefixes).
const masterKey = await getWindowsChromiumMasterKey(userDataDir, 'Chrome');
if (!masterKey.ok) {
return { cookies: [], warnings: [masterKey.error] };
}
const decrypt = (encryptedValue, opts) => {
return decryptChromiumAes256GcmCookieValue(encryptedValue, masterKey.value, {
stripHashPrefix: opts.stripHashPrefix,
});
};
const dbOptions = {
dbPath,
};
if (options.profile)
dbOptions.profile = options.profile;
if (options.includeExpired !== undefined)
dbOptions.includeExpired = options.includeExpired;
if (options.debug !== undefined)
dbOptions.debug = options.debug;
return await getCookiesFromChromeSqliteDb(dbOptions, origins, allowlistNames, decrypt);
}
//# sourceMappingURL=chromeSqliteWindows.js.map
@@ -0,0 +1 @@
{"version":3,"file":"chromeSqliteWindows.js","sourceRoot":"","sources":["../../src/providers/chromeSqliteWindows.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,mCAAmC,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAEzE,MAAM,CAAC,KAAK,UAAU,iCAAiC,CACtD,OAAwE,EACxE,OAAiB,EACjB,cAAkC;IAElC,MAAM,WAAW,GAAsD;QACtE,sBAAsB,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,CAAC;KAClE,CAAC;IACF,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS;QAAE,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACzE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,2BAA2B,CAAC,WAAW,CAAC,CAAC;IACzE,IAAI,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAC7B,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,oCAAoC,CAAC,EAAE,CAAC;IAC1E,CAAC;IAED,4FAA4F;IAC5F,2FAA2F;IAC3F,MAAM,SAAS,GAAG,MAAM,2BAA2B,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAC3E,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QACnB,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;IACrD,CAAC;IAED,MAAM,OAAO,GAAG,CACf,cAA0B,EAC1B,IAAkC,EAClB,EAAE;QAClB,OAAO,mCAAmC,CAAC,cAAc,EAAE,SAAS,CAAC,KAAK,EAAE;YAC3E,eAAe,EAAE,IAAI,CAAC,eAAe;SACrC,CAAC,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,SAAS,GACd;QACC,MAAM;KACN,CAAC;IACH,IAAI,OAAO,CAAC,OAAO;QAAE,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACzD,IAAI,OAAO,CAAC,cAAc,KAAK,SAAS;QAAE,SAAS,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAC5F,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,SAAS,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAEjE,OAAO,MAAM,4BAA4B,CAAC,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;AACxF,CAAC"}
@@ -0,0 +1,5 @@
export declare function resolveChromiumCookiesDbLinux(options: {
configDirName: string;
profile?: string;
}): string | null;
//# sourceMappingURL=linuxPaths.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"linuxPaths.d.ts","sourceRoot":"","sources":["../../../src/providers/chromium/linuxPaths.ts"],"names":[],"mappings":"AAMA,wBAAgB,6BAA6B,CAAC,OAAO,EAAE;IACtD,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,GAAG,IAAI,CA0BhB"}
@@ -0,0 +1,33 @@
import { existsSync } from 'node:fs';
import { homedir } from 'node:os';
import path from 'node:path';
import { expandPath, looksLikePath } from './paths.js';
export function resolveChromiumCookiesDbLinux(options) {
const home = homedir();
// biome-ignore lint/complexity/useLiteralKeys: process.env is an index signature under strict TS.
const configHome = process.env['XDG_CONFIG_HOME']?.trim() || path.join(home, '.config');
const root = path.join(configHome, options.configDirName);
if (options.profile && looksLikePath(options.profile)) {
const candidate = expandPath(options.profile);
if (candidate.endsWith('Cookies') && existsSync(candidate))
return candidate;
const direct = path.join(candidate, 'Cookies');
if (existsSync(direct))
return direct;
const network = path.join(candidate, 'Network', 'Cookies');
if (existsSync(network))
return network;
return null;
}
const profileDir = options.profile && options.profile.trim().length > 0 ? options.profile.trim() : 'Default';
const candidates = [
path.join(root, profileDir, 'Cookies'),
path.join(root, profileDir, 'Network', 'Cookies'),
];
for (const candidate of candidates) {
if (existsSync(candidate))
return candidate;
}
return null;
}
//# sourceMappingURL=linuxPaths.js.map
@@ -0,0 +1 @@
{"version":3,"file":"linuxPaths.js","sourceRoot":"","sources":["../../../src/providers/chromium/linuxPaths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEvD,MAAM,UAAU,6BAA6B,CAAC,OAG7C;IACA,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,kGAAkG;IAClG,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACxF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAE1D,IAAI,OAAO,CAAC,OAAO,IAAI,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACvD,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;QAC7E,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC/C,IAAI,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QAC3D,IAAI,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO,OAAO,CAAC;QACxC,OAAO,IAAI,CAAC;IACb,CAAC;IAED,MAAM,UAAU,GACf,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3F,MAAM,UAAU,GAAG;QAClB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC;QACtC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC;KACjD,CAAC;IACF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,IAAI,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;IAC7C,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC"}
@@ -0,0 +1,24 @@
export declare function readKeychainGenericPassword(options: {
account: string;
service: string;
timeoutMs: number;
}): Promise<{
ok: true;
password: string;
} | {
ok: false;
error: string;
}>;
export declare function readKeychainGenericPasswordFirst(options: {
account: string;
services: string[];
timeoutMs: number;
label: string;
}): Promise<{
ok: true;
password: string;
} | {
ok: false;
error: string;
}>;
//# sourceMappingURL=macosKeychain.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"macosKeychain.d.ts","sourceRoot":"","sources":["../../../src/providers/chromium/macosKeychain.ts"],"names":[],"mappings":"AAEA,wBAAsB,2BAA2B,CAAC,OAAO,EAAE;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAczE;AAED,wBAAsB,gCAAgC,CAAC,OAAO,EAAE;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAgBzE"}
@@ -0,0 +1,30 @@
import { execCapture } from '../../util/exec.js';
export async function readKeychainGenericPassword(options) {
const res = await execCapture('security', ['find-generic-password', '-w', '-a', options.account, '-s', options.service], { timeoutMs: options.timeoutMs });
if (res.code === 0) {
const password = res.stdout.trim();
return { ok: true, password };
}
return {
ok: false,
error: `${res.stderr.trim() || `exit ${res.code}`}`,
};
}
export async function readKeychainGenericPasswordFirst(options) {
let lastError = null;
for (const service of options.services) {
const r = await readKeychainGenericPassword({
account: options.account,
service,
timeoutMs: options.timeoutMs,
});
if (r.ok)
return r;
lastError = r.error;
}
return {
ok: false,
error: `Failed to read macOS Keychain (${options.label}): ${lastError ?? 'permission denied / keychain locked / entry missing.'}`,
};
}
//# sourceMappingURL=macosKeychain.js.map
@@ -0,0 +1 @@
{"version":3,"file":"macosKeychain.js","sourceRoot":"","sources":["../../../src/providers/chromium/macosKeychain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,OAIjD;IACA,MAAM,GAAG,GAAG,MAAM,WAAW,CAC5B,UAAU,EACV,CAAC,uBAAuB,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,EAC7E,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAChC,CAAC;IACF,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACnC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC/B,CAAC;IACD,OAAO;QACN,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,QAAQ,GAAG,CAAC,IAAI,EAAE,EAAE;KACnD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gCAAgC,CAAC,OAKtD;IACA,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,MAAM,2BAA2B,CAAC;YAC3C,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,OAAO;YACP,SAAS,EAAE,OAAO,CAAC,SAAS;SAC5B,CAAC,CAAC;QACH,IAAI,CAAC,CAAC,EAAE;YAAE,OAAO,CAAC,CAAC;QACnB,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC;IACrB,CAAC;IAED,OAAO;QACN,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,kCAAkC,OAAO,CAAC,KAAK,MAAM,SAAS,IAAI,sDAAsD,EAAE;KACjI,CAAC;AACH,CAAC"}
@@ -0,0 +1,11 @@
export declare function looksLikePath(value: string): boolean;
export declare function expandPath(input: string): string;
export declare function safeStat(candidate: string): {
isFile: () => boolean;
isDirectory: () => boolean;
} | null;
export declare function resolveCookiesDbFromProfileOrRoots(options: {
profile?: string;
roots: string[];
}): string | null;
//# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/providers/chromium/paths.ts"],"names":[],"mappings":"AAIA,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGhD;AAED,wBAAgB,QAAQ,CACvB,SAAS,EAAE,MAAM,GACf;IAAE,MAAM,EAAE,MAAM,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,OAAO,CAAA;CAAE,GAAG,IAAI,CAM9D;AAED,wBAAgB,kCAAkC,CAAC,OAAO,EAAE;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;CAChB,GAAG,MAAM,GAAG,IAAI,CAuBhB"}
@@ -0,0 +1,43 @@
import { existsSync, statSync } from 'node:fs';
import { homedir } from 'node:os';
import path from 'node:path';
export function looksLikePath(value) {
return value.includes('/') || value.includes('\\');
}
export function expandPath(input) {
if (input.startsWith('~/'))
return path.join(homedir(), input.slice(2));
return path.isAbsolute(input) ? input : path.resolve(process.cwd(), input);
}
export function safeStat(candidate) {
try {
return statSync(candidate);
}
catch {
return null;
}
}
export function resolveCookiesDbFromProfileOrRoots(options) {
const candidates = [];
if (options.profile && looksLikePath(options.profile)) {
const expanded = expandPath(options.profile);
const stat = safeStat(expanded);
if (stat?.isFile())
return expanded;
candidates.push(path.join(expanded, 'Cookies'));
candidates.push(path.join(expanded, 'Network', 'Cookies'));
}
else {
const profileDir = options.profile && options.profile.trim().length > 0 ? options.profile.trim() : 'Default';
for (const root of options.roots) {
candidates.push(path.join(root, profileDir, 'Cookies'));
candidates.push(path.join(root, profileDir, 'Network', 'Cookies'));
}
}
for (const candidate of candidates) {
if (existsSync(candidate))
return candidate;
}
return null;
}
//# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/providers/chromium/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,UAAU,aAAa,CAAC,KAAa;IAC1C,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACvC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,QAAQ,CACvB,SAAiB;IAEjB,IAAI,CAAC;QACJ,OAAO,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,MAAM,UAAU,kCAAkC,CAAC,OAGlD;IACA,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,IAAI,OAAO,CAAC,OAAO,IAAI,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACvD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAChC,IAAI,IAAI,EAAE,MAAM,EAAE;YAAE,OAAO,QAAQ,CAAC;QACpC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;QAChD,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;IAC5D,CAAC;SAAM,CAAC;QACP,MAAM,UAAU,GACf,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3F,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC;YACxD,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;QACpE,CAAC;IACF,CAAC;IAED,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,IAAI,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;IAC7C,CAAC;IAED,OAAO,IAAI,CAAC;AACb,CAAC"}
@@ -0,0 +1,8 @@
export declare function getWindowsChromiumMasterKey(userDataDir: string, label: string): Promise<{
ok: true;
value: Buffer;
} | {
ok: false;
error: string;
}>;
//# sourceMappingURL=windowsMasterKey.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"windowsMasterKey.d.ts","sourceRoot":"","sources":["../../../src/providers/chromium/windowsMasterKey.ts"],"names":[],"mappings":"AAKA,wBAAsB,2BAA2B,CAChD,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,GACX,OAAO,CACP;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC3B;IACA,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACb,CACH,CAsCA"}
@@ -0,0 +1,41 @@
import { existsSync, readFileSync } from 'node:fs';
import path from 'node:path';
import { dpapiUnprotect } from '../chromeSqlite/windowsDpapi.js';
export async function getWindowsChromiumMasterKey(userDataDir, label) {
const localStatePath = path.join(userDataDir, 'Local State');
if (!existsSync(localStatePath)) {
return { ok: false, error: `${label} Local State file not found.` };
}
let encryptedKeyB64 = null;
try {
const raw = readFileSync(localStatePath, 'utf8');
const parsed = JSON.parse(raw);
encryptedKeyB64 =
typeof parsed.os_crypt?.encrypted_key === 'string' ? parsed.os_crypt.encrypted_key : null;
}
catch (error) {
return {
ok: false,
error: `Failed to parse ${label} Local State: ${error instanceof Error ? error.message : String(error)}`,
};
}
if (!encryptedKeyB64)
return { ok: false, error: `${label} Local State missing os_crypt.encrypted_key.` };
let encryptedKey;
try {
encryptedKey = Buffer.from(encryptedKeyB64, 'base64');
}
catch {
return { ok: false, error: `${label} Local State contains an invalid encrypted_key.` };
}
const prefix = Buffer.from('DPAPI', 'utf8');
if (!encryptedKey.subarray(0, prefix.length).equals(prefix)) {
return { ok: false, error: `${label} encrypted_key does not start with DPAPI prefix.` };
}
const unprotected = await dpapiUnprotect(encryptedKey.subarray(prefix.length));
if (!unprotected.ok) {
return { ok: false, error: `DPAPI decrypt failed: ${unprotected.error}` };
}
return { ok: true, value: unprotected.value };
}
//# sourceMappingURL=windowsMasterKey.js.map
@@ -0,0 +1 @@
{"version":3,"file":"windowsMasterKey.js","sourceRoot":"","sources":["../../../src/providers/chromium/windowsMasterKey.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjE,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAChD,WAAmB,EACnB,KAAa;IAQb,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAC7D,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,8BAA8B,EAAE,CAAC;IACrE,CAAC;IACD,IAAI,eAAe,GAAkB,IAAI,CAAC;IAC1C,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA+C,CAAC;QAC7E,eAAe;YACd,OAAO,MAAM,CAAC,QAAQ,EAAE,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5F,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO;YACN,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,mBAAmB,KAAK,iBAAiB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;SACxG,CAAC;IACH,CAAC;IAED,IAAI,CAAC,eAAe;QACnB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,8CAA8C,EAAE,CAAC;IAErF,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACJ,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,iDAAiD,EAAE,CAAC;IACxF,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,kDAAkD,EAAE,CAAC;IACzF,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,cAAc,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/E,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC;QACrB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,yBAAyB,WAAW,CAAC,KAAK,EAAE,EAAE,CAAC;IAC3E,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,8 @@
export declare function resolveChromiumPathsWindows(options: {
localAppDataVendorPath: string;
profile?: string;
}): {
dbPath: string | null;
userDataDir: string | null;
};
//# sourceMappingURL=windowsPaths.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"windowsPaths.d.ts","sourceRoot":"","sources":["../../../src/providers/chromium/windowsPaths.ts"],"names":[],"mappings":"AAKA,wBAAgB,2BAA2B,CAAC,OAAO,EAAE;IACpD,sBAAsB,EAAE,MAAM,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAmCxD"}
@@ -0,0 +1,53 @@
import { existsSync } from 'node:fs';
import path from 'node:path';
import { expandPath, looksLikePath } from './paths.js';
export function resolveChromiumPathsWindows(options) {
// biome-ignore lint/complexity/useLiteralKeys: process.env is an index signature under strict TS.
const localAppData = process.env['LOCALAPPDATA'];
const root = localAppData ? path.join(localAppData, options.localAppDataVendorPath) : null;
if (options.profile && looksLikePath(options.profile)) {
const expanded = expandPath(options.profile);
const candidates = expanded.endsWith('Cookies')
? [expanded]
: [
path.join(expanded, 'Network', 'Cookies'),
path.join(expanded, 'Cookies'),
path.join(expanded, 'Default', 'Network', 'Cookies'),
];
for (const candidate of candidates) {
if (!existsSync(candidate))
continue;
const userDataDir = findUserDataDir(candidate);
return { dbPath: candidate, userDataDir };
}
if (existsSync(path.join(expanded, 'Local State'))) {
return { dbPath: null, userDataDir: expanded };
}
}
const profileDir = options.profile && options.profile.trim().length > 0 ? options.profile.trim() : 'Default';
if (!root)
return { dbPath: null, userDataDir: null };
const candidates = [
path.join(root, profileDir, 'Network', 'Cookies'),
path.join(root, profileDir, 'Cookies'),
];
for (const candidate of candidates) {
if (existsSync(candidate))
return { dbPath: candidate, userDataDir: root };
}
return { dbPath: null, userDataDir: root };
}
function findUserDataDir(cookiesDbPath) {
let current = path.dirname(cookiesDbPath);
for (let i = 0; i < 6; i += 1) {
const localState = path.join(current, 'Local State');
if (existsSync(localState))
return current;
const next = path.dirname(current);
if (next === current)
break;
current = next;
}
return null;
}
//# sourceMappingURL=windowsPaths.js.map
@@ -0,0 +1 @@
{"version":3,"file":"windowsPaths.js","sourceRoot":"","sources":["../../../src/providers/chromium/windowsPaths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
import type { GetCookiesResult } from '../types.js';
export declare function getCookiesFromEdge(options: {
profile?: string;
timeoutMs?: number;
includeExpired?: boolean;
debug?: boolean;
}, origins: string[], allowlistNames: Set<string> | null): Promise<GetCookiesResult>;
//# sourceMappingURL=edge.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"edge.d.ts","sourceRoot":"","sources":["../../src/providers/edge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAK5D,wBAAsB,kBAAkB,CACvC,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EAC5F,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAChC,OAAO,CAAC,gBAAgB,CAAC,CA0B3B"}
@@ -0,0 +1,27 @@
import { getCookiesFromEdgeSqliteLinux } from './edgeSqliteLinux.js';
import { getCookiesFromEdgeSqliteMac } from './edgeSqliteMac.js';
import { getCookiesFromEdgeSqliteWindows } from './edgeSqliteWindows.js';
export async function getCookiesFromEdge(options, origins, allowlistNames) {
const warnings = [];
// Platform dispatch only. All real logic lives in the per-OS providers.
if (process.platform === 'darwin') {
const r = await getCookiesFromEdgeSqliteMac(options, origins, allowlistNames);
warnings.push(...r.warnings);
const cookies = r.cookies;
return { cookies, warnings };
}
if (process.platform === 'linux') {
const r = await getCookiesFromEdgeSqliteLinux(options, origins, allowlistNames);
warnings.push(...r.warnings);
const cookies = r.cookies;
return { cookies, warnings };
}
if (process.platform === 'win32') {
const r = await getCookiesFromEdgeSqliteWindows(options, origins, allowlistNames);
warnings.push(...r.warnings);
const cookies = r.cookies;
return { cookies, warnings };
}
return { cookies: [], warnings };
}
//# sourceMappingURL=edge.js.map
@@ -0,0 +1 @@
{"version":3,"file":"edge.js","sourceRoot":"","sources":["../../src/providers/edge.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAC;AAEzE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACvC,OAA4F,EAC5F,OAAiB,EACjB,cAAkC;IAElC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,wEAAwE;IACxE,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACnC,MAAM,CAAC,GAAG,MAAM,2BAA2B,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;QAC9E,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAa,CAAC,CAAC,OAAO,CAAC;QACpC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IAC9B,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAClC,MAAM,CAAC,GAAG,MAAM,6BAA6B,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;QAChF,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAa,CAAC,CAAC,OAAO,CAAC;QACpC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IAC9B,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAClC,MAAM,CAAC,GAAG,MAAM,+BAA+B,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;QAClF,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAa,CAAC,CAAC,OAAO,CAAC;QACpC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IAC9B,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,7 @@
import type { GetCookiesResult } from '../types.js';
export declare function getCookiesFromEdgeSqliteLinux(options: {
profile?: string;
includeExpired?: boolean;
debug?: boolean;
}, origins: string[], allowlistNames: Set<string> | null): Promise<GetCookiesResult>;
//# sourceMappingURL=edgeSqliteLinux.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"edgeSqliteLinux.d.ts","sourceRoot":"","sources":["../../src/providers/edgeSqliteLinux.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AASpD,wBAAsB,6BAA6B,CAClD,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EACxE,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAChC,OAAO,CAAC,gBAAgB,CAAC,CAoD3B"}
@@ -0,0 +1,53 @@
import { decryptChromiumAes128CbcCookieValue, deriveAes128CbcKeyFromPassword, } from './chromeSqlite/crypto.js';
import { getLinuxChromiumSafeStoragePassword } from './chromeSqlite/linuxKeyring.js';
import { getCookiesFromChromeSqliteDb } from './chromeSqlite/shared.js';
import { resolveChromiumCookiesDbLinux } from './chromium/linuxPaths.js';
export async function getCookiesFromEdgeSqliteLinux(options, origins, allowlistNames) {
const args = {
configDirName: 'microsoft-edge',
};
if (options.profile !== undefined)
args.profile = options.profile;
const dbPath = resolveChromiumCookiesDbLinux(args);
if (!dbPath) {
return { cookies: [], warnings: ['Edge cookies database not found.'] };
}
const { password, warnings: keyringWarnings } = await getLinuxChromiumSafeStoragePassword({
app: 'edge',
});
// Linux uses multiple schemes depending on distro/keyring availability.
// - v10 often uses the hard-coded "peanuts" password
// - v11 uses "<browser> Safe Storage" from the keyring (may be empty/unavailable)
const v10Key = deriveAes128CbcKeyFromPassword('peanuts', { iterations: 1 });
const emptyKey = deriveAes128CbcKeyFromPassword('', { iterations: 1 });
const v11Key = deriveAes128CbcKeyFromPassword(password, { iterations: 1 });
const decrypt = (encryptedValue, opts) => {
const prefix = Buffer.from(encryptedValue).subarray(0, 3).toString('utf8');
if (prefix === 'v10') {
return decryptChromiumAes128CbcCookieValue(encryptedValue, [v10Key, emptyKey], {
stripHashPrefix: opts.stripHashPrefix,
treatUnknownPrefixAsPlaintext: false,
});
}
if (prefix === 'v11') {
return decryptChromiumAes128CbcCookieValue(encryptedValue, [v11Key, emptyKey], {
stripHashPrefix: opts.stripHashPrefix,
treatUnknownPrefixAsPlaintext: false,
});
}
return null;
};
const dbOptions = {
dbPath,
};
if (options.profile)
dbOptions.profile = options.profile;
if (options.includeExpired !== undefined)
dbOptions.includeExpired = options.includeExpired;
if (options.debug !== undefined)
dbOptions.debug = options.debug;
const result = await getCookiesFromChromeSqliteDb(dbOptions, origins, allowlistNames, decrypt);
result.warnings.unshift(...keyringWarnings);
return result;
}
//# sourceMappingURL=edgeSqliteLinux.js.map
@@ -0,0 +1 @@
{"version":3,"file":"edgeSqliteLinux.js","sourceRoot":"","sources":["../../src/providers/edgeSqliteLinux.ts"],"names":[],"mappings":"AACA,OAAO,EACN,mCAAmC,EACnC,8BAA8B,GAC9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,mCAAmC,EAAE,MAAM,gCAAgC,CAAC;AACrF,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AAEzE,MAAM,CAAC,KAAK,UAAU,6BAA6B,CAClD,OAAwE,EACxE,OAAiB,EACjB,cAAkC;IAElC,MAAM,IAAI,GAAwD;QACjE,aAAa,EAAE,gBAAgB;KAC/B,CAAC;IACF,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS;QAAE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAClE,MAAM,MAAM,GAAG,6BAA6B,CAAC,IAAI,CAAC,CAAC;IACnD,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,kCAAkC,CAAC,EAAE,CAAC;IACxE,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM,mCAAmC,CAAC;QACzF,GAAG,EAAE,MAAM;KACX,CAAC,CAAC;IAEH,wEAAwE;IACxE,qDAAqD;IACrD,kFAAkF;IAClF,MAAM,MAAM,GAAG,8BAA8B,CAAC,SAAS,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5E,MAAM,QAAQ,GAAG,8BAA8B,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,8BAA8B,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;IAE3E,MAAM,OAAO,GAAG,CACf,cAA0B,EAC1B,IAAkC,EAClB,EAAE;QAClB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC3E,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACtB,OAAO,mCAAmC,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE;gBAC9E,eAAe,EAAE,IAAI,CAAC,eAAe;gBACrC,6BAA6B,EAAE,KAAK;aACpC,CAAC,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACtB,OAAO,mCAAmC,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE;gBAC9E,eAAe,EAAE,IAAI,CAAC,eAAe;gBACrC,6BAA6B,EAAE,KAAK;aACpC,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC,CAAC;IAEF,MAAM,SAAS,GACd;QACC,MAAM;KACN,CAAC;IACH,IAAI,OAAO,CAAC,OAAO;QAAE,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACzD,IAAI,OAAO,CAAC,cAAc,KAAK,SAAS;QAAE,SAAS,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAC5F,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,SAAS,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAEjE,MAAM,MAAM,GAAG,MAAM,4BAA4B,CAAC,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;IAC/F,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,eAAe,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC;AACf,CAAC"}
@@ -0,0 +1,8 @@
import type { GetCookiesResult } from '../types.js';
export declare function getCookiesFromEdgeSqliteMac(options: {
profile?: string;
includeExpired?: boolean;
debug?: boolean;
timeoutMs?: number;
}, origins: string[], allowlistNames: Set<string> | null): Promise<GetCookiesResult>;
//# sourceMappingURL=edgeSqliteMac.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"edgeSqliteMac.d.ts","sourceRoot":"","sources":["../../src/providers/edgeSqliteMac.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AASpD,wBAAsB,2BAA2B,CAChD,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5F,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAChC,OAAO,CAAC,gBAAgB,CAAC,CA8C3B"}
@@ -0,0 +1,60 @@
import { homedir } from 'node:os';
import path from 'node:path';
import { decryptChromiumAes128CbcCookieValue, deriveAes128CbcKeyFromPassword, } from './chromeSqlite/crypto.js';
import { getCookiesFromChromeSqliteDb } from './chromeSqlite/shared.js';
import { readKeychainGenericPasswordFirst } from './chromium/macosKeychain.js';
import { resolveCookiesDbFromProfileOrRoots } from './chromium/paths.js';
export async function getCookiesFromEdgeSqliteMac(options, origins, allowlistNames) {
const dbPath = resolveEdgeCookiesDb(options.profile);
if (!dbPath) {
return { cookies: [], warnings: ['Edge cookies database not found.'] };
}
const warnings = [];
// On macOS, Edge stores its "Safe Storage" secret in Keychain (same scheme as Chrome).
// `security find-generic-password` is stable and avoids any native Node keychain modules.
const passwordResult = await readKeychainGenericPasswordFirst({
account: 'Microsoft Edge',
services: ['Microsoft Edge Safe Storage', 'Microsoft Edge'],
timeoutMs: options.timeoutMs ?? 3_000,
label: 'Microsoft Edge Safe Storage',
});
if (!passwordResult.ok) {
warnings.push(passwordResult.error);
return { cookies: [], warnings };
}
const edgePassword = passwordResult.password.trim();
if (!edgePassword) {
warnings.push('macOS Keychain returned an empty Microsoft Edge Safe Storage password.');
return { cookies: [], warnings };
}
// Chromium uses PBKDF2(password, "saltysalt", 1003, 16, sha1) for AES-128-CBC cookie values on macOS.
const key = deriveAes128CbcKeyFromPassword(edgePassword, { iterations: 1003 });
const decrypt = (encryptedValue, opts) => decryptChromiumAes128CbcCookieValue(encryptedValue, [key], {
stripHashPrefix: opts.stripHashPrefix,
treatUnknownPrefixAsPlaintext: true,
});
const dbOptions = {
dbPath,
};
if (options.profile)
dbOptions.profile = options.profile;
if (options.includeExpired !== undefined)
dbOptions.includeExpired = options.includeExpired;
if (options.debug !== undefined)
dbOptions.debug = options.debug;
const result = await getCookiesFromChromeSqliteDb(dbOptions, origins, allowlistNames, decrypt);
result.warnings.unshift(...warnings);
return result;
}
function resolveEdgeCookiesDb(profile) {
const home = homedir();
/* c8 ignore next */
const roots = process.platform === 'darwin'
? [path.join(home, 'Library', 'Application Support', 'Microsoft Edge')]
: [];
const args = { roots };
if (profile !== undefined)
args.profile = profile;
return resolveCookiesDbFromProfileOrRoots(args);
}
//# sourceMappingURL=edgeSqliteMac.js.map
@@ -0,0 +1 @@
{"version":3,"file":"edgeSqliteMac.js","sourceRoot":"","sources":["../../src/providers/edgeSqliteMac.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
import type { GetCookiesResult } from '../types.js';
export declare function getCookiesFromEdgeSqliteWindows(options: {
profile?: string;
includeExpired?: boolean;
debug?: boolean;
}, origins: string[], allowlistNames: Set<string> | null): Promise<GetCookiesResult>;
//# sourceMappingURL=edgeSqliteWindows.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"edgeSqliteWindows.d.ts","sourceRoot":"","sources":["../../src/providers/edgeSqliteWindows.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAMpD,wBAAsB,+BAA+B,CACpD,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EACxE,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAChC,OAAO,CAAC,gBAAgB,CAAC,CAmC3B"}
@@ -0,0 +1,38 @@
import path from 'node:path';
import { decryptChromiumAes256GcmCookieValue } from './chromeSqlite/crypto.js';
import { getCookiesFromChromeSqliteDb } from './chromeSqlite/shared.js';
import { getWindowsChromiumMasterKey } from './chromium/windowsMasterKey.js';
import { resolveChromiumPathsWindows } from './chromium/windowsPaths.js';
export async function getCookiesFromEdgeSqliteWindows(options, origins, allowlistNames) {
const resolveArgs = {
localAppDataVendorPath: path.join('Microsoft', 'Edge', 'User Data'),
};
if (options.profile !== undefined)
resolveArgs.profile = options.profile;
const { dbPath, userDataDir } = resolveChromiumPathsWindows(resolveArgs);
if (!dbPath || !userDataDir) {
return { cookies: [], warnings: ['Edge cookies database not found.'] };
}
// On Windows, Edge stores an AES key in `Local State` encrypted with DPAPI (CurrentUser).
// That master key is then used for AES-256-GCM cookie values (`v10`/`v11`/`v20` prefixes).
const masterKey = await getWindowsChromiumMasterKey(userDataDir, 'Edge');
if (!masterKey.ok) {
return { cookies: [], warnings: [masterKey.error] };
}
const decrypt = (encryptedValue, opts) => {
return decryptChromiumAes256GcmCookieValue(encryptedValue, masterKey.value, {
stripHashPrefix: opts.stripHashPrefix,
});
};
const dbOptions = {
dbPath,
};
if (options.profile)
dbOptions.profile = options.profile;
if (options.includeExpired !== undefined)
dbOptions.includeExpired = options.includeExpired;
if (options.debug !== undefined)
dbOptions.debug = options.debug;
return await getCookiesFromChromeSqliteDb(dbOptions, origins, allowlistNames, decrypt);
}
//# sourceMappingURL=edgeSqliteWindows.js.map
@@ -0,0 +1 @@
{"version":3,"file":"edgeSqliteWindows.js","sourceRoot":"","sources":["../../src/providers/edgeSqliteWindows.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,mCAAmC,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAEzE,MAAM,CAAC,KAAK,UAAU,+BAA+B,CACpD,OAAwE,EACxE,OAAiB,EACjB,cAAkC;IAElC,MAAM,WAAW,GAAsD;QACtE,sBAAsB,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,CAAC;KACnE,CAAC;IACF,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS;QAAE,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACzE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,2BAA2B,CAAC,WAAW,CAAC,CAAC;IACzE,IAAI,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAC7B,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,kCAAkC,CAAC,EAAE,CAAC;IACxE,CAAC;IAED,0FAA0F;IAC1F,2FAA2F;IAC3F,MAAM,SAAS,GAAG,MAAM,2BAA2B,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACzE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QACnB,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;IACrD,CAAC;IAED,MAAM,OAAO,GAAG,CACf,cAA0B,EAC1B,IAAkC,EAClB,EAAE;QAClB,OAAO,mCAAmC,CAAC,cAAc,EAAE,SAAS,CAAC,KAAK,EAAE;YAC3E,eAAe,EAAE,IAAI,CAAC,eAAe;SACrC,CAAC,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,SAAS,GACd;QACC,MAAM;KACN,CAAC;IACH,IAAI,OAAO,CAAC,OAAO;QAAE,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACzD,IAAI,OAAO,CAAC,cAAc,KAAK,SAAS;QAAE,SAAS,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAC5F,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,SAAS,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAEjE,OAAO,MAAM,4BAA4B,CAAC,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;AACxF,CAAC"}
@@ -0,0 +1,6 @@
import type { GetCookiesResult } from '../types.js';
export declare function getCookiesFromFirefox(options: {
profile?: string;
includeExpired?: boolean;
}, origins: string[], allowlistNames: Set<string> | null): Promise<GetCookiesResult>;
//# sourceMappingURL=firefoxSqlite.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"firefoxSqlite.d.ts","sourceRoot":"","sources":["../../src/providers/firefoxSqlite.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAA0B,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAK5E,wBAAsB,qBAAqB,CAC1C,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,EACvD,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAChC,OAAO,CAAC,gBAAgB,CAAC,CAmD3B"}
@@ -0,0 +1,257 @@
import { copyFileSync, existsSync, mkdtempSync, readdirSync, rmSync } from 'node:fs';
import { homedir, tmpdir } from 'node:os';
import path from 'node:path';
import { hostMatchesCookieDomain } from '../util/hostMatch.js';
import { importNodeSqlite } from '../util/nodeSqlite.js';
import { isBunRuntime } from '../util/runtime.js';
export async function getCookiesFromFirefox(options, origins, allowlistNames) {
const warnings = [];
const dbPath = resolveFirefoxCookiesDb(options.profile);
if (!dbPath) {
warnings.push('Firefox cookies database not found.');
return { cookies: [], warnings };
}
const tempDir = mkdtempSync(path.join(tmpdir(), 'sweet-cookie-firefox-'));
const tempDbPath = path.join(tempDir, 'cookies.sqlite');
try {
copyFileSync(dbPath, tempDbPath);
copySidecar(dbPath, `${tempDbPath}-wal`, '-wal');
copySidecar(dbPath, `${tempDbPath}-shm`, '-shm');
}
catch (error) {
rmSync(tempDir, { recursive: true, force: true });
warnings.push(`Failed to copy Firefox cookie DB: ${error instanceof Error ? error.message : String(error)}`);
return { cookies: [], warnings };
}
const hosts = origins.map((o) => new URL(o).hostname);
const now = Math.floor(Date.now() / 1000);
const where = buildHostWhereClause(hosts);
const expiryClause = options.includeExpired ? '' : ` AND (expiry = 0 OR expiry > ${now})`;
const sql = `SELECT name, value, host, path, expiry, isSecure, isHttpOnly, sameSite ` +
`FROM moz_cookies WHERE (${where})${expiryClause} ORDER BY expiry DESC;`;
try {
if (isBunRuntime()) {
const bunResult = await queryFirefoxCookiesWithBunSqlite(tempDbPath, sql);
if (!bunResult.ok) {
warnings.push(`bun:sqlite failed reading Firefox cookies: ${bunResult.error}`);
return { cookies: [], warnings };
}
const cookies = collectFirefoxCookiesFromRows(bunResult.rows, options, hosts, allowlistNames);
return { cookies: dedupeCookies(cookies), warnings };
}
const nodeResult = await queryFirefoxCookiesWithNodeSqlite(tempDbPath, sql);
if (!nodeResult.ok) {
warnings.push(`node:sqlite failed reading Firefox cookies: ${nodeResult.error}`);
return { cookies: [], warnings };
}
const cookies = collectFirefoxCookiesFromRows(nodeResult.rows, options, hosts, allowlistNames);
return { cookies: dedupeCookies(cookies), warnings };
}
finally {
rmSync(tempDir, { recursive: true, force: true });
}
}
async function queryFirefoxCookiesWithNodeSqlite(dbPath, sql) {
try {
const { DatabaseSync } = await importNodeSqlite();
const db = new DatabaseSync(dbPath, { readOnly: true });
try {
const rows = db.prepare(sql).all();
return { ok: true, rows };
}
finally {
db.close();
}
}
catch (error) {
return { ok: false, error: error instanceof Error ? error.message : String(error) };
}
}
async function queryFirefoxCookiesWithBunSqlite(dbPath, sql) {
try {
const { Database } = await import('bun:sqlite');
const db = new Database(dbPath, { readonly: true });
try {
const rows = db.query(sql).all();
return { ok: true, rows };
}
finally {
db.close();
}
}
catch (error) {
return { ok: false, error: error instanceof Error ? error.message : String(error) };
}
}
function collectFirefoxCookiesFromRows(rows, options, hosts, allowlistNames) {
const now = Math.floor(Date.now() / 1000);
const cookies = [];
for (const row of rows) {
const name = typeof row.name === 'string' ? row.name : null;
const value = typeof row.value === 'string' ? row.value : null;
const host = typeof row.host === 'string' ? row.host : null;
const cookiePath = typeof row.path === 'string' ? row.path : '';
if (!name || value === null || !host)
continue;
if (allowlistNames && allowlistNames.size > 0 && !allowlistNames.has(name))
continue;
if (!hostMatchesAny(hosts, host))
continue;
const expiryText = typeof row.expiry === 'number'
? String(row.expiry)
: typeof row.expiry === 'string'
? row.expiry
: undefined;
const expires = normalizeFirefoxExpiry(expiryText);
if (!options.includeExpired && expires && expires < now)
continue;
const isSecure = row.isSecure === 1 || row.isSecure === '1' || row.isSecure === true;
const isHttpOnly = row.isHttpOnly === 1 || row.isHttpOnly === '1' || row.isHttpOnly === true;
const cookie = {
name,
value,
domain: host.startsWith('.') ? host.slice(1) : host,
path: cookiePath || '/',
secure: isSecure,
httpOnly: isHttpOnly,
};
if (expires !== undefined)
cookie.expires = expires;
const normalizedSameSite = normalizeFirefoxSameSite(typeof row.sameSite === 'number'
? String(row.sameSite)
: typeof row.sameSite === 'string'
? row.sameSite
: undefined);
if (normalizedSameSite !== undefined)
cookie.sameSite = normalizedSameSite;
const source = { browser: 'firefox' };
if (options.profile)
source.profile = options.profile;
cookie.source = source;
cookies.push(cookie);
}
return cookies;
}
function resolveFirefoxCookiesDb(profile) {
const home = homedir();
// biome-ignore lint/complexity/useLiteralKeys: process.env is an index signature under strict TS.
const appData = process.env['APPDATA'];
/* c8 ignore next 10 */
const roots = process.platform === 'darwin'
? [path.join(home, 'Library', 'Application Support', 'Firefox', 'Profiles')]
: process.platform === 'linux'
? [path.join(home, '.mozilla', 'firefox')]
: process.platform === 'win32'
? appData
? [path.join(appData, 'Mozilla', 'Firefox', 'Profiles')]
: []
: [];
if (profile && looksLikePath(profile)) {
const candidate = profile.endsWith('cookies.sqlite')
? profile
: path.join(profile, 'cookies.sqlite');
return existsSync(candidate) ? candidate : null;
}
for (const root of roots) {
if (!root || !existsSync(root))
continue;
if (profile) {
const candidate = path.join(root, profile, 'cookies.sqlite');
if (existsSync(candidate))
return candidate;
continue;
}
const entries = safeReaddir(root);
const defaultRelease = entries.find((e) => e.includes('default-release'));
const picked = defaultRelease ?? entries[0];
if (!picked)
continue;
const candidate = path.join(root, picked, 'cookies.sqlite');
if (existsSync(candidate))
return candidate;
}
return null;
}
function safeReaddir(dir) {
try {
return readdirSync(dir, { withFileTypes: true })
.filter((e) => e.isDirectory())
.map((e) => e.name);
}
catch {
return [];
}
}
function looksLikePath(value) {
return value.includes('/') || value.includes('\\');
}
function copySidecar(sourceDbPath, target, suffix) {
const sidecar = `${sourceDbPath}${suffix}`;
if (!existsSync(sidecar))
return;
try {
copyFileSync(sidecar, target);
}
catch {
// ignore
}
}
function buildHostWhereClause(hosts) {
const clauses = [];
for (const host of hosts) {
const escaped = sqlLiteral(host);
const escapedDot = sqlLiteral(`.${host}`);
const escapedLike = sqlLiteral(`%.${host}`);
clauses.push(`host = ${escaped}`);
clauses.push(`host = ${escapedDot}`);
clauses.push(`host LIKE ${escapedLike}`);
}
return clauses.length ? clauses.join(' OR ') : '1=0';
}
function sqlLiteral(value) {
const escaped = value.replaceAll("'", "''");
return `'${escaped}'`;
}
function normalizeFirefoxExpiry(expiry) {
if (!expiry)
return undefined;
const value = Number.parseInt(expiry, 10);
if (!Number.isFinite(value) || value <= 0)
return undefined;
return value;
}
function normalizeFirefoxSameSite(raw) {
if (!raw)
return undefined;
const value = Number.parseInt(raw, 10);
if (Number.isFinite(value)) {
if (value === 2)
return 'Strict';
if (value === 1)
return 'Lax';
if (value === 0)
return 'None';
}
const normalized = raw.toLowerCase();
if (normalized === 'strict')
return 'Strict';
if (normalized === 'lax')
return 'Lax';
if (normalized === 'none')
return 'None';
return undefined;
}
function hostMatchesAny(hosts, cookieHost) {
const cookieDomain = cookieHost.startsWith('.') ? cookieHost.slice(1) : cookieHost;
return hosts.some((host) => hostMatchesCookieDomain(host, cookieDomain));
}
function dedupeCookies(cookies) {
const merged = new Map();
for (const cookie of cookies) {
const key = `${cookie.name}|${cookie.domain ?? ''}|${cookie.path ?? ''}`;
if (!merged.has(key))
merged.set(key, cookie);
}
return Array.from(merged.values());
}
//# sourceMappingURL=firefoxSqlite.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
import type { GetCookiesResult } from '../types.js';
type InlineSource = {
source: string;
payload: string;
};
export declare function getCookiesFromInline(inline: InlineSource, origins: string[], allowlistNames: Set<string> | null): Promise<GetCookiesResult>;
export {};
//# sourceMappingURL=inline.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../../src/providers/inline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAK5D,KAAK,YAAY,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,wBAAsB,oBAAoB,CACzC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAChC,OAAO,CAAC,gBAAgB,CAAC,CAkC3B"}
@@ -0,0 +1,71 @@
import { tryDecodeBase64Json } from '../util/base64.js';
import { readTextFileIfExists } from '../util/fs.js';
import { hostMatchesCookieDomain } from '../util/hostMatch.js';
export async function getCookiesFromInline(inline, origins, allowlistNames) {
const warnings = [];
// Inline sources can be:
// - the payload itself (JSON or base64)
// - a file path that contains JSON/base64
//
// We do a small heuristic: treat `*.json`/`*.base64` and explicit "file" sources as file paths first.
const rawPayload = inline.source.endsWith('file') ||
inline.payload.endsWith('.json') ||
inline.payload.endsWith('.base64')
? ((await readTextFileIfExists(inline.payload)) ?? inline.payload)
: inline.payload;
// If it looks like base64, decode it to JSON. Otherwise use it as-is.
const decoded = tryDecodeBase64Json(rawPayload) ?? rawPayload;
const parsed = tryParseCookiePayload(decoded);
if (!parsed) {
return { cookies: [], warnings };
}
const hostAllow = new Set(origins.map((o) => new URL(o).hostname));
const cookies = [];
for (const cookie of parsed.cookies) {
if (!cookie?.name)
continue;
if (allowlistNames && allowlistNames.size > 0 && !allowlistNames.has(cookie.name))
continue;
const domain = cookie.domain ?? (cookie.url ? safeHostnameFromUrl(cookie.url) : undefined);
if (domain && hostAllow.size > 0 && !matchesAnyHost(hostAllow, domain))
continue;
cookies.push(cookie);
}
return { cookies, warnings };
}
function tryParseCookiePayload(input) {
const trimmed = input.trim();
if (!trimmed)
return null;
try {
const parsed = JSON.parse(trimmed);
if (Array.isArray(parsed)) {
return { cookies: parsed };
}
if (parsed &&
typeof parsed === 'object' &&
Array.isArray(parsed.cookies)) {
return { cookies: parsed.cookies };
}
return null;
}
catch {
return null;
}
}
function matchesAnyHost(hosts, cookieDomain) {
for (const host of hosts) {
if (hostMatchesCookieDomain(host, cookieDomain))
return true;
}
return false;
}
function safeHostnameFromUrl(url) {
try {
return new URL(url).hostname;
}
catch {
return undefined;
}
}
//# sourceMappingURL=inline.js.map
@@ -0,0 +1 @@
{"version":3,"file":"inline.js","sourceRoot":"","sources":["../../src/providers/inline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
import type { GetCookiesResult } from '../types.js';
export declare function getCookiesFromSafari(options: {
includeExpired?: boolean;
file?: string;
}, origins: string[], allowlistNames: Set<string> | null): Promise<GetCookiesResult>;
//# sourceMappingURL=safariBinaryCookies.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"safariBinaryCookies.d.ts","sourceRoot":"","sources":["../../src/providers/safariBinaryCookies.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAU,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAK5D,wBAAsB,oBAAoB,CACzC,OAAO,EAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EACpD,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAChC,OAAO,CAAC,gBAAgB,CAAC,CAqC3B"}
@@ -0,0 +1,173 @@
import { existsSync, readFileSync } from 'node:fs';
import { homedir } from 'node:os';
import path from 'node:path';
import { hostMatchesCookieDomain } from '../util/hostMatch.js';
const MAC_EPOCH_DELTA_SECONDS = 978_307_200;
export async function getCookiesFromSafari(options, origins, allowlistNames) {
const warnings = [];
if (process.platform !== 'darwin') {
return { cookies: [], warnings };
}
const cookieFile = options.file ?? resolveSafariBinaryCookiesPath();
if (!cookieFile) {
warnings.push('Safari Cookies.binarycookies not found.');
return { cookies: [], warnings };
}
const hosts = origins.map((o) => new URL(o).hostname);
const now = Math.floor(Date.now() / 1000);
try {
const data = readFileSync(cookieFile);
// Safari's `Cookies.binarycookies` is a small binary container with multiple "pages".
// We decode only the fields we need (name/value/domain/path/flags/expiry).
const parsed = decodeBinaryCookies(data);
const cookies = [];
for (const cookie of parsed) {
if (!cookie.name)
continue;
if (allowlistNames && allowlistNames.size > 0 && !allowlistNames.has(cookie.name))
continue;
const domain = cookie.domain;
if (!domain)
continue;
if (!hosts.some((h) => hostMatchesCookieDomain(h, domain)))
continue;
if (!options.includeExpired && cookie.expires && cookie.expires < now)
continue;
cookies.push(cookie);
}
return { cookies: dedupeCookies(cookies), warnings };
}
catch (error) {
warnings.push(`Failed to read Safari cookies: ${error instanceof Error ? error.message : String(error)}`);
return { cookies: [], warnings };
}
}
function resolveSafariBinaryCookiesPath() {
const home = homedir();
const candidates = [
path.join(home, 'Library', 'Cookies', 'Cookies.binarycookies'),
path.join(home, 'Library', 'Containers', 'com.apple.Safari', 'Data', 'Library', 'Cookies', 'Cookies.binarycookies'),
];
for (const candidate of candidates) {
if (existsSync(candidate))
return candidate;
}
return null;
}
function decodeBinaryCookies(buffer) {
if (buffer.length < 8)
return [];
if (buffer.subarray(0, 4).toString('utf8') !== 'cook')
return [];
const pageCount = buffer.readUInt32BE(4);
let cursor = 8;
const pageSizes = [];
for (let i = 0; i < pageCount; i += 1) {
pageSizes.push(buffer.readUInt32BE(cursor));
cursor += 4;
}
const cookies = [];
for (const pageSize of pageSizes) {
const page = buffer.subarray(cursor, cursor + pageSize);
cursor += pageSize;
cookies.push(...decodePage(page));
}
return cookies;
}
function decodePage(page) {
if (page.length < 16)
return [];
const header = page.readUInt32BE(0);
if (header !== 0x00000100)
return [];
const cookieCount = page.readUInt32LE(4);
const offsets = [];
let cursor = 8;
for (let i = 0; i < cookieCount; i += 1) {
offsets.push(page.readUInt32LE(cursor));
cursor += 4;
}
const cookies = [];
for (const offset of offsets) {
const cookie = decodeCookie(page.subarray(offset));
if (cookie)
cookies.push(cookie);
}
return cookies;
}
function decodeCookie(cookieBuffer) {
if (cookieBuffer.length < 48)
return null;
const size = cookieBuffer.readUInt32LE(0);
if (size < 48 || size > cookieBuffer.length)
return null;
const flagsValue = cookieBuffer.readUInt32LE(8);
const isSecure = (flagsValue & 1) !== 0;
const isHttpOnly = (flagsValue & 4) !== 0;
const urlOffset = cookieBuffer.readUInt32LE(16);
const nameOffset = cookieBuffer.readUInt32LE(20);
const pathOffset = cookieBuffer.readUInt32LE(24);
const valueOffset = cookieBuffer.readUInt32LE(28);
// Safari stores dates as "Mac absolute time" (seconds since 2001-01-01).
const expiration = readDoubleLE(cookieBuffer, 40);
const rawUrl = readCString(cookieBuffer, urlOffset, size);
const name = readCString(cookieBuffer, nameOffset, size);
const cookiePath = readCString(cookieBuffer, pathOffset, size) ?? '/';
const value = readCString(cookieBuffer, valueOffset, size) ?? '';
if (!name)
return null;
const domain = rawUrl ? safeHostnameFromUrl(rawUrl) : undefined;
const expires = expiration && expiration > 0 ? Math.round(expiration + MAC_EPOCH_DELTA_SECONDS) : undefined;
const decoded = {
name,
value,
path: cookiePath,
secure: isSecure,
httpOnly: isHttpOnly,
source: { browser: 'safari' },
};
if (domain)
decoded.domain = domain;
if (expires !== undefined)
decoded.expires = expires;
return decoded;
}
function readDoubleLE(buffer, offset) {
if (offset + 8 > buffer.length)
return 0;
const slice = buffer.subarray(offset, offset + 8);
return slice.readDoubleLE(0);
}
function readCString(buffer, offset, end) {
if (offset <= 0 || offset >= end)
return null;
let cursor = offset;
while (cursor < end && buffer[cursor] !== 0)
cursor += 1;
if (cursor >= end)
return null;
return buffer.toString('utf8', offset, cursor);
}
function safeHostnameFromUrl(raw) {
try {
const url = raw.includes('://') ? raw : `https://${raw}`;
const parsed = new URL(url);
return parsed.hostname.startsWith('.') ? parsed.hostname.slice(1) : parsed.hostname;
}
catch {
const cleaned = raw.trim();
if (!cleaned)
return undefined;
return cleaned.startsWith('.') ? cleaned.slice(1) : cleaned;
}
}
function dedupeCookies(cookies) {
const merged = new Map();
for (const cookie of cookies) {
const key = `${cookie.name}|${cookie.domain ?? ''}|${cookie.path ?? ''}`;
if (!merged.has(key))
merged.set(key, cookie);
}
return Array.from(merged.values());
}
//# sourceMappingURL=safariBinaryCookies.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,26 @@
import type { Cookie, CookieHeaderOptions, GetCookiesOptions, GetCookiesResult } from './types.js';
/**
* Read cookies for a URL from one or more browser backends (and/or inline payloads).
*
* Supported backends:
* - `chrome`: macOS / Windows / Linux (Chromium-based; default discovery targets Google Chrome paths)
* - `edge`: macOS / Windows / Linux (Chromium-based; default discovery targets Microsoft Edge paths)
* - `firefox`: macOS / Windows / Linux
* - `safari`: macOS only (`Cookies.binarycookies`)
*
* Runtime requirements:
* - Node >= 22 (uses `node:sqlite`) or Bun (uses `bun:sqlite`)
*
* The function returns `{ cookies, warnings }`:
* - `cookies`: best-effort results, filtered by `url`/`origins` and optional `names` allowlist
* - `warnings`: non-fatal diagnostics (no raw cookie values)
*/
export declare function getCookies(options: GetCookiesOptions): Promise<GetCookiesResult>;
/**
* Convert cookies to an HTTP `Cookie` header value.
*
* This is a helper for typical Node fetch clients / HTTP libraries.
* It does not validate cookie RFC edge cases; it simply joins `name=value` pairs.
*/
export declare function toCookieHeader(cookies: readonly Cookie[], options?: CookieHeaderOptions): string;
//# sourceMappingURL=public.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../src/public.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEX,MAAM,EACN,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,MAAM,YAAY,CAAC;AAKpB;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA2FtF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,OAAO,GAAE,mBAAwB,GAC/B,MAAM,CAyBR"}
@@ -0,0 +1,195 @@
import { getCookiesFromChrome } from './providers/chrome.js';
import { getCookiesFromEdge } from './providers/edge.js';
import { getCookiesFromFirefox } from './providers/firefoxSqlite.js';
import { getCookiesFromInline } from './providers/inline.js';
import { getCookiesFromSafari } from './providers/safariBinaryCookies.js';
import { normalizeOrigins } from './util/origins.js';
const DEFAULT_BROWSERS = ['chrome', 'safari', 'firefox'];
/**
* Read cookies for a URL from one or more browser backends (and/or inline payloads).
*
* Supported backends:
* - `chrome`: macOS / Windows / Linux (Chromium-based; default discovery targets Google Chrome paths)
* - `edge`: macOS / Windows / Linux (Chromium-based; default discovery targets Microsoft Edge paths)
* - `firefox`: macOS / Windows / Linux
* - `safari`: macOS only (`Cookies.binarycookies`)
*
* Runtime requirements:
* - Node >= 22 (uses `node:sqlite`) or Bun (uses `bun:sqlite`)
*
* The function returns `{ cookies, warnings }`:
* - `cookies`: best-effort results, filtered by `url`/`origins` and optional `names` allowlist
* - `warnings`: non-fatal diagnostics (no raw cookie values)
*/
export async function getCookies(options) {
const warnings = [];
const url = options.url;
const origins = normalizeOrigins(url, options.origins);
const names = normalizeNames(options.names);
let browsers;
if (Array.isArray(options.browsers) && options.browsers.length > 0) {
browsers = options.browsers;
}
else {
browsers = parseBrowsersEnv() ?? DEFAULT_BROWSERS;
}
const mode = options.mode ?? parseModeEnv() ?? 'merge';
const inlineSources = await resolveInlineSources(options);
// Inline sources are the most reliable path (they bypass DB locks + keychain prompts).
// We short-circuit on the first inline source that yields any cookies.
for (const source of inlineSources) {
const inlineResult = await getCookiesFromInline(source, origins, names);
warnings.push(...inlineResult.warnings);
if (inlineResult.cookies.length) {
return { cookies: inlineResult.cookies, warnings };
}
}
const merged = new Map();
const tryAdd = (cookie) => {
// Dedupe by name+domain+path (a common stable identity for HTTP cookies).
const domain = cookie.domain ?? '';
const pathValue = cookie.path ?? '';
const key = `${cookie.name}|${domain}|${pathValue}`;
if (!merged.has(key)) {
merged.set(key, cookie);
}
};
for (const browser of browsers) {
let result;
if (browser === 'chrome') {
const chromeOptions = {};
const chromeProfile = options.chromeProfile ?? options.profile ?? readEnv('SWEET_COOKIE_CHROME_PROFILE');
if (chromeProfile)
chromeOptions.profile = chromeProfile;
if (options.timeoutMs !== undefined)
chromeOptions.timeoutMs = options.timeoutMs;
if (options.includeExpired !== undefined)
chromeOptions.includeExpired = options.includeExpired;
if (options.debug !== undefined)
chromeOptions.debug = options.debug;
result = await getCookiesFromChrome(chromeOptions, origins, names);
}
else if (browser === 'edge') {
const edgeOptions = {};
const edgeProfile = options.edgeProfile ??
options.profile ??
readEnv('SWEET_COOKIE_EDGE_PROFILE') ??
readEnv('SWEET_COOKIE_CHROME_PROFILE');
if (edgeProfile)
edgeOptions.profile = edgeProfile;
if (options.timeoutMs !== undefined)
edgeOptions.timeoutMs = options.timeoutMs;
if (options.includeExpired !== undefined)
edgeOptions.includeExpired = options.includeExpired;
if (options.debug !== undefined)
edgeOptions.debug = options.debug;
result = await getCookiesFromEdge(edgeOptions, origins, names);
}
else if (browser === 'firefox') {
const firefoxOptions = {};
const firefoxProfile = options.firefoxProfile ?? readEnv('SWEET_COOKIE_FIREFOX_PROFILE');
if (firefoxProfile)
firefoxOptions.profile = firefoxProfile;
if (options.includeExpired !== undefined)
firefoxOptions.includeExpired = options.includeExpired;
result = await getCookiesFromFirefox(firefoxOptions, origins, names);
}
else {
const safariOptions = {};
if (options.includeExpired !== undefined)
safariOptions.includeExpired = options.includeExpired;
if (options.safariCookiesFile)
safariOptions.file = options.safariCookiesFile;
result = await getCookiesFromSafari(safariOptions, origins, names);
}
warnings.push(...result.warnings);
if (mode === 'first' && result.cookies.length) {
// "first" returns the first backend that produced anything (plus accumulated warnings).
return { cookies: result.cookies, warnings };
}
for (const cookie of result.cookies) {
tryAdd(cookie);
}
}
return { cookies: Array.from(merged.values()), warnings };
}
/**
* Convert cookies to an HTTP `Cookie` header value.
*
* This is a helper for typical Node fetch clients / HTTP libraries.
* It does not validate cookie RFC edge cases; it simply joins `name=value` pairs.
*/
export function toCookieHeader(cookies, options = {}) {
const sort = options.sort ?? 'name';
const dedupeByName = options.dedupeByName ?? false;
const items = cookies
.filter((cookie) => cookie?.name && typeof cookie.value === 'string')
.map((cookie) => ({ name: cookie.name, value: cookie.value }));
const ordered = sort === 'name' ? items.slice().sort((a, b) => a.name.localeCompare(b.name)) : items;
if (!dedupeByName) {
return ordered.map((cookie) => `${cookie.name}=${cookie.value}`).join('; ');
}
const seen = new Set();
const deduped = [];
for (const cookie of ordered) {
const key = cookie.name;
if (seen.has(key))
continue;
seen.add(key);
deduped.push(cookie);
}
return deduped.map((cookie) => `${cookie.name}=${cookie.value}`).join('; ');
}
function normalizeNames(names) {
if (!names?.length)
return null;
const cleaned = names.map((n) => n.trim()).filter(Boolean);
if (!cleaned.length)
return null;
return new Set(cleaned);
}
async function resolveInlineSources(options) {
const sources = [];
if (options.inlineCookiesJson) {
sources.push({ source: 'inline-json', payload: options.inlineCookiesJson });
}
if (options.inlineCookiesBase64) {
sources.push({ source: 'inline-base64', payload: options.inlineCookiesBase64 });
}
if (options.inlineCookiesFile) {
sources.push({ source: 'inline-file', payload: options.inlineCookiesFile });
}
return sources;
}
function parseBrowsersEnv() {
const raw = readEnv('SWEET_COOKIE_BROWSERS') ?? readEnv('SWEET_COOKIE_SOURCES');
if (!raw)
return undefined;
const tokens = raw
.split(/[,\s]+/)
.map((t) => t.trim().toLowerCase())
.filter(Boolean);
const out = [];
for (const token of tokens) {
if (token === 'chrome' || token === 'edge' || token === 'firefox' || token === 'safari') {
if (!out.includes(token))
out.push(token);
}
}
return out.length ? out : undefined;
}
function parseModeEnv() {
const raw = readEnv('SWEET_COOKIE_MODE');
if (!raw)
return undefined;
const normalized = raw.trim().toLowerCase();
if (normalized === 'merge' || normalized === 'first')
return normalized;
return undefined;
}
function readEnv(key) {
const value = process.env[key];
const trimmed = typeof value === 'string' ? value.trim() : '';
return trimmed.length ? trimmed : undefined;
}
//# sourceMappingURL=public.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,121 @@
/**
* Supported browser backends.
*
* Notes:
* - `safari` is macOS-only.
* - `chrome` targets Google Chrome paths by default.
* - `edge` targets Microsoft Edge paths by default.
* - Other Chromium browsers often work by passing an explicit cookie DB path via `chromeProfile`
* (or `edgeProfile` if you want to keep sources separate).
* - Only modern Chromium cookie DB schemas are supported (roughly Chrome >= 100).
*/
export type BrowserName = 'chrome' | 'edge' | 'firefox' | 'safari';
export type CookieSameSite = 'Strict' | 'Lax' | 'None';
export interface Cookie {
/** Cookie name (required). */
name: string;
/** Cookie value (required; may be empty string). */
value: string;
/** Hostname without leading dot (e.g. `example.com`). */
domain?: string;
/** Path (defaults to `/` when omitted). */
path?: string;
/**
* Optional URL form. Useful when a cookie is host-only or when `domain` is not known.
* If present, it should be a valid origin URL like `https://example.com`.
*/
url?: string;
/** Unix timestamp in seconds. Omit for session cookies. */
expires?: number;
secure?: boolean;
httpOnly?: boolean;
sameSite?: CookieSameSite;
source?: {
browser: BrowserName;
/** Optional profile identifier when resolved from a profile. */
profile?: string;
/** Optional origin that produced this cookie (implementation detail). */
origin?: string;
/** Optional store identifier (implementation detail; e.g. extension cookie store). */
storeId?: string;
};
}
export type CookieMode = 'merge' | 'first';
export interface GetCookiesOptions {
/**
* Primary URL used to derive default origin filtering.
* Must include a protocol (e.g. `https://example.com/`).
*/
url: string;
/**
* Additional origins to include when filtering cookies.
* Useful for OAuth/SSO flows where cookies are set on multiple domains.
*/
origins?: string[];
/** Allowlist of cookie names. When omitted, all matching cookies are returned. */
names?: string[];
/**
* Which browser backends to try, in order.
* Defaults to `chrome`, `safari`, `firefox` (and is also configurable via env).
*/
browsers?: BrowserName[];
/** Alias for chromeProfile (common case). */
profile?: string;
/**
* Chrome/Chromium profile selector.
*
* Accepted values:
* - profile directory name like `Default` / `Profile 2`
* - a path to a profile directory
* - a path to a cookie DB file (`.../Network/Cookies` or `.../Cookies`)
*/
chromeProfile?: string;
/**
* Microsoft Edge profile selector.
*
* Accepted values:
* - profile directory name like `Default` / `Profile 2`
* - a path to a profile directory
* - a path to a cookie DB file (`.../Network/Cookies` or `.../Cookies`)
*/
edgeProfile?: string;
/**
* Firefox profile selector (profile name or filesystem path).
* If a directory is provided, `cookies.sqlite` is resolved within it.
*/
firefoxProfile?: string;
/** Override path to Safari Cookies.binarycookies (for tests / debugging). */
safariCookiesFile?: string;
/** Include expired cookies (default: false). */
includeExpired?: boolean;
/** Timeout for OS helper calls (keychain/keyring/DPAPI). */
timeoutMs?: number;
/** Emit extra provider warnings (no raw cookie values). */
debug?: boolean;
/** Merge cookies across backends (`merge`) or return first successful backend (`first`). */
mode?: CookieMode;
/**
* Inline cookie payload source. Commonly a file path exported by the extension.
* If it looks like a file path, Sweet Cookie will attempt to read it.
*/
inlineCookiesFile?: string;
/** Inline cookie payload as JSON string (either `Cookie[]` or `{ cookies: Cookie[] }`). */
inlineCookiesJson?: string;
/** Inline cookie payload as base64-encoded JSON. */
inlineCookiesBase64?: string;
}
export interface GetCookiesResult {
cookies: Cookie[];
/**
* Non-fatal warnings from providers (missing keyring tools, schema drift, unsupported cookies, etc).
* Never includes raw cookie values.
*/
warnings: string[];
}
export interface CookieHeaderOptions {
/** If true, keeps the first cookie value per name and drops duplicates. */
dedupeByName?: boolean;
/** Sorting strategy for the emitted header. */
sort?: 'name' | 'none';
}
//# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEnE,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;AAEvD,MAAM,WAAW,MAAM;IACtB,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,MAAM,CAAC,EAAE;QACR,OAAO,EAAE,WAAW,CAAC;QACrB,gEAAgE;QAChE,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,yEAAyE;QACzE,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,sFAAsF;QACtF,OAAO,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACF;AAED,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;AAE3C,MAAM,WAAW,iBAAiB;IACjC;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gDAAgD;IAChD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,4FAA4F;IAC5F,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2FAA2F;IAC3F,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oDAAoD;IACpD,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;OAGG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IACnC,2EAA2E;IAC3E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB"}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
export declare function tryDecodeBase64Json(input: string): string | null;
//# sourceMappingURL=base64.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../../src/util/base64.ts"],"names":[],"mappings":"AAAA,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAahE"}
@@ -0,0 +1,18 @@
export function tryDecodeBase64Json(input) {
const trimmed = input.trim();
if (!trimmed)
return null;
try {
const encoding = /[-_]/.test(trimmed) ? 'base64url' : 'base64';
const buf = Buffer.from(trimmed, encoding);
const decoded = buf.toString('utf8').trim();
if (!decoded)
return null;
JSON.parse(decoded);
return decoded;
}
catch {
return null;
}
}
//# sourceMappingURL=base64.js.map
@@ -0,0 +1 @@
{"version":3,"file":"base64.js","sourceRoot":"","sources":["../../src/util/base64.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAChD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAmB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC/E,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACpB,OAAO,OAAO,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC"}
@@ -0,0 +1,8 @@
export declare function execCapture(file: string, args: string[], options?: {
timeoutMs?: number;
}): Promise<{
code: number;
stdout: string;
stderr: string;
}>;
//# sourceMappingURL=exec.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/util/exec.ts"],"names":[],"mappings":"AAEA,wBAAsB,WAAW,CAChC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA8G3D"}

Some files were not shown because too many files have changed in this diff Show More