← back to Beverlyhillsvideos
poster: add read-only BHV token-health watchdog (Cody FIX-FIRST #2)
e7027024d3ba43c344d5d65eca6268e5e184ec94 · 2026-08-06 16:25:38 -0700 · Steve Abrams
Files touched
A social/poster/token-health.mjs
Diff
commit e7027024d3ba43c344d5d65eca6268e5e184ec94
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Aug 6 16:25:38 2026 -0700
poster: add read-only BHV token-health watchdog (Cody FIX-FIRST #2)
---
social/poster/token-health.mjs | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/social/poster/token-health.mjs b/social/poster/token-health.mjs
new file mode 100644
index 0000000..3728f55
--- /dev/null
+++ b/social/poster/token-health.mjs
@@ -0,0 +1,36 @@
+#!/usr/bin/env node
+// token-health.mjs — READ-ONLY check that the BHV Graph API token is alive + not near
+// expiry. The 6-hour poster fails SILENTLY if the token dies (launchd doesn't alert),
+// so this is the missing watchdog (Cody FIX-FIRST #2). Exit 0 healthy, 1 dead/expiring.
+// Wire to a launchd job + George email on non-zero (gated install — see pending-approval).
+import fs from 'node:fs';
+import os from 'node:os';
+import path from 'node:path';
+
+const env = fs.readFileSync(path.join(os.homedir(), 'Projects/secrets-manager/.env'), 'utf8');
+const g = (k) => { let v = null; for (const l of env.split('\n')) { const m = l.match(/^([A-Z0-9_]+)=(.*)$/); if (m && m[1] === k) v = m[2]; } return v; };
+const tok = g('BHV_IG_ACCESS_TOKEN'), ig = g('BHV_IG_USER_ID'), ver = g('BHV_IG_GRAPH_VERSION') || 'v21.0';
+
+const WARN_DAYS = 10; // warn if expiry within this window
+
+(async () => {
+ if (!tok || !ig) { console.log('DEAD: missing BHV_IG token/user id'); process.exit(1); }
+ try {
+ // 1) can the token still read the IG account?
+ const r = await fetch(`https://graph.facebook.com/${ver}/${ig}?fields=username&access_token=${tok}`);
+ const j = await r.json();
+ if (j.error) { console.log(`DEAD: ${JSON.stringify(j.error).slice(0, 160)}`); process.exit(1); }
+ // 2) how long until it expires?
+ const d = await (await fetch(`https://graph.facebook.com/${ver}/debug_token?input_token=${tok}&access_token=${tok}`)).json();
+ const exp = d?.data?.expires_at;
+ if (exp && exp > 0) {
+ const days = Math.round((exp * 1000 - Date.now()) / 864e5);
+ if (days <= 0) { console.log('DEAD: token expired'); process.exit(1); }
+ if (days <= WARN_DAYS) { console.log(`EXPIRING: @${j.username} token expires in ${days}d`); process.exit(1); }
+ console.log(`HEALTHY: @${j.username} token OK, expires in ${days}d`);
+ } else {
+ console.log(`HEALTHY: @${j.username} token OK (no expiry / long-lived)`);
+ }
+ process.exit(0);
+ } catch (e) { console.log(`DEAD: ${e.message}`); process.exit(1); }
+})();
← 6e16b66 poster: rankByLRU ignores dry-run entries (Cody FIX-FIRST)
·
back to Beverlyhillsvideos
·
auto-data-snapshot: 2026-08-06T16:33:58 (2 data files) — soc e150ee7 →