chromancer
Version:
A powerful command-line interface for automating Chrome browser using Playwright. Perfect for web scraping, automation, testing, and browser workflows.
223 lines (222 loc) • 8.49 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@oclif/core");
const base_js_1 = require("../base.js");
const fs_1 = require("fs");
class Cookies extends base_js_1.BaseCommand {
static description = 'Complete cookie management - save/load sessions, inspect values, set custom cookies, and maintain authenticated states across runs';
static examples = [
'<%= config.bin %> <%= command.id %> list',
'<%= config.bin %> <%= command.id %> get sessionId',
'<%= config.bin %> <%= command.id %> set name=value',
'<%= config.bin %> <%= command.id %> delete sessionId',
'<%= config.bin %> <%= command.id %> clear',
'<%= config.bin %> <%= command.id %> save --output cookies.json',
'<%= config.bin %> <%= command.id %> load --file cookies.json',
];
static args = {
action: core_1.Args.string({
description: 'Cookie action',
options: ['list', 'get', 'set', 'delete', 'clear', 'save', 'load'],
required: true,
}),
value: core_1.Args.string({
description: 'Cookie name or name=value pair',
}),
};
static flags = {
...base_js_1.BaseCommand.baseFlags,
output: core_1.Flags.string({
char: 'o',
description: 'Output file for save action',
default: 'cookies.json',
}),
file: core_1.Flags.string({
char: 'f',
description: 'Input file for load action',
}),
domain: core_1.Flags.string({
description: 'Cookie domain',
}),
path: core_1.Flags.string({
description: 'Cookie path',
default: '/',
}),
secure: core_1.Flags.boolean({
description: 'Secure cookie flag',
default: false,
}),
httpOnly: core_1.Flags.boolean({
description: 'HttpOnly cookie flag',
default: false,
}),
sameSite: core_1.Flags.string({
description: 'SameSite cookie attribute',
options: ['Strict', 'Lax', 'None'],
}),
expires: core_1.Flags.integer({
description: 'Cookie expiration timestamp',
}),
};
async run() {
const { args, flags } = await this.parse(Cookies);
await this.connectToChrome(flags.port, flags.host, flags.launch, flags.profile, flags.headless, flags.verbose, flags.keepOpen);
if (!this.page || !this.context) {
this.error('Failed to connect to Chrome');
}
await this.manageCookies(this.page, args.action, args.value);
}
async manageCookies(page, action, value) {
const { flags } = await this.parse(Cookies);
switch (action) {
case 'list':
await this.listCookies(page);
break;
case 'get':
if (!value)
this.error('Cookie name required for get action');
await this.getCookie(page, value);
break;
case 'set':
if (!value)
this.error('Cookie data required for set action (name=value)');
await this.setCookie(page, value);
break;
case 'delete':
if (!value)
this.error('Cookie name required for delete action');
await this.deleteCookie(page, value);
break;
case 'clear':
await this.clearCookies(page);
break;
case 'save':
await this.saveCookies(page, flags.output);
break;
case 'load':
if (!flags.file)
this.error('File path required for load action (use --file)');
await this.loadCookies(page, flags.file);
break;
default:
this.error(`Unknown action: ${action}`);
}
}
async listCookies(page) {
const cookies = await this.context.cookies();
if (cookies.length === 0) {
this.log('No cookies found');
return;
}
this.log(`Found ${cookies.length} cookies:\n`);
for (const cookie of cookies) {
this.log(`🍪 ${cookie.name}`);
this.log(` Value: ${cookie.value}`);
this.log(` Domain: ${cookie.domain}`);
this.log(` Path: ${cookie.path}`);
if (cookie.expires && cookie.expires > 0) {
this.log(` Expires: ${new Date(cookie.expires * 1000).toISOString()}`);
}
if (cookie.secure)
this.log(' Secure: ✓');
if (cookie.httpOnly)
this.log(' HttpOnly: ✓');
if (cookie.sameSite)
this.log(` SameSite: ${cookie.sameSite}`);
this.log('');
}
}
async getCookie(page, name) {
const { flags } = await this.parse(Cookies);
const cookies = await this.context.cookies();
const cookie = cookies.find(c => c.name === name);
if (!cookie) {
this.error(`Cookie not found: ${name}`);
}
this.log(`🍪 ${cookie.name} = ${cookie.value}`);
if (flags.verbose) {
this.logVerbose('Cookie details', {
domain: cookie.domain,
path: cookie.path,
secure: cookie.secure,
httpOnly: cookie.httpOnly,
sameSite: cookie.sameSite,
expires: cookie.expires ? new Date(cookie.expires * 1000).toISOString() : 'Session',
});
}
}
async setCookie(page, value) {
const { flags } = await this.parse(Cookies);
// Parse name=value format
const [name, ...valueParts] = value.split('=');
if (!name || valueParts.length === 0) {
this.error('Invalid cookie format. Use: name=value');
}
const cookieValue = valueParts.join('='); // Handle values with = in them
// Get current URL for domain
const url = page.url();
const urlObj = new URL(url);
const cookie = {
name: name.trim(),
value: cookieValue.trim(),
domain: flags.domain || urlObj.hostname,
path: flags.path,
secure: flags.secure,
httpOnly: flags.httpOnly,
sameSite: flags.sameSite,
expires: flags.expires || -1, // -1 means session cookie
};
await this.context.addCookies([cookie]);
this.log(`✅ Cookie set: ${name}`);
if (flags.verbose) {
this.logVerbose('Cookie set', cookie);
}
}
async deleteCookie(page, name) {
const cookies = await this.context.cookies();
const cookie = cookies.find(c => c.name === name);
if (!cookie) {
this.warn(`Cookie not found: ${name}`);
return;
}
// Delete by setting expiration to past
await this.context.addCookies([{
...cookie,
expires: 0,
}]);
this.log(`✅ Cookie deleted: ${name}`);
}
async clearCookies(page) {
const cookies = await this.context.cookies();
if (cookies.length === 0) {
this.log('No cookies to clear');
return;
}
await this.context.clearCookies();
this.log(`✅ Cleared ${cookies.length} cookies`);
}
async saveCookies(page, outputPath) {
const cookies = await this.context.cookies();
if (cookies.length === 0) {
this.warn('No cookies to save');
return;
}
await fs_1.promises.writeFile(outputPath, JSON.stringify(cookies, null, 2));
this.log(`✅ Saved ${cookies.length} cookies to: ${outputPath}`);
}
async loadCookies(page, filePath) {
try {
const content = await fs_1.promises.readFile(filePath, 'utf-8');
const cookies = JSON.parse(content);
if (!Array.isArray(cookies)) {
this.error('Invalid cookie file format');
}
await this.context.addCookies(cookies);
this.log(`✅ Loaded ${cookies.length} cookies from: ${filePath}`);
}
catch (error) {
this.error(`Failed to load cookies: ${error.message}`);
}
}
}
exports.default = Cookies;