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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolverine-ai",
3
- "version": "2.4.3",
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 npm registry for the latest published version.
42
- * Uses `npm view` no network dependency beyond npm.
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
- return result || null;
51
- } catch {
52
- return null;
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 config files before update and restore after.
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 backupConfigs(cwd) {
74
- const configs = [
75
- "server/config/settings.json",
76
- ".env.local",
77
- ".env",
78
- ];
98
+ function backupUserFiles(cwd) {
79
99
  const backups = {};
80
- for (const file of configs) {
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 restoreConfigs(cwd, backups) {
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 configBackups = backupConfigs(cwd);
115
- console.log(chalk.gray(` 🔒 Backed up ${Object.keys(configBackups).length} config files`));
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
- // Determine install method: global or local
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
- console.log(chalk.blue(` 📦 Running: ${cmd}`));
125
- execSync(cmd, { cwd, stdio: "pipe", timeout: 120000 });
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 configs (npm might have overwritten them)
128
- restoreConfigs(cwd, configBackups);
129
- console.log(chalk.gray(` 🔒 Restored config files`));
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) {