← back to Delivery Address Fix

scripts/lib.js

107 lines

// Shared helpers for delivery-address scripts. Zero deps beyond playwright.
const fs = require('fs');
const path = require('path');

const PLATFORMS = {
  ubereats: {
    home: 'https://www.ubereats.com/',
    login: 'https://auth.uber.com/v2/?next_url=https%3A%2F%2Fwww.ubereats.com%2Flogin-redirect',
    // BOTH domains: ubereats.com is the logged-in surface, auth.uber.com the
    // login flow — and 'uber.com' is NOT a substring of 'ubereats.com'
    // (caught live by 5x sweep 2, 2026-08-02).
    match: ['ubereats.com', 'auth.uber.com'],
    emailField: '#PHONE_NUMBER_or_EMAIL_ADDRESS',
    emailSubmit: '#forward-button',
  },
  doordash: {
    home: 'https://www.doordash.com/',
    login: 'https://identity.doordash.com/auth',
    match: 'doordash.com',
    emailField: 'input[type="email"]',
    emailSubmit: 'button[type="submit"]',
  },
  instacart: {
    home: 'https://www.instacart.com/',
    login: 'https://www.instacart.com/login',
    match: 'instacart.com',
    emailField: 'input[type="email"]',
    emailSubmit: 'button[type="submit"]',
  },
};

// playwright isn't vendored (no node_modules in this repo) — fall back to the
// machine's global npm roots when a plain require can't resolve it.
function requirePlaywright() {
  try { return require('playwright'); } catch (e) {
    const roots = [
      path.join(process.env.HOME || '', '.npm-global', 'lib', 'node_modules'),
      '/opt/homebrew/lib/node_modules',
      '/usr/local/lib/node_modules',
    ];
    for (const r of roots) {
      const p = path.join(r, 'playwright');
      if (fs.existsSync(p)) return require(p);
    }
    console.error('playwright not resolvable: npm i -g playwright (or npm i playwright in this repo)');
    process.exit(1);
  }
}

function parseArgs(argv) {
  const args = {};
  for (let i = 2; i < argv.length; i++) {
    const m = argv[i].match(/^--([a-z-]+)$/);
    if (m) { args[m[1]] = argv[i + 1]; i++; }
  }
  return args;
}

function loadConfig() {
  const p = path.join(__dirname, '..', 'config.json');
  return fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, 'utf8')) : {};
}

function platform(name) {
  const p = PLATFORMS[name];
  if (!p) { console.error(`Unknown platform "${name}". One of: ${Object.keys(PLATFORMS).join(', ')}`); process.exit(2); }
  return p;
}

function sessionDir(cfg, args) {
  const dir = args['session-dir'] || cfg.session_dir ||
    path.join(__dirname, '..', 'sessions', `${(args.user || cfg.user_email || 'user').replace(/[@.]/g, '_')}-${args.platform || 'session'}`);
  fs.mkdirSync(dir, { recursive: true });
  return dir;
}

// HARD RULE: select the tab by URL substring, never pages()[0] — other
// automations share the browser (Tesla tab hijacked slot 0 on 2026-08-02).
async function findTab(port, match) {
  // match: url substring, comma-separated substrings, or array of substrings
  const candidates = (Array.isArray(match) ? match : String(match).split(','))
    .map(s => s.trim()).filter(Boolean);
  const { chromium } = requirePlaywright();
  const browser = await chromium.connectOverCDP(`http://127.0.0.1:${port}`);
  const pages = browser.contexts()[0].pages();
  const page = pages.find(p => candidates.some(c => p.url().includes(c)));
  if (!page) {
    console.error(`NO TAB matching "${candidates.join(' | ')}". Open tabs: ${pages.map(p => p.url()).join(' | ')}`);
    process.exit(2);
  }
  return { browser, page };
}

async function dumpState(page, dir, label) {
  const shot = path.join(dir, `${label}-${Date.now()}.png`);
  await page.screenshot({ path: shot }).catch(() => {});
  console.log('URL:', page.url());
  console.log('SHOT:', shot);
  console.log(await page.evaluate(() => document.body.innerText.slice(0, 1000)));
  const inputs = await page.evaluate(() =>
    Array.from(document.querySelectorAll('input')).map(i =>
      ({ t: i.type, ph: i.placeholder, val: i.value ? '<set>' : '', vis: !!i.offsetParent })));
  console.log('INPUTS:', JSON.stringify(inputs.filter(i => i.vis)));
}

module.exports = { PLATFORMS, parseArgs, loadConfig, platform, sessionDir, findTab, dumpState, requirePlaywright };