mirror of
https://github.com/Heretek-AI/openclaw.git
synced 2026-07-19 18:43:46 -04:00
a9ae1a6778
Matrix Protocol: - docker-compose.matrix.yml: Dendrite homeserver + PostgreSQL + Nginx TLS - src/channels/plugins/matrix-channel.ts: OpenClaw plugin implementation - docs/matrix-triad-setup.md: Setup guide with auth scheme (@tm1-4:triad.local) MCP Server Integration: - docs/mcp-triad-integration.md: SearXNG, Playwright, GitHub MCP configs - docs/mcp-curiosity-mapping.md: Gap-to-capability mapping Node Sync Architecture: - src/services/node-sync-service.ts: WebSocket peer sync + presence detection - src/services/node-sync-service.test.ts: Unit tests - docs/node-sync-architecture.md: Architecture docs Triad Resilience: - scripts/triad-corruption-check.mjs: SQLite + log + config + git integrity - docs/triad-resilience.md: Recovery procedures - .secure/deployment-logs/README.md: Schema v2 - skills/triad-heartbeat/SKILL.md: Corruption check integration NPM Publish Workflow: - scripts/npm-publish.mjs: version, changelog, validate, publish, rollback - .github/workflows/npm-publish.yml: GitHub Actions with provenance - docs/npm-publish-guide.md: Complete documentation All deliverables tested in Docker before production.
123 lines
4.3 KiB
JavaScript
Executable File
123 lines
4.3 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
// Triad Ledger Backup — Exports SQLite to JSON, commits to Tabula_Myriad
|
|
// Usage: node scripts/backup-ledger.js [--remote]
|
|
|
|
import { execSync } from "child_process";
|
|
import fs from "fs";
|
|
import path from "path";
|
|
import { fileURLToPath } from "url";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const workspaceRoot = path.join(__dirname, "..");
|
|
const auraPath = path.join(workspaceRoot, ".aura");
|
|
const dbPath = path.join(auraPath, "consensus.db");
|
|
const backupDir = path.join(workspaceRoot, ".ledger-backups");
|
|
const privateRepoPath =
|
|
process.env.TABULA_MYRIAD_PATH ||
|
|
path.join(process.env.HOME || "/home/openclaw", "tabula-myriad-private");
|
|
|
|
// Ensure backup dir exists
|
|
if (!fs.existsSync(backupDir)) {
|
|
fs.mkdirSync(backupDir, { recursive: true });
|
|
}
|
|
|
|
console.log("🦞 Triad Ledger Backup — Starting...\n");
|
|
|
|
// Check if better-sqlite3 is available
|
|
let Database;
|
|
try {
|
|
Database = (await import("better-sqlite3")).default;
|
|
} catch {
|
|
console.log("❌ better-sqlite3 not installed. Run: npm install better-sqlite3");
|
|
process.exit(1);
|
|
}
|
|
|
|
// Open DB
|
|
if (!fs.existsSync(dbPath)) {
|
|
console.log(`❌ Ledger DB not found: ${dbPath}`);
|
|
console.log("Run: node scripts/init-triad-ledger.js first");
|
|
process.exit(1);
|
|
}
|
|
|
|
const db = new Database(dbPath);
|
|
console.log(`✅ Opened: ${dbPath}`);
|
|
|
|
// Export tables (handle missing tables gracefully)
|
|
const tables = {};
|
|
const tableQueries = {
|
|
consensus_votes: "SELECT * FROM consensus_votes ORDER BY timestamp DESC",
|
|
triad_state: "SELECT * FROM triad_state",
|
|
triad_tasks: "SELECT * FROM triad_tasks ORDER BY created_at DESC",
|
|
knowledge_entries:
|
|
"SELECT id, source, title, tags, ingested_at FROM knowledge_entries ORDER BY ingested_at DESC LIMIT 100",
|
|
curiosity_metrics: "SELECT * FROM curiosity_metrics ORDER BY timestamp DESC LIMIT 30",
|
|
};
|
|
|
|
for (const [table, query] of Object.entries(tableQueries)) {
|
|
try {
|
|
tables[table] = db.prepare(query).all();
|
|
console.log(`✅ Exported ${tables[table].length} rows from ${table}`);
|
|
} catch (e) {
|
|
if (e.code === "SQLITE_ERROR" && e.message.includes("no such table")) {
|
|
tables[table] = [];
|
|
console.log(`⚠️ Table ${table} not yet created (skipped)`);
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Count records
|
|
const total = Object.entries(tables).reduce((sum, [, rows]) => sum + rows.length, 0);
|
|
console.log(`✅ Exported ${total} records across ${Object.keys(tables).length} tables`);
|
|
|
|
// Write JSON backup
|
|
const timestamp = new Date().toISOString().replace(/:/g, "-").slice(0, 19);
|
|
const backupFile = path.join(backupDir, `ledger-backup-${timestamp}.json`);
|
|
fs.writeFileSync(backupFile, JSON.stringify({ timestamp, tables }, null, 2));
|
|
console.log(`✅ Backup written: ${backupFile}`);
|
|
|
|
// Also write latest.json for easy restore
|
|
const latestFile = path.join(backupDir, "latest.json");
|
|
fs.writeFileSync(latestFile, JSON.stringify({ timestamp, tables }, null, 2));
|
|
console.log(`✅ Latest symlink: ${latestFile}`);
|
|
|
|
// Close DB
|
|
db.close();
|
|
|
|
// Copy to private repo if --remote flag
|
|
if (process.argv.includes("--remote")) {
|
|
if (!fs.existsSync(privateRepoPath)) {
|
|
console.log(`⚠️ Private repo not found: ${privateRepoPath}`);
|
|
console.log("Clone Tabula_Myriad first, or set TABULA_MYRIAD_PATH env var");
|
|
} else {
|
|
const ledgerBackupsPath = path.join(privateRepoPath, "ledger-backups");
|
|
if (!fs.existsSync(ledgerBackupsPath)) {
|
|
fs.mkdirSync(ledgerBackupsPath, { recursive: true });
|
|
}
|
|
|
|
// Copy backup
|
|
const remoteBackup = path.join(ledgerBackupsPath, `ledger-backup-${timestamp}.json`);
|
|
fs.copyFileSync(backupFile, remoteBackup);
|
|
fs.copyFileSync(latestFile, path.join(ledgerBackupsPath, "latest.json"));
|
|
console.log(`✅ Copied to private repo: ${ledgerBackupsPath}`);
|
|
|
|
// Commit
|
|
try {
|
|
execSync("git add -A", { cwd: privateRepoPath, stdio: "pipe" });
|
|
execSync(`git commit -m "Ledger backup ${timestamp}"`, {
|
|
cwd: privateRepoPath,
|
|
stdio: "pipe",
|
|
});
|
|
execSync("git push", { cwd: privateRepoPath, stdio: "pipe" });
|
|
console.log(`✅ Committed + pushed to Tabula_Myriad`);
|
|
} catch {
|
|
console.log("⚠️ Git commit failed (may be unconfigured). Manual commit needed.");
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log("\n🦞 Backup complete.\n");
|
|
console.log("Restore command:");
|
|
console.log(` node scripts/restore-ledger.js ${latestFile}`);
|