agent-first-http 0.13.0

Give your AI agent its own private browser — so it reads the real page, past logins and bot walls, without ever touching yours.
Documentation
(() => {
  const NODE_LIMIT = 100;
  const SCAN_LIMIT = 2000;
  const SEMANTIC_SELECTOR = [
    'a[href]', 'button', 'input', 'textarea', 'select', 'summary', 'iframe',
    '[role]', '[tabindex]', '[contenteditable="true"]'
  ].join(',');

  const trim = (s, n = 160) => (s || '').replace(/\s+/g, ' ').trim().slice(0, n);
  const queryAll = (root, selector) => {
    try {
      return Array.from(root.querySelectorAll(selector));
    } catch (_) {
      return [];
    }
  };
  const queryOne = (root, selector) => {
    try {
      return root.querySelector(selector);
    } catch (_) {
      return null;
    }
  };
  const cssPath = (el) => {
    if (!el || !el.tagName) return null;
    if (el.id) return `#${CSS.escape(el.id)}`;
    const parts = [];
    let cur = el;
    while (cur && cur.nodeType === Node.ELEMENT_NODE && parts.length < 4) {
      let part = cur.tagName.toLowerCase();
      if (cur.classList && cur.classList.length) {
        part += '.' + Array.from(cur.classList).slice(0, 2).map(c => CSS.escape(c)).join('.');
      }
      const parent = cur.parentElement;
      if (parent) {
        const siblings = Array.from(parent.children).filter(x => x.tagName === cur.tagName);
        if (siblings.length > 1) part += `:nth-of-type(${siblings.indexOf(cur) + 1})`;
      }
      parts.unshift(part);
      cur = parent;
    }
    return parts.join(' > ');
  };
  const shadowHint = (chain, local) => {
    if (!local) return null;
    if (!chain.length) return local;
    return chain.concat([local]).join(' >> shadow >> ');
  };
  const selectorUnique = (root, chain, local) => {
    if (!local) return null;
    let scope = root;
    for (const hostSelector of chain) {
      if (queryAll(scope, hostSelector).length !== 1) return false;
      const host = queryOne(scope, hostSelector);
      if (!host || !host.shadowRoot) return false;
      scope = host.shadowRoot;
    }
    return queryAll(scope, local).length === 1;
  };
  const labelFor = (root, el) => {
    if (!el.id) return '';
    const label = queryOne(root, `label[for="${CSS.escape(el.id)}"]`);
    return label ? trim(label.innerText || label.textContent) : '';
  };
  const roleFor = (el) => {
    const explicit = el.getAttribute('role');
    if (explicit) return explicit;
    const tag = el.tagName.toLowerCase();
    if (tag === 'a') return 'link';
    if (tag === 'button') return 'button';
    if (tag === 'select') {
      const size = Number(el.getAttribute('size') || '0');
      if (el.multiple || size > 1) return 'listbox';
      return 'combobox';
    }
    if (tag === 'textarea') return 'textbox';
    if (tag === 'iframe') return 'iframe';
    if (tag === 'input') {
      const type = (el.getAttribute('type') || 'text').toLowerCase();
      if (type === 'checkbox') return 'checkbox';
      if (type === 'radio') return 'radio';
      if (type === 'submit' || type === 'button') return 'button';
      return 'textbox';
    }
    return tag;
  };
  const actionsFor = (el) => {
    const tag = el.tagName.toLowerCase();
    const type = (el.getAttribute('type') || '').toLowerCase();
    if (tag === 'input' && ['checkbox', 'radio'].includes(type)) return ['check', 'focus'];
    if (tag === 'input' || tag === 'textarea') return ['fill', 'focus'];
    if (tag === 'select') return ['select', 'focus'];
    if (tag === 'a' || tag === 'button' || el.getAttribute('role') === 'button') return ['click', 'focus'];
    if (el.hasAttribute('contenteditable')) return ['fill', 'focus'];
    return ['click'];
  };
  const isVisible = (el, rect, style) =>
    !!(rect.width || rect.height || el.getClientRects().length) &&
    style.visibility !== 'hidden' && style.display !== 'none';
  const isSemantic = (el) => {
    try {
      return el.matches(SEMANTIC_SELECTOR);
    } catch (_) {
      return false;
    }
  };
  const childDocument = (iframe) => {
    try {
      const doc = iframe.contentDocument;
      if (doc && doc.documentElement) return doc;
    } catch (_) {
      return null;
    }
    return null;
  };

  const state = {
    nodes: [],
    forms: [],
    frames: [{frame_id: 'main', url: location.href}],
    elementRefs: new WeakMap(),
    iframeIds: new WeakMap(),
    nextNode: 0,
    nextForm: 0,
    nextFrame: 0,
    scanned: 0,
    truncated: null,
    focused_ref: null
  };
  const truncate = (reason) => {
    if (!state.truncated) {
      state.truncated = {
        reason,
        node_limit: NODE_LIMIT,
        scan_limit: SCAN_LIMIT,
        scanned: state.scanned,
        emitted_nodes: state.nodes.length
      };
    }
  };
  const countScan = () => {
    if (state.scanned >= SCAN_LIMIT) {
      truncate('scan_limit_exceeded');
      return false;
    }
    state.scanned += 1;
    return true;
  };
  const canEmit = () => {
    if (state.nodes.length >= NODE_LIMIT) {
      truncate('node_limit_exceeded');
      return false;
    }
    return true;
  };
  const frameForIframe = (el) => {
    const existing = state.iframeIds.get(el);
    if (existing) return existing;
    const frame_id = `iframe-${state.nextFrame++}`;
    let url = el.src || '';
    const doc = childDocument(el);
    if (doc && doc.location && doc.location.href) {
      url = doc.location.href;
    }
    state.iframeIds.set(el, frame_id);
    state.frames.push({frame_id, url});
    return frame_id;
  };
  const emitNode = (el, ctx) => {
    if (state.elementRefs.has(el)) return state.elementRefs.get(el);
    if (!canEmit()) return null;
    const rect = el.getBoundingClientRect();
    const style = getComputedStyle(el);
    const ref = `obs-${state.nextNode++}`;
    const tag = el.tagName.toLowerCase();
    const localSelector = cssPath(el);
    const selectorHint = shadowHint(ctx.shadowChain, localSelector);
    const type = tag === 'input' ? (el.getAttribute('type') || 'text').toLowerCase() : null;
    const frameRef = tag === 'iframe' ? frameForIframe(el) : null;
    const focused = ctx.root.activeElement === el;
    if (focused) state.focused_ref = ref;
    const name = trim(el.getAttribute('aria-label')) || trim(el.getAttribute('alt')) ||
      trim(el.getAttribute('title')) || labelFor(ctx.queryRoot, el) || trim(el.innerText || el.textContent);
    state.elementRefs.set(el, ref);
    state.nodes.push({
      ref,
      frame_id: ctx.frameId,
      role: roleFor(el),
      name: name || null,
      text: trim(el.innerText || el.textContent) || null,
      visible: isVisible(el, rect, style),
      enabled: !(el.disabled || el.getAttribute('aria-disabled') === 'true'),
      bbox: {x: rect.x, y: rect.y, width: rect.width, height: rect.height},
      actions: actionsFor(el),
      href: el.href || null,
      src: el.src || null,
      frame_ref: frameRef,
      input_type: type,
      checked: (type === 'checkbox' || type === 'radio') ? !!el.checked : null,
      selected: tag === 'option' ? !!el.selected : null,
      focused,
      value_redacted: ('value' in el && String(el.value || '').length > 0) ? true : null,
      selector_hint: selectorHint,
      selector_hint_unique: selectorUnique(ctx.queryRoot, ctx.shadowChain, localSelector)
    });
    return ref;
  };
  const collectForms = (root) => {
    for (const form of queryAll(root, 'form')) {
      const field_refs = Array.from(form.elements || [])
        .map(el => state.elementRefs.get(el) || null)
        .filter(Boolean);
      state.forms.push({
        ref: `form-${state.nextForm++}`,
        action: form.action || null,
        field_refs
      });
    }
  };
  const walkRoot = (root, ctx) => {
    const ownerDocument = root.nodeType === Node.DOCUMENT_NODE ? root : root.ownerDocument;
    const start = root.nodeType === Node.DOCUMENT_NODE ? (root.body || root.documentElement) : root;
    if (!ownerDocument || !start || state.truncated) return;
    const walker = ownerDocument.createTreeWalker(start, NodeFilter.SHOW_ELEMENT);
    while (walker.nextNode()) {
      if (!countScan() || state.truncated) break;
      const el = walker.currentNode;
      const pointer = getComputedStyle(el).cursor === 'pointer';
      if (isSemantic(el) || pointer) emitNode(el, ctx);
      if (state.truncated) break;
      if (el.shadowRoot) {
        const hostSelector = cssPath(el);
        if (hostSelector) {
          walkRoot(el.shadowRoot, {
            frameId: ctx.frameId,
            root: el.shadowRoot,
            queryRoot: ctx.queryRoot,
            shadowChain: ctx.shadowChain.concat([hostSelector])
          });
        }
      }
      if (state.truncated) break;
      if (el.tagName && el.tagName.toLowerCase() === 'iframe') {
        const frameId = frameForIframe(el);
        const doc = childDocument(el);
        if (doc) {
          walkRoot(doc, {
            frameId,
            root: doc,
            queryRoot: doc,
            shadowChain: []
          });
        }
      }
      if (state.truncated) break;
    }
    collectForms(root);
  };

  walkRoot(document, {
    frameId: 'main',
    root: document,
    queryRoot: document,
    shadowChain: []
  });
  return JSON.stringify({
    nodes: state.nodes,
    forms: state.forms,
    frames: state.frames,
    focused_ref: state.focused_ref,
    truncated: state.truncated
  });
})()