[object Object]

← back to Allnewsdaily

Add prod static-wire mode: WIRE_STATIC=1 serves pre-built data/wire.json (no Ollama on prod); scripts/build-wire.js + scripts/push-wire.sh keep it fresh from this Mac's Ollama

98297054f98b43e080b5b28dd36fac247c09d656 · 2026-09-09 11:28:58 -0700 · Steve Abrams

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177CKXFpA1rVH3Sk24fgRZ9

Files touched

Diff

commit 98297054f98b43e080b5b28dd36fac247c09d656
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 9 11:28:58 2026 -0700

    Add prod static-wire mode: WIRE_STATIC=1 serves pre-built data/wire.json (no Ollama on prod); scripts/build-wire.js + scripts/push-wire.sh keep it fresh from this Mac's Ollama
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_0177CKXFpA1rVH3Sk24fgRZ9
---
 .gitignore            |  1 +
 lib/aggregate.js      | 24 +++++++++++++++++++++++-
 scripts/build-wire.js | 18 ++++++++++++++++++
 scripts/push-wire.sh  | 15 +++++++++++++++
 server.js             | 20 +++++++++++++++-----
 5 files changed, 72 insertions(+), 6 deletions(-)

diff --git a/.gitignore b/.gitignore
index 78cd896..4f9b69c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@ build/
 data/live-status.json
 
 data/paraphrase-cache.json
+data/wire.json
diff --git a/lib/aggregate.js b/lib/aggregate.js
index 6fcf3b9..c37afcb 100644
--- a/lib/aggregate.js
+++ b/lib/aggregate.js
@@ -89,4 +89,26 @@ async function rebuild() {
 function getWire() { return WIRE; }
 function meta() { return loadFeeds(); }
 
-module.exports = { rebuild, getWire, meta };
+const WIRE_JSON = path.join(__dirname, '..', 'data', 'wire.json');
+
+// Persist the current wire to data/wire.json (used by scripts/build-wire.js).
+function writeWireFile() {
+  const w = getWire();
+  const out = { columns: w.columns, splash: w.splash, updatedAt: w.updatedAt, sources: w.sources, sourcesOk: w.sourcesOk };
+  fs.writeFileSync(WIRE_JSON, JSON.stringify(out));
+  return out;
+}
+
+// Load a pre-built wire snapshot into memory (prod static mode — no Ollama needed).
+function loadStaticWire() {
+  try {
+    const w = JSON.parse(fs.readFileSync(WIRE_JSON, 'utf8'));
+    if (w && Array.isArray(w.columns)) {
+      WIRE = { ...w, building: false };
+      return true;
+    }
+  } catch (e) { /* file missing/invalid — leave WIRE as-is */ }
+  return false;
+}
+
+module.exports = { rebuild, getWire, meta, writeWireFile, loadStaticWire, WIRE_JSON };
diff --git a/scripts/build-wire.js b/scripts/build-wire.js
new file mode 100755
index 0000000..67f57a4
--- /dev/null
+++ b/scripts/build-wire.js
@@ -0,0 +1,18 @@
+#!/usr/bin/env node
+'use strict';
+// Build the wire (fetch free feeds + rewrite headlines via local Ollama) and write data/wire.json.
+// Runs on a machine that HAS Ollama (this Mac). The prod server (WIRE_STATIC=1) just serves the file.
+const { rebuild, writeWireFile, WIRE_JSON } = require('../lib/aggregate');
+
+(async () => {
+  const t0 = Date.now();
+  const w = await rebuild();
+  const stories = (w.columns || []).reduce((n, c) => n + c.items.length, 0);
+  if (stories === 0) {
+    console.error(`[build-wire] refusing to write empty wire (sources ${w.sourcesOk}/${w.sources}) — keeping previous snapshot`);
+    process.exit(1);
+  }
+  const out = writeWireFile();
+  console.log(`[build-wire] wrote ${WIRE_JSON} — ${stories} stories, ${w.sourcesOk}/${w.sources} sources, ${Date.now() - t0}ms`);
+  process.exit(0);
+})().catch((e) => { console.error('[build-wire] failed', e.message); process.exit(1); });
diff --git a/scripts/push-wire.sh b/scripts/push-wire.sh
new file mode 100755
index 0000000..1fa20ed
--- /dev/null
+++ b/scripts/push-wire.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+# Rebuild the wire locally (needs Ollama) then rsync ONLY data/wire.json to prod.
+# Runs from a launchd job on this Mac every ~10 min. Reversible: unload the job to stop.
+set -euo pipefail
+cd "$(dirname "$0")/.."
+PROD="${PROD_HOST:-root@45.61.58.125}"
+REMOTE="/root/Projects/allnewsdaily/data/wire.json"
+
+node scripts/build-wire.js || { echo "[push-wire] build failed, not pushing"; exit 1; }
+
+# only push a non-empty, valid file
+node -e "const w=require('./data/wire.json'); if(!w.columns||!w.columns.length){process.exit(2)}" || { echo "[push-wire] wire.json invalid, not pushing"; exit 2; }
+
+rsync -az -e "ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new" data/wire.json "$PROD:$REMOTE"
+echo "[push-wire] pushed $(date -u +%FT%TZ) → $PROD:$REMOTE"
diff --git a/server.js b/server.js
index f988975..b3efbb7 100644
--- a/server.js
+++ b/server.js
@@ -3,7 +3,10 @@ const fs = require('fs');
 const path = require('path');
 const { spawn } = require('child_process');
 const { GUIDES } = require('./content/guides');
-const { rebuild, getWire, meta } = require('./lib/aggregate');
+const { rebuild, getWire, meta, loadStaticWire } = require('./lib/aggregate');
+// On prod (no Ollama) set WIRE_STATIC=1: serve the pre-built data/wire.json, refreshed by an
+// external pusher (scripts/build-wire.js on a machine that HAS Ollama). Never fetches/rewrites here.
+const WIRE_STATIC = process.env.WIRE_STATIC === '1';
 
 const app = express();
 const PORT = process.env.PORT || 9788;
@@ -320,8 +323,15 @@ app.listen(PORT, () => {
   console.log(`outlets loaded: ${loadOutlets().length}`);
   runChecker();
   setInterval(runChecker, POLL_INTERVAL_MS);
-  // Build the wire (fetch feeds + rewrite headlines) now, then refresh on an interval.
-  rebuild().then((w) => console.log(`[wire] first build: ${w.sourcesOk}/${w.sources} sources, ${(w.columns || []).reduce((n, c) => n + c.items.length, 0)} stories`))
-           .catch((e) => console.error('[wire] first build error', e.message));
-  setInterval(() => { rebuild().catch(() => {}); }, WIRE_REFRESH_MS);
+  if (WIRE_STATIC) {
+    // Prod: serve the pre-built snapshot; re-read the file as the pusher refreshes it.
+    const ok = loadStaticWire();
+    console.log(`[wire] static mode — loaded ${ok ? (getWire().columns || []).reduce((n, c) => n + c.items.length, 0) + ' stories' : 'NO snapshot yet'}`);
+    setInterval(() => { loadStaticWire(); }, 60000);
+  } else {
+    // Dev / builder host (has Ollama): fetch feeds + rewrite headlines, refresh on interval.
+    rebuild().then((w) => console.log(`[wire] first build: ${w.sourcesOk}/${w.sources} sources, ${(w.columns || []).reduce((n, c) => n + c.items.length, 0)} stories`))
+             .catch((e) => console.error('[wire] first build error', e.message));
+    setInterval(() => { rebuild().catch(() => {}); }, WIRE_REFRESH_MS);
+  }
 });

← 4563468 Drudge-style front page: live free-RSS wire rewritten into o  ·  back to Allnewsdaily  ·  wire-push: robust node resolution for launchd; add wire-push f1da61c →