refactor(auth)!: replace allow toggles with cookieSource

This commit is contained in:
Peter Steinberger
2025-12-26 21:28:27 +01:00
parent 008e550654
commit e8e98e641a
5 changed files with 63 additions and 66 deletions
+38 -14
View File
@@ -12,7 +12,7 @@
import { existsSync, readFileSync } from 'node:fs';
import { homedir } from 'node:os';
import { join } from 'node:path';
import { Command, Option } from 'commander';
import { Command } from 'commander';
import JSON5 from 'json5';
import kleur from 'kleur';
import { resolveCliInvocation } from './lib/cli-args.js';
@@ -45,6 +45,32 @@ const collect = (value: string, previous: string[] = []) => {
return previous;
};
const COOKIE_SOURCES: CookieSource[] = ['safari', 'chrome', 'firefox'];
function parseCookieSource(value: string): CookieSource {
const normalized = value.trim().toLowerCase();
if (normalized === 'safari' || normalized === 'chrome' || normalized === 'firefox') return normalized;
throw new Error(`Invalid --cookie-source "${value}". Allowed: safari, chrome, firefox.`);
}
function resolveCookieSourceOrder(input: unknown): CookieSource[] | undefined {
if (typeof input === 'string') return [parseCookieSource(input)];
if (Array.isArray(input)) {
const result: CookieSource[] = [];
for (const entry of input) {
if (typeof entry !== 'string') continue;
result.push(parseCookieSource(entry));
}
return result.length > 0 ? result : undefined;
}
return undefined;
}
const collectCookieSource = (value: string, previous: CookieSource[] = []) => {
previous.push(parseCookieSource(value));
return previous;
};
const p = (kind: Parameters<typeof statusPrefix>[0]) => statusPrefix(kind, output);
const l = (kind: Parameters<typeof labelPrefix>[0]) => labelPrefix(kind, output);
@@ -70,10 +96,7 @@ const colors = {
type BirdConfig = {
chromeProfile?: string;
firefoxProfile?: string;
cookieSource?: CookieSource;
allowSafari?: boolean;
allowChrome?: boolean;
allowFirefox?: boolean;
cookieSource?: CookieSource | CookieSource[];
timeoutMs?: number;
};
@@ -146,10 +169,11 @@ program
.option('--ct0 <token>', 'Twitter ct0 cookie')
.option('--chrome-profile <name>', 'Chrome profile name for cookie extraction', config.chromeProfile)
.option('--firefox-profile <name>', 'Firefox profile name for cookie extraction', config.firefoxProfile)
.addOption(
new Option('--cookie-source <source>', 'Cookie source for browser cookie extraction')
.choices(['auto', 'safari', 'chrome', 'firefox'])
.default(config.cookieSource ?? 'auto'),
.option(
'--cookie-source <source>',
'Cookie source for browser cookie extraction (repeatable)',
collectCookieSource,
[],
)
.option('--media <path>', 'Attach media file (repeatable, up to 4 images or 1 video)', collect, [])
.option('--alt <text>', 'Alt text for the corresponding --media (repeatable)', collect, [])
@@ -163,19 +187,19 @@ type CredentialsOptions = {
ct0?: string;
chromeProfile?: string;
firefoxProfile?: string;
cookieSource?: CookieSource;
cookieSource?: CookieSource[];
};
function resolveCredentialsFromOptions(opts: CredentialsOptions) {
const cookieSource = opts.cookieSource?.length
? opts.cookieSource
: (resolveCookieSourceOrder(config.cookieSource) ?? COOKIE_SOURCES);
return resolveCredentials({
authToken: opts.authToken,
ct0: opts.ct0,
cookieSource: opts.cookieSource ?? config.cookieSource ?? 'auto',
cookieSource,
chromeProfile: opts.chromeProfile || config.chromeProfile,
firefoxProfile: opts.firefoxProfile || config.firefoxProfile,
allowSafari: config.allowSafari ?? true,
allowChrome: config.allowChrome ?? true,
allowFirefox: config.allowFirefox ?? true,
});
}
+8 -27
View File
@@ -20,7 +20,7 @@ export interface CookieExtractionResult {
warnings: string[];
}
export type CookieSource = 'auto' | 'safari' | 'chrome' | 'firefox';
export type CookieSource = 'safari' | 'chrome' | 'firefox';
function normalizeValue(value: unknown): string | null {
if (typeof value === 'string') {
@@ -446,12 +446,9 @@ export async function extractCookiesFromFirefox(profile?: string): Promise<Cooki
export async function resolveCredentials(options: {
authToken?: string;
ct0?: string;
cookieSource?: CookieSource;
allowSafari?: boolean;
cookieSource?: CookieSource | CookieSource[];
chromeProfile?: string;
firefoxProfile?: string;
allowChrome?: boolean;
allowFirefox?: boolean;
}): Promise<CookieExtractionResult> {
const warnings: string[] = [];
const cookies: TwitterCookies = {
@@ -460,7 +457,7 @@ export async function resolveCredentials(options: {
source: null,
};
const cookieSource: CookieSource = options.cookieSource ?? 'auto';
const cookieSource = options.cookieSource;
// 1. CLI arguments (highest priority)
if (options.authToken) {
@@ -498,22 +495,16 @@ export async function resolveCredentials(options: {
}
}
const allowSafari = options.allowSafari ?? true;
const allowChrome = options.allowChrome ?? true;
const allowFirefox = options.allowFirefox ?? true;
const sourcesToTry: Array<Exclude<CookieSource, 'auto'>> =
cookieSource === 'auto' ? ['safari', 'chrome', 'firefox'] : [cookieSource];
const sourcesToTry: CookieSource[] = Array.isArray(cookieSource)
? cookieSource
: cookieSource
? [cookieSource]
: ['safari', 'chrome', 'firefox'];
for (const source of sourcesToTry) {
if (cookies.authToken && cookies.ct0) break;
if (source === 'safari') {
if (!allowSafari) {
warnings.push('Safari cookie extraction disabled (allowSafari=false).');
continue;
}
const safariResult = await extractCookiesFromSafari();
warnings.push(...safariResult.warnings);
@@ -529,11 +520,6 @@ export async function resolveCredentials(options: {
}
if (source === 'chrome') {
if (!allowChrome) {
warnings.push('Chrome cookie extraction disabled (allowChrome=false).');
continue;
}
const chromeResult = await extractCookiesFromChrome(options.chromeProfile);
warnings.push(...chromeResult.warnings);
@@ -549,11 +535,6 @@ export async function resolveCredentials(options: {
}
if (source === 'firefox') {
if (!allowFirefox) {
warnings.push('Firefox cookie extraction disabled (allowFirefox=false).');
continue;
}
const firefoxResult = await extractCookiesFromFirefox(options.firefoxProfile);
warnings.push(...firefoxResult.warnings);