@yemi33/minions 0.1.384 → 0.1.386

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/CHANGELOG.md CHANGED
@@ -1,8 +1,14 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.384 (2026-04-06)
3
+ ## 0.1.386 (2026-04-06)
4
+
5
+ ### Fixes
6
+ - cache git rev-parse in version check — was spawning every 4s
7
+
8
+ ## 0.1.385 (2026-04-06)
4
9
 
5
10
  ### Features
11
+ - npm update check — show when newer version is available
6
12
  - show engine version and stale-code warning in dashboard
7
13
 
8
14
  ## 0.1.383 (2026-04-06)
@@ -215,19 +215,24 @@ function renderVersionBanner(version) {
215
215
  if (!el) return;
216
216
  if (!version) { el.style.display = 'none'; return; }
217
217
 
218
- // Show version in footer area
219
- const label = version.running ? 'v' + version.running : '';
218
+ const v = version.running || version.disk || '?';
220
219
  const commitLabel = version.runningCommit ? ' (' + version.runningCommit + ')' : '';
221
- el.textContent = label + commitLabel;
222
- el.title = 'Engine: v' + (version.running || '?') + ' ' + (version.runningCommit || '') +
223
- '\nDisk: v' + (version.disk || '?') + ' ' + (version.diskCommit || '');
224
220
 
225
221
  if (version.stale) {
222
+ // Engine running old code — needs restart
226
223
  el.style.cssText = 'font-size:9px;padding:2px 8px;background:rgba(210,153,34,0.15);border:1px solid rgba(210,153,34,0.3);border-radius:4px;color:var(--yellow);cursor:help';
227
- el.textContent = '\u26A0 Engine running v' + (version.running || '?') + ' (' + (version.runningCommit || '?') + ') — disk has v' + (version.disk || '?') + ' (' + (version.diskCommit || '?') + '). Restart to apply.';
224
+ el.textContent = '\u26A0 Engine running v' + (version.running || '?') + ' — disk has v' + (version.disk || '?') + '. Restart to apply.';
228
225
  el.title = 'The engine process is running older code than what is on disk. Run: minions restart';
226
+ } else if (version.updateAvailable) {
227
+ // New version on npm
228
+ el.style.cssText = 'font-size:9px;padding:2px 8px;background:rgba(63,185,80,0.1);border:1px solid rgba(63,185,80,0.3);border-radius:4px;color:var(--green);cursor:help';
229
+ el.textContent = 'v' + v + commitLabel + ' — v' + version.latest + ' available. Run: npm update -g @yemi33/minions';
230
+ el.title = 'A newer version is available on npm';
229
231
  } else {
232
+ // Up to date
230
233
  el.style.cssText = 'font-size:9px;color:var(--muted)';
234
+ el.textContent = 'v' + v + commitLabel;
235
+ el.title = 'Minions v' + v + (version.latest ? ' (latest)' : '');
231
236
  }
232
237
  }
233
238
 
package/dashboard.js CHANGED
@@ -161,6 +161,63 @@ function getVerifyGuides() {
161
161
  function getArchivedPrds() { return []; }
162
162
  function getEngineState() { return queries.getControl(); }
163
163
 
164
+ // ── npm update check (cached for 4 hours) ──────────────────────────────────
165
+ let _npmVersionCache = null;
166
+ let _npmVersionCacheTs = 0;
167
+ const NPM_CHECK_INTERVAL = 4 * 60 * 60 * 1000; // 4 hours
168
+ const PKG_NAME = '@yemi33/minions';
169
+
170
+ async function checkNpmVersion() {
171
+ const now = Date.now();
172
+ if (_npmVersionCache && (now - _npmVersionCacheTs) < NPM_CHECK_INTERVAL) return _npmVersionCache;
173
+ try {
174
+ const https = require('https');
175
+ const data = await new Promise((resolve, reject) => {
176
+ const req = https.get(`https://registry.npmjs.org/${PKG_NAME}/latest`, { timeout: 5000 }, (res) => {
177
+ let body = '';
178
+ res.on('data', c => body += c);
179
+ res.on('end', () => { try { resolve(JSON.parse(body)); } catch { reject(new Error('bad json')); } });
180
+ });
181
+ req.on('error', reject);
182
+ req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
183
+ });
184
+ _npmVersionCache = { latest: data.version || null, checkedAt: new Date().toISOString() };
185
+ _npmVersionCacheTs = now;
186
+ } catch {
187
+ _npmVersionCache = _npmVersionCache || { latest: null, checkedAt: null, error: 'check failed' };
188
+ }
189
+ return _npmVersionCache;
190
+ }
191
+
192
+ function _compareVersions(a, b) {
193
+ const pa = (a || '').split('.').map(Number);
194
+ const pb = (b || '').split('.').map(Number);
195
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
196
+ if ((pa[i] || 0) > (pb[i] || 0)) return 1;
197
+ if ((pa[i] || 0) < (pb[i] || 0)) return -1;
198
+ }
199
+ return 0;
200
+ }
201
+
202
+ // Kick off first npm check on startup (non-blocking)
203
+ checkNpmVersion().catch(() => {});
204
+
205
+ // Cache disk version + git commit (only changes on deploy/pull, not per-request)
206
+ let _diskVersionCache = null;
207
+ let _diskVersionCacheTs = 0;
208
+ const DISK_VERSION_TTL = 60000; // re-check every 60s
209
+ function getDiskVersion() {
210
+ const now = Date.now();
211
+ if (_diskVersionCache && (now - _diskVersionCacheTs) < DISK_VERSION_TTL) return _diskVersionCache;
212
+ let diskVersion = null;
213
+ try { diskVersion = require('./package.json').version; } catch {}
214
+ let diskCommit = null;
215
+ try { diskCommit = require('child_process').execSync('git rev-parse --short HEAD', { cwd: MINIONS_DIR, encoding: 'utf8', timeout: 5000, windowsHide: true }).trim(); } catch {}
216
+ _diskVersionCache = { diskVersion, diskCommit };
217
+ _diskVersionCacheTs = now;
218
+ return _diskVersionCache;
219
+ }
220
+
164
221
  function getMcpServers() {
165
222
  try {
166
223
  const home = os.homedir();
@@ -250,10 +307,7 @@ function getStatus() {
250
307
  installId: safeRead(path.join(MINIONS_DIR, '.install-id')).trim() || null,
251
308
  version: (() => {
252
309
  const engine = getEngineState();
253
- let diskVersion = null;
254
- try { diskVersion = require('./package.json').version; } catch {}
255
- let diskCommit = null;
256
- try { diskCommit = require('child_process').execSync('git rev-parse --short HEAD', { cwd: MINIONS_DIR, encoding: 'utf8', timeout: 5000, windowsHide: true }).trim(); } catch {}
310
+ const { diskVersion, diskCommit } = getDiskVersion();
257
311
  return {
258
312
  running: engine.codeVersion || null,
259
313
  runningCommit: engine.codeCommit || null,
@@ -261,6 +315,8 @@ function getStatus() {
261
315
  diskCommit,
262
316
  stale: !!(engine.codeVersion && diskVersion && engine.codeVersion !== diskVersion) ||
263
317
  !!(engine.codeCommit && diskCommit && engine.codeCommit !== diskCommit),
318
+ latest: _npmVersionCache?.latest || null,
319
+ updateAvailable: !!(diskVersion && _npmVersionCache?.latest && _npmVersionCache.latest !== diskVersion && _compareVersions(_npmVersionCache.latest, diskVersion) > 0),
264
320
  };
265
321
  })(),
266
322
  timestamp: new Date().toISOString(),
@@ -3367,6 +3423,22 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3367
3423
 
3368
3424
  const ROUTES = [
3369
3425
  // Routes endpoint (self-describing API)
3426
+ { method: 'GET', path: '/api/version', desc: 'Current + latest version info with update check', handler: async (req, res) => {
3427
+ const npm = await checkNpmVersion();
3428
+ const { diskVersion, diskCommit } = getDiskVersion();
3429
+ const engine = getEngineState();
3430
+ return jsonReply(res, 200, {
3431
+ current: diskVersion,
3432
+ currentCommit: diskCommit,
3433
+ running: engine.codeVersion || null,
3434
+ runningCommit: engine.codeCommit || null,
3435
+ latest: npm.latest,
3436
+ updateAvailable: !!(diskVersion && npm.latest && _compareVersions(npm.latest, diskVersion) > 0),
3437
+ stale: !!(engine.codeVersion && diskVersion && engine.codeVersion !== diskVersion) ||
3438
+ !!(engine.codeCommit && diskCommit && engine.codeCommit !== diskCommit),
3439
+ checkedAt: npm.checkedAt,
3440
+ });
3441
+ }},
3370
3442
  { method: 'GET', path: '/api/routes', desc: 'List all available API endpoints', handler: (req, res) => {
3371
3443
  const list = ROUTES.map(r => ({
3372
3444
  method: r.method,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.384",
3
+ "version": "0.1.386",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"