← back to Sublease Agentabrams

crawl/crexi-enrich-recon.js

43 lines

'use strict';
// Recon (~$0.30): find Crexi's broker-profile endpoint + whether contact (phone/email/site) is
// exposed. Loads a broker profile, captures api.crexi.com XHRs, and probes candidate endpoints.
const fs = require('fs');
const path = require('path');
const { chromium } = require('playwright-core');
const Browserbase = require('@browserbasehq/sdk').default || require('@browserbasehq/sdk');
const envTxt = fs.readFileSync(path.join(process.env.HOME, '.claude/skills/browserbase/.env'), 'utf8');
const API_KEY = (envTxt.match(/BROWSERBASE_API_KEY=(.*)/) || [])[1]?.trim();
const PROJECT = (envTxt.match(/BROWSERBASE_PROJECT_ID=(.*)/) || [])[1]?.trim();
const ID = process.argv[2] || '806';

(async () => {
  const t0 = Date.now();
  const bb = new Browserbase({ apiKey: API_KEY });
  const session = await bb.sessions.create({ projectId: PROJECT, browserSettings: { solveCaptchas: true, viewport: { width: 1440, height: 1000 } } });
  const browser = await chromium.connectOverCDP(session.connectUrl);
  const ctx = browser.contexts()[0]; const page = ctx.pages()[0] || await ctx.newPage();
  page.setDefaultTimeout(45000);
  const seen = new Set();
  page.on('response', async (res) => { const u = res.url(); if (u.includes('api.crexi.com') && /broker|user|contact|profile/i.test(u)) seen.add(u.split('?')[0]); });
  // try to reach a broker profile via search page first (to get a valid URL pattern)
  for (const url of [`https://www.crexi.com/brokers/${ID}`, `https://www.crexi.com/broker/${ID}`]) {
    try { await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 35000 }); await page.waitForTimeout(5000); await page.mouse.wheel(0, 2000).catch(()=>{}); await page.waitForTimeout(2500); } catch {}
  }
  // probe candidate API endpoints in-page (authorized via cookies)
  const probe = await page.evaluate(async (id) => {
    const cands = [`/brokers/${id}`, `/users/${id}`, `/brokers/${id}/profile`, `/brokers/${id}/contact-info`, `/broker-profiles/${id}`, `/users/${id}/profile`];
    const out = [];
    for (const c of cands) {
      try { const r = await fetch('https://api.crexi.com' + c, { headers: {} });
        const body = r.ok ? await r.text() : ''; out.push({ c, status: r.status, hasEmail: /email/i.test(body), hasPhone: /phone/i.test(body), snip: body.slice(0, 260) }); }
      catch (e) { out.push({ c, err: String(e).slice(0, 40) }); }
    }
    return out;
  }, ID);
  await browser.close().catch(()=>{});
  console.log('cost: $' + ((Date.now()-t0)/60000*0.15).toFixed(2));
  console.log('profile XHRs seen:', [...seen].join(', ') || 'none');
  console.log('=== endpoint probes ===');
  probe.forEach(p => console.log(`[${p.status||p.err}] ${p.c}  email:${p.hasEmail} phone:${p.hasPhone}\n   ${(p.snip||'').replace(/\s+/g,' ').slice(0,200)}`));
})();