faf-cli
Version:
š½ TURBO-CAT: The Rapid Catalytic Converter ⢠Project DNA ⨠for ANY AI ⢠Fully Integrated with React, Next.js, Svelte, TypeScript, Vite & n8n ⢠FREE FOREVER ⢠10,000+ developers ⢠Championship Edition
296 lines ⢠11.8 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const commander_1 = require("../fix-once/commander");
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const yaml_1 = require("../fix-once/yaml");
const colors_1 = require("../fix-once/colors");
// Simple color utilities
const colors = {
success: (text) => colors_1.chalk.green(text),
error: (text) => colors_1.chalk.red(text),
warning: (text) => colors_1.chalk.yellow(text),
header: (text) => colors_1.chalk.bold.cyan(text),
highlight: (text) => colors_1.chalk.yellow.bold(text),
command: (text) => colors_1.chalk.cyan(text),
file: (text) => colors_1.chalk.blue(text),
url: (text) => colors_1.chalk.underline.blue(text)
};
// Simple logger
const logger = {
error: (message, error) => {
console.error(colors.error(message));
if (error)
console.error(error);
}
};
async function findProjectRoot(startDir) {
return startDir; // Simple implementation - use current dir
}
const program = new commander_1.Command();
async function fileExists(filePath) {
try {
await fs.access(filePath);
return true;
}
catch {
return false;
}
}
async function checkFafHealth(projectRoot) {
const report = {
status: 'healthy',
issues: [],
fixes: [],
autoFixed: false
};
const fafPath = path.join(projectRoot, '.faf');
const dnaPath = path.join(projectRoot, '.faf-dna.json');
const backupPath = path.join(projectRoot, '.faf.backup');
// Check .faf file
const fafExists = await fileExists(fafPath);
if (!fafExists) {
report.status = 'missing';
report.issues.push('.faf file is missing');
if (await fileExists(backupPath)) {
report.status = 'recoverable';
report.fixes.push('Found .faf.backup - can restore');
}
else {
report.fixes.push('Run: faf init --force to recreate');
}
}
else {
// Try to parse .faf
try {
const content = await fs.readFile(fafPath, 'utf-8');
(0, yaml_1.parse)(content);
}
catch (error) {
report.status = 'corrupted';
report.issues.push('.faf file is corrupted (invalid YAML)');
if (await fileExists(backupPath)) {
report.status = 'recoverable';
report.fixes.push('Found .faf.backup - can restore');
}
else {
report.fixes.push('Check git: git checkout HEAD -- .faf');
report.fixes.push('Or rebuild: faf init --force');
}
}
}
// Check DNA file
const dnaExists = await fileExists(dnaPath);
if (!dnaExists) {
report.issues.push('.faf-dna.json is missing (journey history lost)');
report.fixes.push('Run: faf auth to start new journey');
}
else {
try {
const content = await fs.readFile(dnaPath, 'utf-8');
JSON.parse(content);
}
catch {
report.issues.push('.faf-dna.json is corrupted');
report.fixes.push('Delete and recreate: rm .faf-dna.json && faf auth');
}
}
// Check for backups
const backups = await findBackups(projectRoot);
if (backups.length > 0) {
report.fixes.push(`Found ${backups.length} backup(s): ${backups.join(', ')}`);
}
return report;
}
async function findBackups(projectRoot) {
const backups = [];
try {
const files = await fs.readdir(projectRoot);
for (const file of files) {
if (file.startsWith('.faf.backup') || file.endsWith('.faf.bak')) {
backups.push(file);
}
}
}
catch {
// Directory not readable
}
return backups;
}
async function autoRecover(projectRoot, report) {
const fafPath = path.join(projectRoot, '.faf');
const backupPath = path.join(projectRoot, '.faf.backup');
if (report.status === 'recoverable' && await fileExists(backupPath)) {
try {
// Validate backup first
const backupContent = await fs.readFile(backupPath, 'utf-8');
(0, yaml_1.parse)(backupContent); // Will throw if invalid
// Backup is valid, restore it
await fs.copyFile(backupPath, fafPath);
console.log(colors.success(`\nā
Restored .faf from backup`));
return true;
}
catch (error) {
console.log(colors.error(`\nā Backup is also corrupted`));
return false;
}
}
return false;
}
async function showRecoveryPlan(report) {
console.log('\n' + colors.header('šØ FAF DISASTER RECOVERY'));
console.log('ā'.repeat(50));
// Status
const statusEmoji = {
healthy: 'ā
',
corrupted: 'š„',
missing: 'ā',
recoverable: 'š§'
}[report.status];
console.log(`\n${statusEmoji} Status: ${colors.highlight(report.status.toUpperCase())}`);
// Issues
if (report.issues.length > 0) {
console.log('\n' + colors.error('Issues Detected:'));
report.issues.forEach(issue => {
console.log(` ⢠${issue}`);
});
}
// Recovery options
if (report.fixes.length > 0) {
console.log('\n' + colors.success('Recovery Options:'));
report.fixes.forEach((fix, index) => {
console.log(` ${index + 1}. ${fix}`);
});
}
// Emergency commands
console.log('\n' + colors.header('EMERGENCY COMMANDS:'));
console.log('ā'.repeat(50));
console.log(colors.command(' faf recover --auto ') + '# Try automatic recovery');
console.log(colors.command(' faf recover --backup ') + '# List all backups');
console.log(colors.command(' faf init --force ') + '# Start fresh (loses history)');
console.log(colors.command(' faf help disaster ') + '# Full recovery guide');
// Git recovery
console.log('\n' + colors.header('GIT RECOVERY:'));
console.log('ā'.repeat(50));
console.log(colors.command(' git status ') + '# Check git state');
console.log(colors.command(' git checkout HEAD -- .faf') + '# Restore from git');
console.log(colors.command(' git log -p .faf ') + '# View .faf history');
// Support
console.log('\n' + colors.header('GET HELP:'));
console.log('ā'.repeat(50));
console.log(` š Docs: ${colors.url('https://faf.dev/disaster-recovery')}`);
console.log(` š¬ Discord: ${colors.url('https://discord.gg/faf')}`);
console.log(` š§ Email: ${colors.url('help@faf.dev')}`);
}
program
.name('faf-recover')
.description('šØ Disaster recovery for corrupted or missing FAF files')
.option('--auto', 'Attempt automatic recovery')
.option('--backup', 'List available backups')
.option('--force', 'Force recovery even if risky')
.option('--check', 'Check health without recovery')
.action(async (options) => {
try {
const projectRoot = await findProjectRoot(process.cwd());
// Check health
const report = await checkFafHealth(projectRoot);
if (options.backup) {
const backups = await findBackups(projectRoot);
console.log('\n' + colors.header('š¦ AVAILABLE BACKUPS'));
console.log('ā'.repeat(50));
if (backups.length === 0) {
console.log(colors.warning('\nNo backups found'));
console.log('Tip: FAF creates automatic backups before risky operations');
}
else {
for (const backup of backups) {
const stats = await fs.stat(path.join(projectRoot, backup));
console.log(`\n ⢠${colors.file(backup)}`);
console.log(` Modified: ${stats.mtime.toLocaleString()}`);
console.log(` Size: ${stats.size} bytes`);
}
console.log('\n' + colors.success('To restore a backup:'));
console.log(colors.command(` cp ${backups[0]} .faf`));
}
return;
}
if (report.status === 'healthy' && !options.check) {
console.log(colors.success('\nā
Your FAF files are healthy!'));
console.log('\nNo recovery needed. Keep going! š');
return;
}
// Show recovery plan
await showRecoveryPlan(report);
// Auto-recover if requested
if (options.auto && report.status === 'recoverable') {
console.log('\n' + colors.warning('Attempting automatic recovery...'));
const recovered = await autoRecover(projectRoot, report);
if (recovered) {
console.log(colors.success('\nš Recovery successful!'));
console.log('Your .faf file has been restored.');
console.log('\nNext steps:');
console.log(' 1. Run: faf score # Check your score');
console.log(' 2. Run: faf dna # Check your journey');
console.log(' 3. Run: faf update # Save a checkpoint');
}
else {
console.log(colors.error('\nā Automatic recovery failed'));
console.log('Please try manual recovery options above.');
}
}
// Show prevention tips
if (report.issues.length > 0) {
console.log('\n' + colors.header('š” PREVENTION TIPS:'));
console.log('ā'.repeat(50));
console.log(' ⢠Commit .faf to git regularly');
console.log(' ⢠Use "faf update" to save checkpoints');
console.log(' ⢠Enable auto-backup: faf trust --backup-mode');
console.log(' ⢠Keep score above 70% for best results');
}
}
catch (error) {
logger.error('Recovery failed:', error);
console.log(colors.error('\nā Critical error during recovery'));
console.log('\nLast resort options:');
console.log(' 1. Delete everything and start fresh:');
console.log(' rm -f .faf .faf-dna.json');
console.log(' faf init --force');
console.log(' 2. Contact support: help@faf.dev');
}
});
program.parse(process.argv);
//# sourceMappingURL=faf-recover.js.map