[object Object]

← back to Ventura Corridor

feat(fleet): Mac1+Mac2 dashboard bridge on corridor homepage strip

4a0ab3d47b48c849e484188f2f5ca156a81b4240 · 2026-05-08 00:10:40 -0700 · SteveStudio2

Live status of both Mac dashboards now shows on the always-visible /
strip alongside news + ideas counters. Format:
  FLEET · Mac Studio 1 · qwen3:14b ⚓ · cpu 38% · ram 57%
       · Mac Studio 2 · gemma3:12b · cpu 12% · ram 41%

Each is clickable -> opens the corresponding 57 Bel Air / Eldorado
dashboard. Soft-fails per machine — if Mac1 is unreachable, Mac2 still
renders (and shows red OFFLINE link).

Routes:
  GET /api/fleet    {macs:[{label,cpu,ram_pct,ollama_models[],
                           installed_count,ollama_proc,power_mode,
                           ok,url|error}], ts}
                    Proxies http://100.94.103.98:9930/api/status (Mac1)
                    + http://localhost:9931/api/status (Mac2). 10s cache.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 4a0ab3d47b48c849e484188f2f5ca156a81b4240
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Fri May 8 00:10:40 2026 -0700

    feat(fleet): Mac1+Mac2 dashboard bridge on corridor homepage strip
    
    Live status of both Mac dashboards now shows on the always-visible /
    strip alongside news + ideas counters. Format:
      FLEET · Mac Studio 1 · qwen3:14b ⚓ · cpu 38% · ram 57%
           · Mac Studio 2 · gemma3:12b · cpu 12% · ram 41%
    
    Each is clickable -> opens the corresponding 57 Bel Air / Eldorado
    dashboard. Soft-fails per machine — if Mac1 is unreachable, Mac2 still
    renders (and shows red OFFLINE link).
    
    Routes:
      GET /api/fleet    {macs:[{label,cpu,ram_pct,ollama_models[],
                               installed_count,ollama_proc,power_mode,
                               ok,url|error}], ts}
                        Proxies http://100.94.103.98:9930/api/status (Mac1)
                        + http://localhost:9931/api/status (Mac2). 10s cache.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
 public/index.html   | 22 ++++++++++++++++++++++
 src/server/index.ts | 34 ++++++++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+)

diff --git a/public/index.html b/public/index.html
index 2f4b9d6..aceafd0 100644
--- a/public/index.html
+++ b/public/index.html
@@ -124,6 +124,12 @@
     <span><a href="http://127.0.0.1:9920/" target="_blank" rel="noopener" style="color:inherit;text-decoration:none;border-bottom:1px dotted var(--metal)">Ideas accepted</a></span><b id="b-ideas">—</b>
   </div>
   <div class="b-divider"></div>
+  <div class="b-fleet" id="b-fleet" style="font-family:var(--mono);font-size:10px;letter-spacing:.14em;color:var(--ink-mute);text-transform:uppercase;display:flex;gap:18px;align-items:center;flex-wrap:wrap">
+    <span style="color:var(--metal)">Fleet</span>
+    <span id="fleet-mac1">Mac1 ·</span>
+    <span id="fleet-mac2">Mac2 ·</span>
+  </div>
+  <div class="b-divider"></div>
   <div class="b-tier">
     <div class="t A"><b id="b-A">—</b><span>A · 80+</span></div>
     <div class="t B"><b id="b-B">—</b><span>B · 60+</span></div>
@@ -428,6 +434,22 @@ async function loadBuildStatus() {
     setNews('b-news-biz', c.news_biz);
     setNews('b-news-7d', c.news_fresh_7d);
     setNews('b-ideas', c.ideas_accepted);
+    // Fleet: pull Mac1/Mac2 state from /api/fleet (10s cache server-side)
+    try {
+      const fleet = await fetch('/api/fleet').then(r => r.json());
+      const fmtMac = (m, port) => {
+        if (!m || !m.ok) return `<a href="http://${port}" target="_blank" style="color:#c47b7b;border-bottom:1px dotted #c47b7b;text-decoration:none">${m?.label || port} · offline</a>`;
+        const model = m.ollama_models[0];
+        const modelStr = model ? `${model.name} ${model.pinned ? '⚓' : ''}` : 'idle';
+        const cpu = m.cpu.toFixed(0), ram = m.ram_pct.toFixed(0);
+        const dashUrl = port === '100.94.103.98:9930' ? 'http://100.94.103.98:9930' : 'http://localhost:9931';
+        return `<a href="${dashUrl}" target="_blank" style="color:inherit;text-decoration:none"><span style="color:var(--metal)">${m.label}</span> · <span style="color:var(--ink)">${escapeHtml ? escapeHtml(modelStr) : modelStr}</span> · cpu <b style="color:var(--metal-glow)">${cpu}%</b> · ram <b style="color:var(--metal-glow)">${ram}%</b></a>`;
+      };
+      const m1El = document.getElementById('fleet-mac1');
+      const m2El = document.getElementById('fleet-mac2');
+      if (m1El) m1El.innerHTML = fmtMac(fleet.macs[0], '100.94.103.98:9930');
+      if (m2El) m2El.innerHTML = fmtMac(fleet.macs[1], 'localhost:9931');
+    } catch(e) { /* fleet bridge unavailable */ }
     set('b-A', c.tier_a);
     set('b-B', c.tier_b);
     set('b-C', c.tier_c);
diff --git a/src/server/index.ts b/src/server/index.ts
index c1cd119..2f64a2f 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -5179,6 +5179,40 @@ app.get('/api/ideas/stats', async (_req, res) => {
   }
 });
 
+// Fleet status — proxy both Mac dashboards so corridor can show "what
+// is Mac1/Mac2 doing right now" inline. Soft-fails per machine, so a
+// dead dashboard doesn't 500 the whole page.
+app.get('/api/fleet', async (_req, res) => {
+  const fetchOne = async (url: string, label: string) => {
+    try {
+      const r = await fetch(url, { signal: AbortSignal.timeout(2500) });
+      if (!r.ok) return { ok: false, label, status: r.status };
+      const j: any = await r.json();
+      return {
+        ok: true, label,
+        cpu: j.cpu?.total ?? 0,
+        ram_pct: j.memory?.used_pct ?? 0,
+        ollama_models: (j.ollama?.models || []).map((m: any) => ({
+          name: m.name, vram_gb: +(((m.size_vram_bytes || 0) / (1024 ** 3)).toFixed(2)),
+          pinned: m.expires_at ? (new Date(m.expires_at).getTime() - Date.now()) > (180 * 86400 * 1000) : false
+        })),
+        installed_count: j.ollama?.installed_count || 0,
+        ollama_proc: (j.ollama_procs || [])[0] || null,
+        power_mode: j.power_mode || 'auto',
+        url: url.replace('/api/status', '')
+      };
+    } catch (e: any) {
+      return { ok: false, label, error: String(e?.message || e) };
+    }
+  };
+  const [mac1, mac2] = await Promise.all([
+    fetchOne('http://100.94.103.98:9930/api/status', 'Mac Studio 1'),
+    fetchOne('http://localhost:9931/api/status',     'Mac Studio 2')
+  ]);
+  res.set('Cache-Control', 'public, max-age=10');
+  res.json({ macs: [mac1, mac2], ts: Date.now() });
+});
+
 app.get('/api/ideas/recent', async (_req, res) => {
   try {
     const fs = await import('node:fs/promises');

← 4927f54 feat(news): "Hot only" toggle on /news.html filter bar  ·  back to Ventura Corridor  ·  feat(coverage): rejected-ideas drawer below the idea-loop fe 7d1fe85 →