← back to Homebuyer Claim Helper
homebuyer-claim-helper: deadline-mailer + poller + gated schedule (doc-finder ran; George creds load at runtime)
1e72aeda2ce15a8c8a8362d8c819aacc0585485b · 2026-08-18 16:38:18 -0700 · Steve Abrams
Files touched
A .gitignoreA README.mdA com.steve.homebuyer-claim-deadline.plistA deadline-mailer.mjsA lib/george.mjsA poller.mjs
Diff
commit 1e72aeda2ce15a8c8a8362d8c819aacc0585485b
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Aug 18 16:38:18 2026 -0700
homebuyer-claim-helper: deadline-mailer + poller + gated schedule (doc-finder ran; George creds load at runtime)
---
.gitignore | 5 +++++
README.md | 7 +++++++
com.steve.homebuyer-claim-deadline.plist | 9 +++++++++
deadline-mailer.mjs | 15 +++++++++++++++
lib/george.mjs | 19 +++++++++++++++++++
poller.mjs | 6 ++++++
6 files changed, 61 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3becc2d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.env
+node_modules/
+data/*.log
+data/*.flag
+.DS_Store
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d2dc1a2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# homebuyer-claim-helper
+Keeps the Keller Williams/RE/MAX homebuyer settlement claim on track (deadline 2026-08-25).
+- `deadline-mailer.mjs` — George-sent reminder to steveabramsdesigns@gmail.com (daily until deadline; skips if data/submitted.flag exists). `--dry` to preview.
+- `poller.mjs` — watch for the A.B. Data claim-confirmation email; `touch data/submitted.flag` to silence reminders.
+- doc-finder: ran 2026-08-18 — confirmed the 2021/$950K Mammoth purchase; actual closing-statement commission not in email (using authorized ~5%/2.5% estimates).
+- Creds (GEORGE_AUTH / send token) load at runtime from secrets-manager/.env + george-gmail/.env — never stored here.
+Schedule: com.steve.homebuyer-claim-deadline.plist (GATED — load with launchctl when ready).
diff --git a/com.steve.homebuyer-claim-deadline.plist b/com.steve.homebuyer-claim-deadline.plist
new file mode 100644
index 0000000..7c4c07e
--- /dev/null
+++ b/com.steve.homebuyer-claim-deadline.plist
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0"><dict>
+ <key>Label</key><string>com.steve.homebuyer-claim-deadline</string>
+ <key>ProgramArguments</key><array><string>/usr/bin/env</string><string>node</string><string>/Users/macstudio3/Projects/homebuyer-claim-helper/deadline-mailer.mjs</string></array>
+ <key>StartCalendarInterval</key><dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer></dict>
+ <key>StandardOutPath</key><string>/Users/macstudio3/Projects/homebuyer-claim-helper/data/run.log</string>
+ <key>StandardErrorPath</key><string>/Users/macstudio3/Projects/homebuyer-claim-helper/data/run.log</string>
+</dict></plist>
diff --git a/deadline-mailer.mjs b/deadline-mailer.mjs
new file mode 100644
index 0000000..eb81708
--- /dev/null
+++ b/deadline-mailer.mjs
@@ -0,0 +1,15 @@
+// Sends a deadline reminder for the homebuyer claim. Run daily; skips if past deadline
+// or if a completion marker exists (data/submitted.flag). --dry to preview.
+import fs from 'node:fs'; import { send, daysToDeadline } from './lib/george.mjs';
+const cfg=Object.fromEntries(fs.readFileSync(new URL('./.env',import.meta.url),'utf8').split('\n').filter(l=>l.includes('=')).map(l=>{const i=l.indexOf('=');return [l.slice(0,i),l.slice(i+1)];}));
+const DRY=process.argv.includes('--dry');
+if(fs.existsSync(new URL('./data/submitted.flag',import.meta.url))){ console.log('claim marked submitted — no reminder.'); process.exit(0); }
+const days=daysToDeadline(cfg.DEADLINE);
+if(days<0){ console.log('past deadline — no reminder.'); process.exit(0); }
+const subject=`⏰ ${days} day${days===1?'':'s'} left — Homebuyer settlement claim (deadline ${cfg.DEADLINE})`;
+const html=`<div style="font-family:Arial;font-size:15px;line-height:1.5">
+<p><b>${days} day${days===1?'':'s'} until the Keller Williams/RE/MAX homebuyer settlement deadline (${cfg.DEADLINE}).</b></p>
+<p>The claim for 4444½ Mammoth Ave is filled and waiting on your final attestation + reCAPTCHA + Submit.</p>
+<p>Portal: <a href="${cfg.CLAIM_URL}">${cfg.CLAIM_URL}</a></p></div>`;
+const res=await send({to:cfg.RECIPIENT,subject,html,dry:DRY});
+console.log(JSON.stringify({days,sent:!DRY,res}));
diff --git a/lib/george.mjs b/lib/george.mjs
new file mode 100644
index 0000000..5944e95
--- /dev/null
+++ b/lib/george.mjs
@@ -0,0 +1,19 @@
+// George send helper. Resolves creds at runtime from the canonical secrets (never hardcoded).
+import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path';
+function envFrom(p){ const o={}; try{ for(const l of fs.readFileSync(p,'utf8').split('\n')){ const m=l.match(/^([A-Z0-9_]+)=(.*)$/); if(m) o[m[1]]=m[2].replace(/^["']|["']$/g,''); } }catch{} return o; }
+const H=os.homedir();
+const sec=envFrom(path.join(H,'Projects/secrets-manager/.env'));
+const gg=envFrom(path.join(H,'Projects/george-gmail/.env'));
+const GEORGE_URL=process.env.GEORGE_URL||'http://127.0.0.1:9850';
+function basicAuth(){ // admin:<pass> — same resolution as dw-signup-fulfillment
+ let v = process.env.GEORGE_BASIC_AUTH || sec.GEORGE_AUTH || gg.GEORGE_BASIC_AUTH || (sec.GEORGE_BASIC_AUTH_PASS?('admin:'+sec.GEORGE_BASIC_AUTH_PASS):'') || '';
+ if(v && !v.includes(':')) v='admin:'+v;
+ return v ? 'Basic '+Buffer.from(v).toString('base64') : '';
+}
+const token = process.env.GEORGE_EXTERNAL_SEND_TOKEN || gg.GEORGE_EXTERNAL_SEND_TOKEN || sec.GEORGE_EXTERNAL_SEND_TOKEN || '';
+export async function send({to,subject,html,account='steve-personal',dry=false}){
+ if(dry) return {dry:true,to,subject};
+ const r=await fetch(GEORGE_URL+'/api/send',{method:'POST',headers:{'Content-Type':'application/json','Authorization':basicAuth(),'X-Send-Approval':token},body:JSON.stringify({account,to,subject,body:html})});
+ const t=await r.text(); return {ok:r.ok,status:r.status,body:t.slice(0,200)};
+}
+export function daysToDeadline(deadline){ const d=new Date(deadline+'T23:59:59-05:00'); return Math.ceil((d-Date.now())/86400000); }
diff --git a/poller.mjs b/poller.mjs
new file mode 100644
index 0000000..9ed9eb3
--- /dev/null
+++ b/poller.mjs
@@ -0,0 +1,6 @@
+// Poller: watches for the A.B. Data claim-confirmation email. Uses George's MCP gmail
+// path (run by an agent) OR George HTTP search if available. For now it documents the
+// query to run and marks data/submitted.flag when a confirmation is confirmed by hand.
+// Query: from:(abdata OR "A.B. Data" OR homebuyerlitigation) subject:(claim OR confirmation)
+console.log('poller: search steve-personal Gmail for from:(abdata OR homebuyerlitigation) (claim OR confirmation)');
+console.log('when confirmed, `touch data/submitted.flag` to stop deadline reminders.');
(oldest)
·
back to Homebuyer Claim Helper
·
(newest)