← back to Re Flyer Aggregator
be-first PRIMARY feed: free CRE-news closed-deal monitor (TRD-LA/CommercialObserver/ConnectCRE/Registry-NorCal), sale+price+west filter + non-deal exclusion, dedupe, alert feed; wired into daily cron alongside EDGAR + DTT (TK-10708)
854c054fe271d2cae50a6cdbd28c4af803fbdedf · 2026-08-19 13:43:57 -0700 · Steve Abrams
Files touched
A data/crenews-seen.jsonA out/crenews-new-deals-2026-08-19.jsonA scripts/cre-news-monitor.mjsM scripts/dtt-cron.sh
Diff
commit 854c054fe271d2cae50a6cdbd28c4af803fbdedf
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Aug 19 13:43:57 2026 -0700
be-first PRIMARY feed: free CRE-news closed-deal monitor (TRD-LA/CommercialObserver/ConnectCRE/Registry-NorCal), sale+price+west filter + non-deal exclusion, dedupe, alert feed; wired into daily cron alongside EDGAR + DTT (TK-10708)
---
data/crenews-seen.json | 1 +
out/crenews-new-deals-2026-08-19.json | 57 +++++++++++++++++++++++++
scripts/cre-news-monitor.mjs | 80 +++++++++++++++++++++++++++++++++++
scripts/dtt-cron.sh | 11 ++---
4 files changed, 144 insertions(+), 5 deletions(-)
diff --git a/data/crenews-seen.json b/data/crenews-seen.json
new file mode 100644
index 0000000..9daf756
--- /dev/null
+++ b/data/crenews-seen.json
@@ -0,0 +1 @@
+["https://therealdeal.com/la/2026/08/19/pomerancs-brothers-miss-maturity-loan-in-special-servicing/","https://therealdeal.com/la/2026/08/19/kennedy-wilson-taps-jaime-lee/","https://therealdeal.com/la/2026/08/19/rexford-selling-industrial-portfolio-to-eqt-real-estate/","https://therealdeal.com/la/2026/08/19/century-city-office-overflows-to-beverly-hills/","https://therealdeal.com/la/2026/08/18/srs-arranges-sale-of-san-dimas-industrial-campus/","https://therealdeal.com/la/2026/08/18/newport-beach-approves-tpg-lincoln-housing-plans/","https://therealdeal.com/la/2026/08/18/pacific-urban-buys-miracle-mile-apartments/","https://therealdeal.com/la/2026/08/18/luxury-contracts-in-la-county-hit-lowest-week-since-january/","https://therealdeal.com/la/2026/08/18/irvine-co-usa-properties-start-tustin-affordable-housing/","https://therealdeal.com/la/2026/08/17/planning-commission-approves-apartments-on-la-cienega/","https://commercialobserver.com/2026/08/rexford-eqt-sale-california-industrial/","https://commercialobserver.com/2026/08/diamond-home-lease-20-west-36th-street/","https://commercialobserver.com/2026/08/zar-properties-sale-31-35-great-jones-street-noho/","https://commercialobserver.com/2026/08/mnrk-music-group-lease-rosen-443-park-avenue-south/","https://commercialobserver.com/2026/08/esrt-1359-broadway-sale-thor-equities/","https://commercialobserver.com/2026/08/finmarc-virginia-office-cim-group/","https://commercialobserver.com/2026/08/l3-capital-leases-bylt-basics-zara-williamsburg/","https://commercialobserver.com/2026/08/miami-design-district-snohetta-sweetbird-north/","https://commercialobserver.com/2026/08/burlington-lease-bay-ridge-retail-century-marketplace/","https://commercialobserver.com/2026/08/urban-standard-capital-27m-land-loan-vail-colo-complex/","https://commercialobserver.com/2026/08/new-york-485-x-apartment-limits/","https://commercialobserver.com/2026/08/investors-confidence-office-situsamc-survey/","https://commercialobserver.com/2026/08/santo-taco-sublease-dig-inn-232-bleecker-street/","https://commercialobserver.com/2026/08/goodland-pickleball-lease-williamsburg-rego-park-clubs/","https://commercialobserver.com/2026/08/national-resources-450m-sale-lincoln-property-company-saber-hightower-waterfall-asset-management/","https://commercialobserver.com/2026/08/reits-q2-hoya-capital-investors-report/","https://commercialobserver.com/2026/08/ocvibe-scott-frick-anaheim-housing-entertainment/","https://www.connectcre.com/stories/massachusetts-ag-orders-post-merger-sale-of-two-boston-apartment-towers/","https://www.connectcre.com/stories/goldie-initiative-draws-record-applicant-pool-for-45-scholar-spots/","https://www.connectcre.com/stories/entre-commercial-realty-negotiates-long-term-lease-at-truck-maintenance-facility/","https://www.connectcre.com/stories/net-leased-dutch-bros-property-in-indiana-changes-hands/","https://www.connectcre.com/stories/woodmore-towne-centre-in-md-signs-tenants-to-long-term-leases/","https://www.connectcre.com/stories/saragon-launches-to-serve-growing-demand-for-colocation/","https://www.connectcre.com/stories/lincoln-property-company-names-tricia-moore-as-executive-vice-president-asset-management/","https://www.connectcre.com/stories/potrero-hill-apartments-fetch-one-of-san-franciscos-highest-per-unit-prices/","https://www.connectcre.com/stories/jpmorgan-chase-opens-magnificent-mile-branch/","https://www.connectcre.com/stories/kidder-mathews-expanding-into-dallas/","https://news.theregistrysf.com/valiance-capital-canyon-partners-open-265-bed-the-valiant-student-housing-tower-in-berkeleys-southside/","https://news.theregistrysf.com/alameda-county-weighs-handing-supervisors-power-over-measure-d-easing-development-limits-on-livermores-protected-hills/","https://news.theregistrysf.com/93000-sqft-downtown-novato-center-lists-for-24-76mm-in-marin-county/","https://news.theregistrysf.com/valley-oak-partners-41000-sqft-agnews-historic-campus-hits-the-market-in-santa-clara/","https://news.theregistrysf.com/nimitz-groups-2mm-sqft-mare-island-shipyard-hits-the-market-in-vallejo/","https://news.theregistrysf.com/sack-capital-partners-lem-capital-acquire-122-unit-koda-apartments-in-fremont/","https://news.theregistrysf.com/sacramentos-rental-housing-bills-advance-toward-final-votes-with-real-stakes-for-northern-california-landlords/","https://news.theregistrysf.com/state-of-california-signs-28000-sqft-lease-at-del-paso-corporate-center-in-north-natomas/","https://news.theregistrysf.com/canopy-clinches-presidios-largest-lease-of-2026-with-full-40000-sqft-building-102-in-san-francisco/","https://news.theregistrysf.com/agility-robotics-lands-60000-sqft-physical-ai-hub-in-fremont-adds-nearly-200-jobs-ahead-of-nasdaq-debut/"]
\ No newline at end of file
diff --git a/out/crenews-new-deals-2026-08-19.json b/out/crenews-new-deals-2026-08-19.json
new file mode 100644
index 0000000..ee05f78
--- /dev/null
+++ b/out/crenews-new-deals-2026-08-19.json
@@ -0,0 +1,57 @@
+[
+ {
+ "feed": "TRD-LA",
+ "feed_region": "LA",
+ "title": "Rexford strikes $1.2B industrial portfolio deal with EQT amid strategic selloff",
+ "link": "https://therealdeal.com/la/2026/08/19/rexford-selling-industrial-portfolio-to-eqt-real-estate/",
+ "date": "Wed, 19 Aug 2026 19:00:00 +0000",
+ "price": 1200000000,
+ "label": "$1.2B",
+ "location": "Southern California",
+ "type": "sale"
+ },
+ {
+ "feed": "CommercialObserver",
+ "feed_region": "National",
+ "title": "Rexford to Sell $1.2B SoCal Portfolio to EQT",
+ "link": "https://commercialobserver.com/2026/08/rexford-eqt-sale-california-industrial/",
+ "date": "Wed, 19 Aug 2026 20:30:46 +0000",
+ "price": 1200000000,
+ "label": "$1.2B",
+ "location": "SoCal",
+ "type": "sale"
+ },
+ {
+ "feed": "ConnectCRE",
+ "feed_region": "National",
+ "title": "Potrero Hill Apartments Fetch One of San Francisco's Highest Per-Unit Prices",
+ "link": "https://www.connectcre.com/stories/potrero-hill-apartments-fetch-one-of-san-franciscos-highest-per-unit-prices/",
+ "date": "Wed, 19 Aug 2026 19:41:44 +0000",
+ "price": 132000000,
+ "label": "$132M",
+ "location": "San Francisco",
+ "type": "sale"
+ },
+ {
+ "feed": "TRD-LA",
+ "feed_region": "LA",
+ "title": "Pacific Urban buys Miracle Mile apartments from Sares-Regis for $70.5M",
+ "link": "https://therealdeal.com/la/2026/08/18/pacific-urban-buys-miracle-mile-apartments/",
+ "date": "Tue, 18 Aug 2026 20:30:00 +0000",
+ "price": 70500000,
+ "label": "$70.5M",
+ "location": "Los Angeles",
+ "type": "sale"
+ },
+ {
+ "feed": "TRD-LA",
+ "feed_region": "LA",
+ "title": "Institutional investor drops $41M on San Dimas industrial park",
+ "link": "https://therealdeal.com/la/2026/08/18/srs-arranges-sale-of-san-dimas-industrial-campus/",
+ "date": "Tue, 18 Aug 2026 22:45:00 +0000",
+ "price": 41000000,
+ "label": "$41M",
+ "location": null,
+ "type": "sale"
+ }
+]
\ No newline at end of file
diff --git a/scripts/cre-news-monitor.mjs b/scripts/cre-news-monitor.mjs
new file mode 100644
index 0000000..5997d52
--- /dev/null
+++ b/scripts/cre-news-monitor.mjs
@@ -0,0 +1,80 @@
+#!/usr/bin/env node
+// TK-10708 FREE CRE-news closed-deal monitor. Aggregates working CRE trade-press RSS
+// feeds (TRD-LA, Commercial Observer, Connect CRE, The Registry NorCal), filters to
+// SALE ("closed deal") items with a $ amount in the West footprint, extracts price +
+// property + location, dedupes by link -> the same-day "be first" alert feed. $0, no key.
+//
+// Not "before the news exists" -- it's first to have EVERY outlet's closed deals compiled
+// same-day, before a competitor assembles them. Usage: node scripts/cre-news-monitor.mjs [--dry]
+
+import { readFileSync, writeFileSync, existsSync } from 'node:fs';
+import { fileURLToPath } from 'node:url';
+import { dirname, join } from 'node:path';
+
+const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
+const args = process.argv.slice(2);
+const UA = 'Mozilla/5.0 (DW-research; steve@designerwallcoverings.com)';
+const SEEN = join(ROOT, 'data', 'crenews-seen.json');
+
+const FEEDS = [
+ { name: 'TRD-LA', region: 'LA', url: 'https://therealdeal.com/la/feed/' },
+ { name: 'CommercialObserver', region: 'National', url: 'https://commercialobserver.com/feed/' },
+ { name: 'ConnectCRE', region: 'National', url: 'https://www.connectcre.com/feed/' },
+ { name: 'Registry-NorCal', region: 'NorCal', url: 'https://news.theregistrysf.com/feed/' },
+];
+// West footprint terms (regional feeds like TRD-LA / Registry are inherently in-footprint).
+const WEST = /\b(Los Angeles|SoCal|Southern California|California|\bL\.?A\.?\b|San Diego|Orange County|Inland Empire|Ventura|San Francisco|Bay Area|Silicon Valley|San Jose|Oakland|Sacramento|NorCal|Seattle|Portland|Phoenix|Scottsdale|Las Vegas|Reno|Denver|Texas|Austin|Dallas|Houston|Nevada|Arizona|Oregon|Washington|Colorado)\b/i;
+const SALE = /\b(sells?|sold|buys?|acquires?|acquired|purchase[sd]?|pays?|trades?|snaps up|picks up|closes? on|nabs?|grabs?)\b/i;
+const LOAN = /\b(loan|refinanc|financing|mortgage|CMBS|debt)\b/i;
+
+const decode = s => (s || '').replace(/<!\[CDATA\[|\]\]>/g, '').replace(/&/g, '&').replace(/’|'|’/g, "'").replace(/–|—/g, '-').replace(/"/g, '"').replace(/&#\d+;/g, ' ').replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
+const tag = (block, t) => { const m = block.match(new RegExp(`<${t}[^>]*>([\\s\\S]*?)</${t}>`, 'i')); return m ? decode(m[1]) : ''; };
+
+function priceOf(text) {
+ const m = text.match(/\$\s?([\d]+(?:\.\d+)?)\s*(billion|million|B|M|K|thousand)\b/i);
+ if (!m) return { price: null, label: null };
+ const unit = m[2].toLowerCase();
+ const mult = unit.startsWith('b') ? 1e9 : unit.startsWith('m') ? 1e6 : (unit === 'k' || unit === 'thousand') ? 1e3 : 1;
+ return { price: Math.round(parseFloat(m[1]) * mult), label: `$${m[1]}${unit[0].toUpperCase()}` };
+}
+function locOf(text) { const m = text.match(WEST); return m ? m[0] : null; }
+function typeOf(text) { return SALE.test(text) ? 'sale' : LOAN.test(text) ? 'loan' : 'other'; }
+
+async function pull(feed) {
+ try {
+ const xml = await (await fetch(feed.url, { headers: { 'User-Agent': UA }, signal: AbortSignal.timeout(12000) })).text();
+ const items = xml.split(/<item[\s>]/).slice(1).map(b => '<item ' + b.split('</item>')[0] + '</item>');
+ return items.map(b => {
+ const title = tag(b, 'title');
+ let link = tag(b, 'link'); if (!link) link = (b.match(/<link[^>]*>([^<]+)</i) || [])[1] || tag(b, 'guid');
+ const desc = tag(b, 'description');
+ const date = tag(b, 'pubDate');
+ const text = `${title} ${desc}`;
+ return { feed: feed.name, feed_region: feed.region, title, link: (link || '').trim(), date, ...priceOf(text), location: locOf(text), type: typeOf(text) };
+ });
+ } catch (e) { console.error(` ${feed.name} feed error: ${e.message}`); return []; }
+}
+
+const all = (await Promise.all(FEEDS.map(pull))).flat().filter(x => x.title && x.link);
+// closed-deal filter: SALE type + a $ amount + in-footprint (regional feeds count as in-footprint)
+const inWest = x => x.location || x.feed_region === 'LA' || x.feed_region === 'NorCal';
+// exclude non-deal headlines that happen to carry a $ figure (personnel, rankings, op-eds).
+const NOT_DEAL = /\b(to head|to lead|hires?|names?|appoints?|joins?|promotes?|taps|steps down|resigns?|obituary|op-ed|opinion|ranking|list of|top \d+|named to|to oversee)\b/i;
+const dealItems = all.filter(x => x.type === 'sale' && x.price && inWest(x) && !NOT_DEAL.test(x.title));
+
+const seen = existsSync(SEEN) ? new Set(JSON.parse(readFileSync(SEEN, 'utf8'))) : new Set();
+const fresh = dealItems.filter(x => !seen.has(x.link));
+fresh.sort((a, b) => (b.price || 0) - (a.price || 0));
+
+console.log(`CRE-news scan: ${all.length} items across ${FEEDS.length} feeds -> ${dealItems.length} closed-deal (sale+price+west), ${fresh.length} NEW.`);
+for (const d of fresh.slice(0, 15)) console.log(` ${d.label.padStart(7)} ${(d.location||d.feed_region).padEnd(14)} ${d.title.slice(0,60)} [${d.feed}]`);
+
+if (!args.includes('--dry')) {
+ all.forEach(x => seen.add(x.link)); // mark everything seen so non-deal items don't re-scan
+ writeFileSync(SEEN, JSON.stringify([...seen], null, 0));
+ if (fresh.length) {
+ const stamp = new Date().toISOString().slice(0, 10);
+ writeFileSync(join(ROOT, 'out', `crenews-new-deals-${stamp}.json`), JSON.stringify(fresh, null, 2));
+ console.log(`-> out/crenews-new-deals-${stamp}.json (${fresh.length} deals) for the alert feed.`);
+ }
+}
diff --git a/scripts/dtt-cron.sh b/scripts/dtt-cron.sh
index c30d0b6..66e6c5f 100755
--- a/scripts/dtt-cron.sh
+++ b/scripts/dtt-cron.sh
@@ -1,12 +1,13 @@
#!/bin/bash
-# TK-10708 Daily "closed-deal" jobs (launchd com.steve.dtt-import).
-# Baked PATH so bare node/psql resolve under launchd's minimal env (bare-node lesson).
+# TK-10708 Daily "be first to a closed deal" jobs (launchd com.steve.dtt-import).
export PATH="/opt/homebrew/bin:/opt/homebrew/opt/postgresql@14/bin:/usr/bin:/bin:/usr/sbin:/sbin"
cd "$(dirname "$0")/.." || exit 1
LOG="data/dtt-cron.log"; mkdir -p data out
echo "=== $(date '+%Y-%m-%d %H:%M:%S') daily run ===" >> "$LOG"
-# 1) FREE be-first feed: SEC EDGAR CRE acquisition 8-Ks (same-week, $0)
-node scripts/edgar-cre-monitor.mjs --loc "Los Angeles" --days 14 >> "$LOG" 2>&1
-# 2) DTT sales import (free assessor source; frozen until a fresh feed is added)
+# 1) FREE broad same-day feed: CRE trade-press closed deals (private + public)
+node scripts/cre-news-monitor.mjs >> "$LOG" 2>&1
+# 2) FREE early-signal: SEC EDGAR CRE acquisition 8-Ks (biggest public deals)
+node scripts/edgar-cre-monitor.mjs --days 14 >> "$LOG" 2>&1
+# 3) DTT sales import (free assessor source; frozen until a fresh feed is added)
node scripts/ingest-la-sales.mjs >> "$LOG" 2>&1
echo "--- exit $? ---" >> "$LOG"
← dda8f01 EDGAR monitor: West-footprint sweep + filing enrichment (pro
·
back to Re Flyer Aggregator
·
morning email digest: build-digest.mjs + email-digest.sh sen 01cc8e0 →