@yemi33/minions 0.1.1825 → 0.1.1826

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,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1826 (2026-05-09)
4
+
5
+ ### Fixes
6
+ - always open dashboard if no live tab reconnects after restart
7
+
3
8
  ## 0.1.1825 (2026-05-09)
4
9
 
5
10
  ### Fixes
package/bin/minions.js CHANGED
@@ -86,6 +86,43 @@ function hasRecentDashboardBrowserTab(minionsHome, now = Date.now()) {
86
86
  } catch { return false; }
87
87
  }
88
88
 
89
+ // Clear the browser-presence cache. Pre-restart beacons can outlive the
90
+ // browser window that produced them (closed Edge, locked-screen RDP session,
91
+ // stale heuristic timer) and falsely tell `restart` to skip the auto-open. We
92
+ // wipe it during restart so the post-restart probe only counts beacons that
93
+ // arrive AFTER the new dashboard is up — i.e. from a still-living tab.
94
+ function _clearDashboardBrowserState(minionsHome) {
95
+ try {
96
+ const fp = path.join(minionsHome, 'engine', 'dashboard-browser.json');
97
+ fs.writeFileSync(fp, JSON.stringify({ tabs: {}, updatedAt: new Date().toISOString() }));
98
+ } catch {}
99
+ }
100
+
101
+ async function _waitForBrowserReconnect(minionsHome, { afterMs, timeoutMs = 5000, pollMs = 500 } = {}) {
102
+ const start = Date.now();
103
+ while (Date.now() - start < timeoutMs) {
104
+ try {
105
+ const fp = path.join(minionsHome, 'engine', 'dashboard-browser.json');
106
+ const state = JSON.parse(fs.readFileSync(fp, 'utf8'));
107
+ const tabs = state && state.tabs ? Object.values(state.tabs) : [];
108
+ if (tabs.some(t => Number(t && t.lastSeen) > afterMs)) return true;
109
+ } catch { /* file may be temporarily missing during write */ }
110
+ await new Promise(r => setTimeout(r, pollMs));
111
+ }
112
+ return false;
113
+ }
114
+
115
+ function _openInBrowser(url) {
116
+ try {
117
+ if (process.platform === 'win32') execSync(`start "" "${url}"`, { stdio: 'ignore', windowsHide: true });
118
+ else if (process.platform === 'darwin') execSync(`open "${url}"`, { stdio: 'ignore' });
119
+ else execSync(`xdg-open "${url}"`, { stdio: 'ignore' });
120
+ } catch (e) {
121
+ console.log(` Could not auto-open browser: ${e.message}`);
122
+ console.log(` Please open ${url} manually.`);
123
+ }
124
+ }
125
+
89
126
  /**
90
127
  * Read the engine's recorded PID from engine/control.json. Returns null if
91
128
  * the file is missing/corrupt or the PID isn't a positive integer.
@@ -411,12 +448,12 @@ function init() {
411
448
  if (isUpgrade && skipStart) return;
412
449
 
413
450
  // Auto-start on fresh install; direct force-upgrade restarts automatically.
414
- // Probe before kill so we suppress browser auto-open only when a browser tab
415
- // was recently polling the dashboard. A bare/orphan dashboard process on the
416
- // port is not enough; cold starts should still open the UI.
417
451
  const dashWasUp = isPortListening(DASH_PORT);
418
- const suppressDashboardOpen = !forceOpen && dashWasUp && hasRecentDashboardBrowserTab(MINIONS_HOME);
452
+ const restartStartMs = Date.now();
419
453
  if (isUpgrade) {
454
+ // Clear stale beacons so the post-restart probe only counts tabs that
455
+ // reconnect to the NEW dashboard.
456
+ _clearDashboardBrowserState(MINIONS_HOME);
420
457
  try { execSync(`node "${path.join(MINIONS_HOME, 'engine.js')}" stop`, { stdio: 'ignore', cwd: MINIONS_HOME, timeout: 10000, windowsHide: true }); } catch {}
421
458
  // Free the dashboard port too — without this the new dashboard EADDRINUSE-dies
422
459
  // silently and the user keeps running stale code from the old dashboard process.
@@ -431,10 +468,28 @@ function init() {
431
468
  engineProc.unref();
432
469
  console.log(` Engine started (PID: ${engineProc.pid})`);
433
470
 
434
- const dashProc = spawnDashboard(suppressDashboardOpen);
471
+ // Always suppress dashboard's self-open — we decide here after the health
472
+ // check based on whether an existing browser tab actually reconnects.
473
+ const dashProc = spawnDashboard(true);
435
474
  console.log(` Dashboard started (PID: ${dashProc.pid})`);
436
475
  console.log(` Dashboard: http://localhost:${DASH_PORT}`);
437
476
 
477
+ void (async () => {
478
+ let shouldOpen = forceOpen || !dashWasUp;
479
+ if (!shouldOpen) {
480
+ // Hot upgrade: give an existing browser tab up to 5s to reconnect via
481
+ // its 4s auto-refresh poll. If nothing beacons, the tab is gone — open.
482
+ const reconnected = await _waitForBrowserReconnect(MINIONS_HOME, {
483
+ afterMs: restartStartMs, timeoutMs: 5000,
484
+ });
485
+ shouldOpen = !reconnected;
486
+ }
487
+ if (shouldOpen) {
488
+ console.log(` Opening dashboard in browser...`);
489
+ _openInBrowser(`http://localhost:${DASH_PORT}`);
490
+ }
491
+ })();
492
+
438
493
  // Next steps guidance
439
494
  console.log(`
440
495
  Next steps:
@@ -689,11 +744,11 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
689
744
  // `--cli` / `--model` flags forward to `engine.js start` so the runtime
690
745
  // fleet flips before the daemon spawns (P-6b3f9c2e AC: works on restart).
691
746
  ensureInstalled();
692
- // Probe before kill so we suppress browser auto-open only when a browser tab
693
- // was recently polling the dashboard. A bare/orphan dashboard process on the
694
- // port is not enough; cold starts should still open the UI.
695
747
  const dashWasUp = isPortListening(DASH_PORT);
696
- const suppressDashboardOpen = !forceOpen && dashWasUp && hasRecentDashboardBrowserTab(MINIONS_HOME);
748
+ // Mark the restart boundary so post-restart beacon timestamps are unambiguous,
749
+ // and clear stale beacons (browsers that closed without notifying).
750
+ const restartStartMs = Date.now();
751
+ _clearDashboardBrowserState(MINIONS_HOME);
697
752
  // Layered kill — each step is best-effort, layered so the next still runs if
698
753
  // one fails. Goal: the old engine is gone before we spawn a new one, even if
699
754
  // PowerShell is unavailable, the engine is hung, or its cmdline doesn't match.
@@ -712,7 +767,9 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
712
767
  });
713
768
  engineProc.unref();
714
769
  console.log(`\n Engine started (PID: ${engineProc.pid})`);
715
- const dashProc = spawnDashboard(suppressDashboardOpen);
770
+ // Always tell the dashboard to skip its own auto-open — we'll decide here
771
+ // after observing whether an existing browser tab reconnects.
772
+ const dashProc = spawnDashboard(true);
716
773
  console.log(` Dashboard started (PID: ${dashProc.pid})`);
717
774
  console.log(` Dashboard: http://localhost:${DASH_PORT}`);
718
775
  console.log(' Verifying restart health...');
@@ -725,7 +782,20 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
725
782
  console.error(formatRestartHealthError(result));
726
783
  process.exit(1);
727
784
  }
728
- console.log(` Restart verified: engine PID ${result.engine.pid}; dashboard healthy.\n`);
785
+ console.log(` Restart verified: engine PID ${result.engine.pid}; dashboard healthy.`);
786
+
787
+ let shouldOpen = forceOpen || !dashWasUp;
788
+ if (!shouldOpen) {
789
+ const reconnected = await _waitForBrowserReconnect(MINIONS_HOME, {
790
+ afterMs: restartStartMs, timeoutMs: 5000,
791
+ });
792
+ shouldOpen = !reconnected;
793
+ }
794
+ if (shouldOpen) {
795
+ console.log(` Opening dashboard in browser...`);
796
+ _openInBrowser(`http://localhost:${DASH_PORT}`);
797
+ }
798
+ console.log('');
729
799
  })().catch(err => {
730
800
  console.error(`\n ERROR: Restart verification failed: ${err.message}\n`);
731
801
  process.exit(1);
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "runtime": "copilot",
3
3
  "models": null,
4
- "cachedAt": "2026-05-09T20:04:56.707Z"
4
+ "cachedAt": "2026-05-09T22:56:52.587Z"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1825",
3
+ "version": "0.1.1826",
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"