mirror-web-cli
Version:
Professional website mirroring tool with intelligent framework preservation, AI-powered analysis, and comprehensive asset optimization
196 lines (172 loc) • 5.7 kB
JavaScript
/**
* Mirror Web CLI - Command Line Interface
* v1.1.3
*
* Simple usage: mirror-web-cli <url>
* JS mode is chosen automatically (JS ON vs OFF) by the engine.
*/
import { Command } from 'commander';
import chalk from 'chalk';
import dotenv from 'dotenv';
import fs from 'fs';
import path from 'path';
import { MirrorCloner } from './core/mirror-cloner.js';
/**
* Load environment variables with priority:
* 1) CLI flag (--openai-key) sets process.env later, highest precedence
* 2) Existing shell environment (preexisting process.env) is preserved
* 3) .env.local overrides values loaded from .env (but does NOT override shell)
* 4) .env (base)
*/
function loadEnvWithPriority() {
const cwd = process.cwd();
const preexisting = new Set(Object.keys(process.env));
// Load base .env (lowest priority)
dotenv.config({ path: path.resolve(cwd, '.env'), override: false });
// Load .env.local, overriding only values that didn't come from the shell
const localPath = path.resolve(cwd, '.env.local');
if (fs.existsSync(localPath)) {
try {
const parsed = dotenv.parse(fs.readFileSync(localPath));
for (const [k, v] of Object.entries(parsed)) {
if (preexisting.has(k)) continue; // don't override shell-provided vars
process.env[k] = v; // override values from .env if present
}
} catch {
// ignore parse errors
}
}
}
// Load env files before reading options
loadEnvWithPriority();
/**
* Validate and configure OpenAI API key for AI features.
* Uses OPENAI_API_KEY from environment (shell, .env.local, .env) or --openai-key if provided.
*/
function validateAISetup(aiEnabled, openaiApiKey) {
if (!aiEnabled) return false;
// CLI flag has highest priority
const finalApiKey = openaiApiKey || process.env.OPENAI_API_KEY;
if (!finalApiKey) {
console.log('');
console.log(
chalk.yellow(
'⚠️ AI features requested but no OPENAI_API_KEY found (checked env, .env.local, and .env).',
),
);
console.log(
chalk.white(
'Add OPENAI_API_KEY to your .env.local or .env file, export it in your shell, or pass --openai-key "sk-..."',
),
);
console.log(chalk.dim('Continuing without AI features...'));
console.log('');
return false;
}
if (!finalApiKey.startsWith('sk-')) {
console.log('');
console.log(
chalk.yellow('⚠️ Invalid OpenAI API key format (must start with "sk-")'),
);
console.log(chalk.dim('Continuing without AI features...'));
console.log('');
return false;
}
// If provided via flag, ensure it’s in process.env for downstream code
if (openaiApiKey) {
process.env.OPENAI_API_KEY = openaiApiKey;
}
return true;
}
const program = new Command();
program
.name('mirror-web-cli')
.description(
'Mirror Web CLI v1.1 - Professional website mirroring with automatic JS mode selection',
)
.version('1.1.3')
.argument('<url>', 'Target website URL to mirror (supports http/https)')
.option(
'-o, --output <dir>',
'Custom output directory path (defaults to domain-standard or domain-ai-enhanced)',
)
.option(
'--clean',
'Remove tracking scripts, analytics, and third-party code',
false,
)
.option(
'--ai',
'Enable AI-powered website analysis (reads OPENAI_API_KEY from env, .env.local, or .env)',
false,
)
.option(
'--openai-key <key>',
'OpenAI API key for AI features (overrides env/.env.local/.env for this run)',
)
.option('--debug', 'Enable detailed debug logging and error traces', false)
.option(
'--timeout <ms>',
'Browser page load timeout in milliseconds',
'120000',
)
.option(
'--headless <bool>',
'Run browser in headless mode (true/false)',
'true',
)
.action(async (url, options) => {
try {
if (!url.startsWith('http')) url = 'https://' + url;
new URL(url); // validate
const aiEnabled = validateAISetup(options.ai, options.openaiKey);
const config = {
outputDir: options.output,
clean: options.clean,
ai: aiEnabled,
debug: options.debug,
timeout: parseInt(options.timeout),
headless: options.headless !== 'false',
};
const cloner = new MirrorCloner(url, config);
const success = await cloner.clone();
process.exit(success ? 0 : 1);
} catch (error) {
console.log('');
console.log(chalk.red('❌ Error: ' + error.message));
if (error.message.includes('Invalid URL')) {
console.log(
chalk.gray(
' Please provide a valid URL (e.g., https://example.com)',
),
);
}
process.exit(1);
}
});
program.addHelpText(
'after',
`
Automatic JS mode:
• You only provide the URL. The tool preflights the site and decides whether to keep JS ON or use a static snapshot (JS OFF).
AI setup:
• The CLI looks for OPENAI_API_KEY in this order:
1) --openai-key flag
2) Existing shell environment
3) .env.local (overrides .env)
4) .env
Examples:
mirror-web-cli https://hitesh.ai --ai
mirror-web-cli https://piyushgarg.dev --ai --openai-key "sk-..."
`,
);
if (process.argv.length <= 2) {
console.log('');
console.log(chalk.white('Mirror any website while preserving its look.'));
console.log(chalk.cyan('Example:'));
console.log(' mirror-web-cli https://example.com');
console.log('');
process.exit(0);
}
program.parse(process.argv);