mirror of
https://github.com/dtzp555-max/clawkeeper.git
synced 2026-07-19 09:41:42 +00:00
restore: safe default (no gateway restart) + --restart-gateway flag
This commit is contained in:
+17
@@ -2,6 +2,7 @@
|
||||
import { readFileSync, writeFileSync, copyFileSync, existsSync } from 'node:fs';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { join } from 'node:path';
|
||||
import { restoreApply, restoreGuide } from './restore.js';
|
||||
|
||||
type Json = any;
|
||||
|
||||
@@ -223,6 +224,8 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
||||
console.log(' clawkeeper switch openai|ollama');
|
||||
console.log(' clawkeeper backup-plan [--json]');
|
||||
console.log(' clawkeeper verify-backup <path.tgz>');
|
||||
console.log(' clawkeeper restore-guide');
|
||||
console.log(' clawkeeper restore-apply <path.tgz>');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
@@ -257,6 +260,20 @@ else if (cmd === 'verify-backup') {
|
||||
die(`Failed to read tarball: ${e?.message || e}`);
|
||||
}
|
||||
}
|
||||
else if (cmd === 'restore-guide') {
|
||||
restoreGuide({ home: HOME });
|
||||
}
|
||||
else if (cmd === 'restore-apply') {
|
||||
const file = args[0];
|
||||
if (!file) die('Usage: clawkeeper restore-apply <path.tgz> [--restart-gateway]');
|
||||
const restartGateway = args.includes('--restart-gateway');
|
||||
const tgz = untildify(file);
|
||||
try {
|
||||
restoreApply({ home: HOME, tgzPath: tgz, restartGateway });
|
||||
} catch (e: any) {
|
||||
die(String(e?.message || e));
|
||||
}
|
||||
}
|
||||
else if (cmd === 'switch') {
|
||||
const to = args[0];
|
||||
if (to !== 'openai' && to !== 'ollama') die('Usage: clawkeeper switch openai|ollama');
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { existsSync, renameSync, mkdirSync } from 'node:fs';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { join } from 'node:path';
|
||||
|
||||
export function untildify(home: string, p: string): string {
|
||||
if (p.startsWith('~/')) return join(home, p.slice(2));
|
||||
return p;
|
||||
}
|
||||
|
||||
export function run(cmd: string, args: string[], quiet = false): string {
|
||||
const out = execFileSync(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
||||
const s = out.toString('utf8');
|
||||
if (!quiet) process.stdout.write(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
export function restoreGuide(opts: { home: string }) {
|
||||
const { home } = opts;
|
||||
const lines: string[] = [];
|
||||
lines.push('Clawkeeper restore guide (safe mode)');
|
||||
lines.push('');
|
||||
lines.push('0) Install OpenClaw on the new machine');
|
||||
lines.push(' - Follow OpenClaw install docs for your OS');
|
||||
lines.push('');
|
||||
lines.push('1) Copy backup tarball to the new machine, e.g. ~/openclaw-backup.tgz');
|
||||
lines.push('');
|
||||
lines.push('2) Restore (safe): rename existing ~/.openclaw before extracting');
|
||||
lines.push(' clawkeeper restore-apply ~/openclaw-backup.tgz');
|
||||
lines.push('');
|
||||
lines.push('3) Verify');
|
||||
lines.push(' openclaw config validate');
|
||||
lines.push(' openclaw gateway restart');
|
||||
lines.push(' openclaw status');
|
||||
lines.push(' openclaw memory status');
|
||||
lines.push('');
|
||||
lines.push('Notes:');
|
||||
lines.push('- This uses safe mode: if ~/.openclaw exists, it will be renamed to ~/.openclaw.bak.<timestamp>');
|
||||
lines.push('- Secrets are restored from ~/.openclaw/credentials and ~/.openclaw/.env (if present in backup).');
|
||||
console.log(lines.join('\n'));
|
||||
}
|
||||
|
||||
export function restoreApply(opts: { home: string; tgzPath: string; restartGateway?: boolean }) {
|
||||
const { home, tgzPath, restartGateway = false } = opts;
|
||||
const src = tgzPath;
|
||||
if (!existsSync(src)) throw new Error(`Backup not found: ${src}`);
|
||||
|
||||
const oc = join(home, '.openclaw');
|
||||
if (existsSync(oc)) {
|
||||
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
const bak = join(home, `.openclaw.bak.${ts}`);
|
||||
renameSync(oc, bak);
|
||||
console.log(`Renamed existing ~/.openclaw -> ${bak}`);
|
||||
}
|
||||
|
||||
// Extract into HOME. The tarball is expected to contain a top-level .openclaw/ folder.
|
||||
mkdirSync(oc, { recursive: true });
|
||||
run('/usr/bin/tar', ['-xzf', src, '-C', home], true);
|
||||
console.log('Extracted backup into HOME.');
|
||||
|
||||
// Best-effort validate
|
||||
try {
|
||||
run('openclaw', ['config', 'validate'], true);
|
||||
console.log('openclaw config validate: OK');
|
||||
} catch (e: any) {
|
||||
console.log(`openclaw config validate: WARN (${e?.message || e})`);
|
||||
}
|
||||
|
||||
if (restartGateway) {
|
||||
try {
|
||||
run('openclaw', ['gateway', 'restart'], true);
|
||||
console.log('openclaw gateway restart: OK');
|
||||
} catch (e: any) {
|
||||
console.log(`openclaw gateway restart: WARN (${e?.message || e})`);
|
||||
console.log('If gateway service is not loaded, try: openclaw gateway install --force');
|
||||
}
|
||||
} else {
|
||||
console.log('NOTE: gateway was NOT restarted (safe default).');
|
||||
console.log('To restart gateway after restore, run: openclaw gateway restart');
|
||||
console.log('If restart fails, run: openclaw gateway install --force');
|
||||
}
|
||||
|
||||
try {
|
||||
const s = run('openclaw', ['status'], true);
|
||||
console.log('openclaw status: OK');
|
||||
console.log(s.split('\n').slice(0, 20).join('\n'));
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user