wolverine-ai 2.6.0 → 2.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/package.json +1 -1
- package/src/backup/backup-manager.js +43 -7
- package/src/brain/brain.js +2 -2
- package/src/skills/update.js +8 -6
package/README.md
CHANGED
|
@@ -535,14 +535,22 @@ All demos use the `server/` directory pattern. Each demo:
|
|
|
535
535
|
|
|
536
536
|
## Backup System
|
|
537
537
|
|
|
538
|
-
|
|
538
|
+
All backups stored in **`~/.wolverine-safe-backups/`** — outside the project directory. Survives `git pull`, `npm install`, `rm -rf .wolverine`, even deleting the project entirely.
|
|
539
539
|
|
|
540
|
-
|
|
540
|
+
```
|
|
541
|
+
~/.wolverine-safe-backups/
|
|
542
|
+
manifest.json ← backup registry
|
|
543
|
+
snapshots/ ← heal snapshots (per fix attempt)
|
|
544
|
+
updates/ ← pre-update snapshots (before framework upgrades)
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
- Created before every repair attempt and every framework update (with reason string)
|
|
541
548
|
- Created on graceful shutdown (`createShutdownBackup()`)
|
|
542
549
|
- Includes all files: `.js`, `.json`, `.sql`, `.db`, `.yaml`, configs
|
|
550
|
+
- Old `.wolverine/backups/` auto-migrated to safe location on first run
|
|
543
551
|
- **Status lifecycle**: UNSTABLE → VERIFIED (fix passed) → STABLE (30min+ uptime)
|
|
544
552
|
- **Retention**: unstable/verified pruned after 7 days, stable keeps 1/day after 7 days
|
|
545
|
-
-
|
|
553
|
+
- Protected files never overwritten during rollback: `settings.json`, `db.js`, `.env.local`
|
|
546
554
|
|
|
547
555
|
**Rollback & Recovery:**
|
|
548
556
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wolverine-ai",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.2",
|
|
4
4
|
"description": "Self-healing Node.js server framework powered by AI. Catches crashes, diagnoses errors, generates fixes, verifies, and restarts — automatically.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -14,9 +14,13 @@ const { redact } = require("../security/secret-redactor");
|
|
|
14
14
|
* Stable backups older than 7 days → keep 1 per day (most recent).
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
const os = require("os");
|
|
18
|
+
|
|
19
|
+
// Safe backup location — OUTSIDE the project directory.
|
|
20
|
+
// Survives git pull, npm install, rm -rf .wolverine, project deletion.
|
|
21
|
+
// All backup infrastructure (heal snapshots, update snapshots, rollbacks) uses this.
|
|
22
|
+
const SAFE_BACKUP_ROOT = path.join(os.homedir(), ".wolverine-safe-backups");
|
|
23
|
+
const MANIFEST_FILE_NAME = "manifest.json";
|
|
20
24
|
const STABILITY_THRESHOLD_MS = 30 * 60 * 1000;
|
|
21
25
|
const RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
22
26
|
|
|
@@ -33,9 +37,11 @@ const NEVER_ROLLBACK = [
|
|
|
33
37
|
class BackupManager {
|
|
34
38
|
constructor(projectRoot) {
|
|
35
39
|
this.projectRoot = path.resolve(projectRoot);
|
|
36
|
-
|
|
37
|
-
this.
|
|
40
|
+
// All backups in safe home directory — never inside the project
|
|
41
|
+
this.backupsDir = path.join(SAFE_BACKUP_ROOT, "snapshots");
|
|
42
|
+
this.manifestPath = path.join(SAFE_BACKUP_ROOT, MANIFEST_FILE_NAME);
|
|
38
43
|
this._ensureDirs();
|
|
44
|
+
this._migrateOldBackups(); // one-time migration from .wolverine/backups/
|
|
39
45
|
this.manifest = this._loadManifest();
|
|
40
46
|
}
|
|
41
47
|
|
|
@@ -276,7 +282,37 @@ class BackupManager {
|
|
|
276
282
|
|
|
277
283
|
// -- Private --
|
|
278
284
|
|
|
279
|
-
_ensureDirs() {
|
|
285
|
+
_ensureDirs() {
|
|
286
|
+
fs.mkdirSync(this.backupsDir, { recursive: true });
|
|
287
|
+
fs.mkdirSync(SAFE_BACKUP_ROOT, { recursive: true });
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* One-time migration: move backups from old .wolverine/backups/ to safe location.
|
|
292
|
+
*/
|
|
293
|
+
_migrateOldBackups() {
|
|
294
|
+
const oldDir = path.join(this.projectRoot, ".wolverine", "backups");
|
|
295
|
+
const oldManifest = path.join(oldDir, "manifest.json");
|
|
296
|
+
if (!fs.existsSync(oldManifest) || fs.existsSync(this.manifestPath)) return;
|
|
297
|
+
|
|
298
|
+
try {
|
|
299
|
+
// Copy manifest
|
|
300
|
+
fs.copyFileSync(oldManifest, this.manifestPath);
|
|
301
|
+
// Copy backup dirs
|
|
302
|
+
for (const entry of fs.readdirSync(oldDir, { withFileTypes: true })) {
|
|
303
|
+
if (!entry.isDirectory()) continue;
|
|
304
|
+
const src = path.join(oldDir, entry.name);
|
|
305
|
+
const dest = path.join(this.backupsDir, entry.name);
|
|
306
|
+
if (!fs.existsSync(dest)) {
|
|
307
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
308
|
+
for (const file of fs.readdirSync(src)) {
|
|
309
|
+
fs.copyFileSync(path.join(src, file), path.join(dest, file));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
console.log(chalk.gray(` 📦 Migrated backups to safe location: ${SAFE_BACKUP_ROOT}`));
|
|
314
|
+
} catch {}
|
|
315
|
+
}
|
|
280
316
|
|
|
281
317
|
_loadManifest() {
|
|
282
318
|
if (fs.existsSync(this.manifestPath)) {
|
|
@@ -323,4 +359,4 @@ class BackupManager {
|
|
|
323
359
|
}
|
|
324
360
|
}
|
|
325
361
|
|
|
326
|
-
module.exports = { BackupManager, STABILITY_THRESHOLD_MS };
|
|
362
|
+
module.exports = { BackupManager, STABILITY_THRESHOLD_MS, SAFE_BACKUP_ROOT };
|
package/src/brain/brain.js
CHANGED
|
@@ -38,7 +38,7 @@ const SEED_DOCS = [
|
|
|
38
38
|
metadata: { topic: "heal-pipeline" },
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
|
-
text: "Wolverine backup system:
|
|
41
|
+
text: "Wolverine backup system: ALL backups stored in ~/.wolverine-safe-backups/ (OUTSIDE project, survives git pull/npm install/rm -rf). Structure: snapshots/ (heal backups per fix attempt), updates/ (pre-update snapshots), manifest.json (backup registry). Old .wolverine/backups/ auto-migrated on first run. Full server/ directory snapshots with lifecycle management. Every fix creates a backup with a reason string before patching. Status lifecycle: UNSTABLE (just created) → VERIFIED (fix passed boot probe) → STABLE (server ran 30min+ without crash). Features: rollbackTo(backupId) creates pre-rollback backup then restores files and restarts server. undoRollback() restores pre-rollback state. Hot-load: admin can load any backup as current server state from dashboard. Shutdown backup on graceful exit. Retention: unstable/verified pruned after 7 days. Stable backups older than 7 days keep 1 per day. Rollback log tracks all rollback/undo operations with timestamps and success status. Dashboard endpoints: POST /api/backups/:id/rollback, POST /api/backups/undo, POST /api/backups/:id/hotload (all require admin auth).",
|
|
42
42
|
metadata: { topic: "backup-system" },
|
|
43
43
|
},
|
|
44
44
|
{
|
|
@@ -238,7 +238,7 @@ const SEED_DOCS = [
|
|
|
238
238
|
metadata: { topic: "skill-deps" },
|
|
239
239
|
},
|
|
240
240
|
{
|
|
241
|
-
text: "CRITICAL: Never run raw 'npm install wolverine-ai' or 'git pull' to update — these OVERWRITE server/, .wolverine/ (brain, backups, events), and .env.local. Always use the safe update skill: wolverine --update (CLI), safeUpdate(cwd) (programmatic), or let auto-update handle it.
|
|
241
|
+
text: "CRITICAL: Never run raw 'npm install wolverine-ai' or 'git pull' to update — these OVERWRITE server/, .wolverine/ (brain, backups, events), and .env.local. Always use the safe update skill: wolverine --update (CLI), safeUpdate(cwd) (programmatic), or let auto-update handle it. ALL backups (heal snapshots + update snapshots) stored in ~/.wolverine-safe-backups/ (OUTSIDE project, survives git clean, rm -rf, project deletion). Structure: ~/.wolverine-safe-backups/snapshots/ (heal backups), ~/.wolverine-safe-backups/updates/ (pre-update snapshots), ~/.wolverine-safe-backups/manifest.json (backup registry). Old .wolverine/backups/ auto-migrated on first run. Restore with: wolverine --restore <name>. List: wolverine --backups.",
|
|
242
242
|
metadata: { topic: "safe-update-warning" },
|
|
243
243
|
},
|
|
244
244
|
{
|
package/src/skills/update.js
CHANGED
|
@@ -27,6 +27,7 @@ const chalk = require("chalk");
|
|
|
27
27
|
|
|
28
28
|
const PACKAGE_NAME = "wolverine-ai";
|
|
29
29
|
const SAFE_BACKUP_DIR = path.join(require("os").homedir(), ".wolverine-safe-backups");
|
|
30
|
+
const SAFE_SNAPSHOTS_DIR = path.join(SAFE_BACKUP_DIR, "snapshots");
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* Create a safe backup snapshot outside the project directory.
|
|
@@ -34,7 +35,7 @@ const SAFE_BACKUP_DIR = path.join(require("os").homedir(), ".wolverine-safe-back
|
|
|
34
35
|
*/
|
|
35
36
|
function createSafeBackup(cwd) {
|
|
36
37
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
37
|
-
const backupDir = path.join(SAFE_BACKUP_DIR, timestamp);
|
|
38
|
+
const backupDir = path.join(SAFE_BACKUP_DIR, "updates", timestamp);
|
|
38
39
|
fs.mkdirSync(backupDir, { recursive: true });
|
|
39
40
|
|
|
40
41
|
const dirsToBackup = [
|
|
@@ -76,12 +77,13 @@ function createSafeBackup(cwd) {
|
|
|
76
77
|
* List available safe backups.
|
|
77
78
|
*/
|
|
78
79
|
function listSafeBackups() {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
const updatesDir = path.join(SAFE_BACKUP_DIR, "updates");
|
|
81
|
+
if (!fs.existsSync(updatesDir)) return [];
|
|
82
|
+
return fs.readdirSync(updatesDir)
|
|
83
|
+
.filter(d => fs.statSync(path.join(updatesDir, d)).isDirectory())
|
|
82
84
|
.map(d => {
|
|
83
85
|
try {
|
|
84
|
-
const manifest = JSON.parse(fs.readFileSync(path.join(SAFE_BACKUP_DIR, d, "manifest.json"), "utf-8"));
|
|
86
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(SAFE_BACKUP_DIR, "updates", d, "manifest.json"), "utf-8"));
|
|
85
87
|
return { dir: d, ...manifest };
|
|
86
88
|
} catch { return { dir: d }; }
|
|
87
89
|
})
|
|
@@ -92,7 +94,7 @@ function listSafeBackups() {
|
|
|
92
94
|
* Restore from a safe backup.
|
|
93
95
|
*/
|
|
94
96
|
function restoreFromSafeBackup(cwd, backupName) {
|
|
95
|
-
const backupDir = path.join(SAFE_BACKUP_DIR, backupName);
|
|
97
|
+
const backupDir = path.join(SAFE_BACKUP_DIR, "updates", backupName);
|
|
96
98
|
if (!fs.existsSync(backupDir)) throw new Error(`Backup not found: ${backupName}`);
|
|
97
99
|
|
|
98
100
|
const dirsToRestore = [".wolverine", "server"];
|