wolverine-ai 2.4.3 → 2.4.5
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/package.json +1 -1
- package/src/platform/auto-update.js +100 -33
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wolverine-ai",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.5",
|
|
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": {
|
|
@@ -38,19 +38,42 @@ function getCurrentVersion() {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* Check
|
|
42
|
-
*
|
|
41
|
+
* Check for the latest available version.
|
|
42
|
+
* For git repos: checks remote for newer commits via `git ls-remote`.
|
|
43
|
+
* For npm installs: checks npm registry via `npm view`.
|
|
43
44
|
*/
|
|
44
|
-
function getLatestVersion() {
|
|
45
|
+
function getLatestVersion(cwd) {
|
|
46
|
+
// Try npm registry first (works for both git and npm installs)
|
|
45
47
|
try {
|
|
46
48
|
const result = execSync(`npm view ${PACKAGE_NAME} version 2>/dev/null`, {
|
|
47
49
|
encoding: "utf-8",
|
|
48
50
|
timeout: 15000,
|
|
51
|
+
cwd: cwd || process.cwd(),
|
|
49
52
|
}).trim();
|
|
50
|
-
|
|
51
|
-
} catch {
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
if (result) return result;
|
|
54
|
+
} catch {}
|
|
55
|
+
|
|
56
|
+
// Fallback for git repos: check if remote has newer commits
|
|
57
|
+
try {
|
|
58
|
+
if (isGitRepo(cwd || process.cwd())) {
|
|
59
|
+
execSync("git fetch origin --quiet", { cwd: cwd || process.cwd(), stdio: "pipe", timeout: 15000 });
|
|
60
|
+
const behind = execSync("git rev-list HEAD..origin/master --count", {
|
|
61
|
+
cwd: cwd || process.cwd(), encoding: "utf-8", timeout: 5000,
|
|
62
|
+
}).trim();
|
|
63
|
+
if (parseInt(behind, 10) > 0) {
|
|
64
|
+
// There are newer commits — read version from remote package.json
|
|
65
|
+
try {
|
|
66
|
+
const remoteVersion = execSync("git show origin/master:package.json", {
|
|
67
|
+
cwd: cwd || process.cwd(), encoding: "utf-8", timeout: 5000,
|
|
68
|
+
});
|
|
69
|
+
const pkg = JSON.parse(remoteVersion);
|
|
70
|
+
return pkg.version || null;
|
|
71
|
+
} catch {}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} catch {}
|
|
75
|
+
|
|
76
|
+
return null;
|
|
54
77
|
}
|
|
55
78
|
|
|
56
79
|
/**
|
|
@@ -68,25 +91,46 @@ function isNewer(latest, current) {
|
|
|
68
91
|
}
|
|
69
92
|
|
|
70
93
|
/**
|
|
71
|
-
* Protect
|
|
94
|
+
* Protect ALL user files before update and restore after.
|
|
95
|
+
* The entire server/ directory is sacred — auto-update must never touch it.
|
|
96
|
+
* Also protects .env files and any user config.
|
|
72
97
|
*/
|
|
73
|
-
function
|
|
74
|
-
const configs = [
|
|
75
|
-
"server/config/settings.json",
|
|
76
|
-
".env.local",
|
|
77
|
-
".env",
|
|
78
|
-
];
|
|
98
|
+
function backupUserFiles(cwd) {
|
|
79
99
|
const backups = {};
|
|
80
|
-
|
|
100
|
+
|
|
101
|
+
// Protect individual config files
|
|
102
|
+
const protectedFiles = [".env.local", ".env", ".wolverine/mcp.json", ".wolverine/pricing.json"];
|
|
103
|
+
for (const file of protectedFiles) {
|
|
81
104
|
const fullPath = path.join(cwd, file);
|
|
82
105
|
if (fs.existsSync(fullPath)) {
|
|
83
106
|
backups[file] = fs.readFileSync(fullPath, "utf-8");
|
|
84
107
|
}
|
|
85
108
|
}
|
|
109
|
+
|
|
110
|
+
// Protect entire server/ directory (recursive)
|
|
111
|
+
const serverDir = path.join(cwd, "server");
|
|
112
|
+
if (fs.existsSync(serverDir)) {
|
|
113
|
+
const walk = (dir, base) => {
|
|
114
|
+
try {
|
|
115
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
116
|
+
for (const entry of entries) {
|
|
117
|
+
if (entry.name === "node_modules") continue;
|
|
118
|
+
const fullPath = path.join(dir, entry.name);
|
|
119
|
+
const relPath = path.join(base, entry.name).replace(/\\/g, "/");
|
|
120
|
+
if (entry.isDirectory()) { walk(fullPath, relPath); }
|
|
121
|
+
else {
|
|
122
|
+
try { backups[relPath] = fs.readFileSync(fullPath, "utf-8"); } catch {}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch {}
|
|
126
|
+
};
|
|
127
|
+
walk(serverDir, "server");
|
|
128
|
+
}
|
|
129
|
+
|
|
86
130
|
return backups;
|
|
87
131
|
}
|
|
88
132
|
|
|
89
|
-
function
|
|
133
|
+
function restoreUserFiles(cwd, backups) {
|
|
90
134
|
for (const [file, content] of Object.entries(backups)) {
|
|
91
135
|
const fullPath = path.join(cwd, file);
|
|
92
136
|
try {
|
|
@@ -96,8 +140,19 @@ function restoreConfigs(cwd, backups) {
|
|
|
96
140
|
}
|
|
97
141
|
}
|
|
98
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Detect if this is a git repo or an npm install.
|
|
145
|
+
*/
|
|
146
|
+
function isGitRepo(cwd) {
|
|
147
|
+
try {
|
|
148
|
+
execSync("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe", timeout: 3000 });
|
|
149
|
+
return true;
|
|
150
|
+
} catch { return false; }
|
|
151
|
+
}
|
|
152
|
+
|
|
99
153
|
/**
|
|
100
154
|
* Perform the upgrade. Returns { success, from, to, error? }
|
|
155
|
+
* Supports both npm-installed and git-cloned wolverine.
|
|
101
156
|
*/
|
|
102
157
|
function upgrade(cwd, logger) {
|
|
103
158
|
const current = getCurrentVersion();
|
|
@@ -110,23 +165,35 @@ function upgrade(cwd, logger) {
|
|
|
110
165
|
console.log(chalk.blue(`\n 🔄 Wolverine update available: ${current} → ${latest}`));
|
|
111
166
|
if (logger) logger.info("update.start", `Upgrading ${current} → ${latest}`, { from: current, to: latest });
|
|
112
167
|
|
|
113
|
-
// Back up configs
|
|
114
|
-
const
|
|
115
|
-
console.log(chalk.gray(` 🔒 Backed up ${Object.keys(
|
|
168
|
+
// Back up ALL user files (server/, .env, configs)
|
|
169
|
+
const userBackups = backupUserFiles(cwd);
|
|
170
|
+
console.log(chalk.gray(` 🔒 Backed up ${Object.keys(userBackups).length} user files (server/ protected)`));
|
|
116
171
|
|
|
117
172
|
try {
|
|
118
|
-
|
|
119
|
-
const isGlobal = __dirname.includes("node_modules") && !cwd.includes("node_modules");
|
|
120
|
-
const cmd = isGlobal
|
|
121
|
-
? `npm install -g ${PACKAGE_NAME}@${latest}`
|
|
122
|
-
: `npm install ${PACKAGE_NAME}@${latest}`;
|
|
173
|
+
const useGit = isGitRepo(cwd);
|
|
123
174
|
|
|
124
|
-
|
|
125
|
-
|
|
175
|
+
if (useGit) {
|
|
176
|
+
// Git-cloned: ONLY update framework files, NEVER touch server/
|
|
177
|
+
// Fetch latest, then selectively checkout only framework dirs
|
|
178
|
+
console.log(chalk.blue(` 📦 Git repo — selective framework update (server/ untouched)`));
|
|
179
|
+
execSync("git fetch origin master", { cwd, stdio: "pipe", timeout: 30000 });
|
|
180
|
+
// Only update: src/, bin/, package.json, examples/, tests/, CLAUDE.md, README.md, CHANGELOG.md
|
|
181
|
+
const frameworkPaths = "src/ bin/ package.json package-lock.json examples/ tests/ CLAUDE.md README.md CHANGELOG.md .npmignore";
|
|
182
|
+
execSync(`git checkout origin/master -- ${frameworkPaths}`, { cwd, stdio: "pipe", timeout: 30000 });
|
|
183
|
+
execSync("npm install", { cwd, stdio: "pipe", timeout: 120000 });
|
|
184
|
+
} else {
|
|
185
|
+
// npm-installed: update the package
|
|
186
|
+
const isGlobal = __dirname.includes("node_modules") && !cwd.includes("node_modules");
|
|
187
|
+
const cmd = isGlobal
|
|
188
|
+
? `npm install -g ${PACKAGE_NAME}@${latest}`
|
|
189
|
+
: `npm install ${PACKAGE_NAME}@${latest}`;
|
|
190
|
+
console.log(chalk.blue(` 📦 Running: ${cmd}`));
|
|
191
|
+
execSync(cmd, { cwd, stdio: "pipe", timeout: 120000 });
|
|
192
|
+
}
|
|
126
193
|
|
|
127
|
-
// Restore
|
|
128
|
-
|
|
129
|
-
console.log(chalk.gray(` 🔒 Restored
|
|
194
|
+
// Restore ALL user files (server/, .env, configs) — belt AND suspenders
|
|
195
|
+
restoreUserFiles(cwd, userBackups);
|
|
196
|
+
console.log(chalk.gray(` 🔒 Restored ${Object.keys(userBackups).length} user files`));
|
|
130
197
|
|
|
131
198
|
// Clear version cache
|
|
132
199
|
_currentVersion = null;
|
|
@@ -149,12 +216,12 @@ function upgrade(cwd, logger) {
|
|
|
149
216
|
* Check for updates (non-blocking). Logs if update available.
|
|
150
217
|
* Call upgrade() separately to actually apply.
|
|
151
218
|
*/
|
|
152
|
-
function checkForUpdate() {
|
|
219
|
+
function checkForUpdate(cwd) {
|
|
153
220
|
if (_checking) return null;
|
|
154
221
|
_checking = true;
|
|
155
222
|
try {
|
|
156
223
|
const current = getCurrentVersion();
|
|
157
|
-
const latest = getLatestVersion();
|
|
224
|
+
const latest = getLatestVersion(cwd);
|
|
158
225
|
_checking = false;
|
|
159
226
|
if (latest && isNewer(latest, current)) {
|
|
160
227
|
console.log(chalk.blue(` 🔄 Update available: ${PACKAGE_NAME} ${current} → ${latest}`));
|
|
@@ -184,7 +251,7 @@ function startAutoUpdate({ cwd, logger, onUpdate, intervalMs }) {
|
|
|
184
251
|
console.log(chalk.gray(` 🔄 Auto-update scheduled: first check in 30s, then every ${Math.round(interval / 60000)}min`));
|
|
185
252
|
setTimeout(() => {
|
|
186
253
|
console.log(chalk.gray(` 🔄 Checking for updates (v${getCurrentVersion()})...`));
|
|
187
|
-
const result = checkForUpdate();
|
|
254
|
+
const result = checkForUpdate(cwd);
|
|
188
255
|
if (result?.available) {
|
|
189
256
|
const upgraded = upgrade(cwd, logger);
|
|
190
257
|
if (upgraded.success && onUpdate) {
|
|
@@ -200,7 +267,7 @@ function startAutoUpdate({ cwd, logger, onUpdate, intervalMs }) {
|
|
|
200
267
|
|
|
201
268
|
// Periodic check
|
|
202
269
|
_timer = setInterval(() => {
|
|
203
|
-
const result = checkForUpdate();
|
|
270
|
+
const result = checkForUpdate(cwd);
|
|
204
271
|
if (result?.available) {
|
|
205
272
|
const upgraded = upgrade(cwd, logger);
|
|
206
273
|
if (upgraded.success && onUpdate) {
|