backup: add JSON plan + verify-backup

This commit is contained in:
2026-03-07 13:00:18 +10:00
parent 9e9a2889ce
commit 400a89a897
+72 -14
View File
@@ -150,18 +150,41 @@ async function doctor() {
} }
} }
function backupPlan() { function backupPlan(opts: { json?: boolean } = {}) {
console.log('Backup include list (recommended):'); const plan = {
console.log('- ~/.openclaw/openclaw.json'); dailyEssential: {
console.log('- ~/.openclaw/.env (if used)'); include: [
console.log('- ~/.openclaw/credentials'); '~/.openclaw/openclaw.json',
console.log('- ~/.openclaw/agents'); '~/.openclaw/.env',
console.log('- ~/.openclaw/memory (per-agent sqlite)'); '~/.openclaw/credentials',
console.log('- ~/.openclaw/workspaces'); '~/.openclaw/agents',
console.log('- ~/.openclaw/workspace/main'); '~/.openclaw/memory',
console.log('- ~/.openclaw/cron'); '~/.openclaw/workspaces',
console.log('Weekly full add-on:'); '~/.openclaw/workspace/main',
console.log('- ~/.openclaw/agents/*/sessions'); '~/.openclaw/cron'
],
exclude: [
'~/.openclaw/logs',
'~/.openclaw/agents/*/sessions'
]
},
weeklyFull: {
include: ['~/.openclaw/agents/*/sessions']
}
};
if (opts.json) {
console.log(JSON.stringify(plan, null, 2));
return;
}
console.log('Backup plan (recommended)');
console.log('\nDaily (essential):');
for (const p of plan.dailyEssential.include) console.log(`- ${p}`);
console.log('\nDaily excludes:');
for (const p of plan.dailyEssential.exclude) console.log(`- ${p}`);
console.log('\nWeekly add-on (full):');
for (const p of plan.weeklyFull.include) console.log(`- ${p}`);
} }
function switchProvider(to: 'openai' | 'ollama') { function switchProvider(to: 'openai' | 'ollama') {
@@ -188,17 +211,52 @@ function switchProvider(to: 'openai' | 'ollama') {
const [,, cmd, ...args] = process.argv; const [,, cmd, ...args] = process.argv;
function untildify(p: string): string {
if (p.startsWith('~/')) return join(HOME, p.slice(2));
return p;
}
if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') { if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
console.log('clawkeeper'); console.log('clawkeeper');
console.log('Commands:'); console.log('Commands:');
console.log(' clawkeeper doctor'); console.log(' clawkeeper doctor');
console.log(' clawkeeper switch openai|ollama'); console.log(' clawkeeper switch openai|ollama');
console.log(' clawkeeper backup-plan'); console.log(' clawkeeper backup-plan [--json]');
console.log(' clawkeeper verify-backup <path.tgz>');
process.exit(0); process.exit(0);
} }
if (cmd === 'doctor') await doctor(); if (cmd === 'doctor') await doctor();
else if (cmd === 'backup-plan') backupPlan(); else if (cmd === 'backup-plan') {
const json = args.includes('--json');
backupPlan({ json });
}
else if (cmd === 'verify-backup') {
const file = args[0];
if (!file) die('Usage: clawkeeper verify-backup <path.tgz>');
const tgz = untildify(file);
try {
const list = execFileSync('/usr/bin/tar', ['-tzf', tgz], { stdio: ['ignore', 'pipe', 'pipe'] }).toString('utf8');
const required = [
'.openclaw/openclaw.json',
'.openclaw/credentials',
'.openclaw/agents',
'.openclaw/memory',
'.openclaw/workspaces',
'.openclaw/cron'
];
const missing = required.filter(r => !list.includes(r));
if (missing.length) {
console.log('FAIL missing required paths:');
for (const m of missing) console.log(`- ${m}`);
process.exitCode = 2;
} else {
console.log('OK backup contains required paths.');
}
} catch (e: any) {
die(`Failed to read tarball: ${e?.message || e}`);
}
}
else if (cmd === 'switch') { else if (cmd === 'switch') {
const to = args[0]; const to = args[0];
if (to !== 'openai' && to !== 'ollama') die('Usage: clawkeeper switch openai|ollama'); if (to !== 'openai' && to !== 'ollama') die('Usage: clawkeeper switch openai|ollama');