{"version":3,"sources":["../../src/skills/browser.ts"],"sourcesContent":["// @agentx/sdk — Browser Control Skill\n// Text-based DOM manipulation for browser-side agent execution.\n// No external dependencies — uses native browser APIs only.\n//\n// Usage:\n//   import { executeBrowserAction, extractAccessibleDOM, sleep } from '@agentxv2/sdk/skills'\n//   const result = executeBrowserAction({ type: 'click', selector: '#submit' })\n//   await sleep(300)  // optional pacing between actions\n\nexport interface BrowserAction {\n  type:\n    | 'click' | 'type' | 'extract' | 'scroll' | 'navigate'\n    // v0.9.0 additions:\n    | 'hover' | 'press' | 'select' | 'back' | 'forward' | 'getInfo'\n  /** CSS selector or text content of the target element */\n  selector?: string\n  /** Value for 'type' / 'select' actions, URL for 'navigate', key for 'press', px for 'scroll' */\n  value?: string\n  /** Natural language description (fallback when no selector) */\n  description?: string\n}\n\nexport interface BrowserActionResult {\n  success: boolean\n  result?: string\n  error?: string\n}\n\n/** Wait for a delay (async pacing helper for agent loops). */\nexport function sleep(ms: number): Promise<void> {\n  return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n/**\n * Extract a text-based DOM representation from the current page.\n * Returns a simplified DOM tree with only interactive and text-containing elements.\n */\nexport function extractAccessibleDOM(): string {\n  if (typeof document === 'undefined') {\n    return 'Browser DOM not available (not running in browser)'\n  }\n\n  const interactiveTags = new Set([\n    'A', 'BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'FORM',\n    'H1', 'H2', 'H3', 'H4', 'H5', 'H6',\n    'P', 'SPAN', 'DIV', 'LI', 'TD', 'TH', 'LABEL',\n  ])\n\n  const elements: string[] = []\n  const walker = document.createTreeWalker(\n    document.body,\n    NodeFilter.SHOW_ELEMENT,\n    {\n      acceptNode: (node) => {\n        if (!(node instanceof HTMLElement)) return NodeFilter.FILTER_REJECT\n        if (!interactiveTags.has(node.tagName)) return NodeFilter.FILTER_REJECT\n        // Skip hidden elements\n        if (node.offsetParent === null && node.tagName !== 'A') return NodeFilter.FILTER_REJECT\n        return NodeFilter.FILTER_ACCEPT\n      },\n    }\n  )\n\n  let node: Node | null\n  while ((node = walker.nextNode())) {\n    const el = node as HTMLElement\n    const tag = el.tagName.toLowerCase()\n    const text = (el.textContent || '').trim().replace(/\\s+/g, ' ').slice(0, 80)\n    const id = el.id ? `#${el.id}` : ''\n    const classes = el.className && typeof el.className === 'string'\n      ? '.' + el.className.trim().split(/\\s+/).slice(0, 2).join('.')\n      : ''\n    const href = el.getAttribute('href')\n    const placeholder = el.getAttribute('placeholder')\n    const type = el.getAttribute('type')\n    const name = el.getAttribute('name')\n    const role = el.getAttribute('role')\n    const ariaLabel = el.getAttribute('aria-label')\n\n    let desc = `<${tag}${id}${classes}`\n    if (href) desc += ` href=\"${href}\"`\n    if (placeholder) desc += ` placeholder=\"${placeholder}\"`\n    if (type) desc += ` type=\"${type}\"`\n    if (name) desc += ` name=\"${name}\"`\n    if (role) desc += ` role=\"${role}\"`\n    if (ariaLabel) desc += ` aria-label=\"${ariaLabel}\"`\n\n    // Form value / state — makes the snapshot actionable for the agent.\n    if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {\n      if (el.value) desc += ` value=\"${String(el.value).slice(0, 60)}\"`\n      if (el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio')) {\n        desc += ` checked=\"${el.checked}\"`\n      }\n    } else if (el instanceof HTMLSelectElement && el.value) {\n      desc += ` value=\"${el.value}\"`\n    } else if (el instanceof HTMLAnchorElement) {\n      desc += ` target=\"${el.target || '_self'}\"`\n    }\n\n    desc += '>'\n    if (text) desc += `${text}`\n    desc += `</${tag}>`\n\n    elements.push(desc)\n  }\n\n  return elements.join('\\n').slice(0, 8000)  // cap at ~8K chars\n}\n\n/**\n * Execute a single browser action directly in the DOM.\n */\nexport function executeBrowserAction(action: BrowserAction): BrowserActionResult {\n  if (typeof document === 'undefined') {\n    return { success: false, error: 'Not running in browser environment' }\n  }\n\n  try {\n    const el = findElement(action.selector, action.description)\n    const needsEl = !['navigate', 'extract', 'getInfo', 'back', 'forward', 'scroll'].includes(action.type)\n    if (!el && needsEl) {\n      return { success: false, error: `Element not found: ${action.selector || action.description}` }\n    }\n\n    switch (action.type) {\n      case 'click': {\n        (el as HTMLElement).click()\n        return { success: true, result: 'Clicked' }\n      }\n\n      case 'type': {\n        const input = el as HTMLInputElement | HTMLTextAreaElement\n        input.focus()\n        input.value = action.value || ''\n        input.dispatchEvent(new Event('input', { bubbles: true }))\n        input.dispatchEvent(new Event('change', { bubbles: true }))\n        return { success: true, result: `Typed: ${action.value}` }\n      }\n\n      case 'press': {\n        const key = action.value || action.selector || ''\n        if (!key) return { success: false, error: 'No key provided for press' }\n        const target = (el || document.activeElement || document.body) as Element\n        const opts = { bubbles: true, cancelable: true, key }\n        target.dispatchEvent(new KeyboardEvent('keydown', opts))\n        target.dispatchEvent(new KeyboardEvent('keyup', opts))\n        return { success: true, result: `Pressed: ${key}` }\n      }\n\n      case 'hover': {\n        const target = el as HTMLElement\n        target.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }))\n        target.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }))\n        target.dispatchEvent(new MouseEvent('mousemove', { bubbles: true }))\n        return { success: true, result: `Hovered: ${action.selector || action.description || target.tagName}` }\n      }\n\n      case 'select': {\n        const value = action.value || ''\n        if (el instanceof HTMLSelectElement) {\n          el.value = value\n          el.dispatchEvent(new Event('change', { bubbles: true }))\n          return { success: true, result: `Selected: ${value}` }\n        }\n        if (el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio')) {\n          const checked = value === '' ? !el.checked : value.toLowerCase() === 'true' || value === '1'\n          el.checked = checked\n          el.dispatchEvent(new Event('change', { bubbles: true }))\n          return { success: true, result: `Checked: ${checked}` }\n        }\n        return { success: false, error: `Element is not a select/checkbox/radio: ${action.selector}` }\n      }\n\n      case 'extract': {\n        if (action.selector) {\n          const content = el?.textContent || ''\n          return { success: true, result: content.slice(0, 5000) }\n        }\n        // Extract full page accessible DOM\n        return { success: true, result: extractAccessibleDOM() }\n      }\n\n      case 'getInfo': {\n        return {\n          success: true,\n          result: JSON.stringify({\n            url: window.location.href,\n            title: document.title,\n            readyState: document.readyState,\n            viewport: { width: window.innerWidth, height: window.innerHeight },\n            scrollY: Math.round(window.scrollY),\n          }),\n        }\n      }\n\n      case 'scroll': {\n        if (el) {\n          el.scrollIntoView({ behavior: 'smooth', block: 'center' })\n        } else {\n          window.scrollBy({ top: action.value ? parseInt(action.value) || 500 : 500, behavior: 'smooth' })\n        }\n        return { success: true, result: 'Scrolled' }\n      }\n\n      case 'navigate': {\n        const url = action.value || action.selector\n        if (!url) return { success: false, error: 'No URL provided' }\n        window.location.href = url\n        return { success: true, result: `Navigating to ${url}` }\n      }\n\n      case 'back': {\n        window.history.back()\n        return { success: true, result: 'Navigated back' }\n      }\n\n      case 'forward': {\n        window.history.forward()\n        return { success: true, result: 'Navigated forward' }\n      }\n\n      default:\n        return { success: false, error: `Unknown action type: ${(action as any).type}` }\n    }\n  } catch (err) {\n    return { success: false, error: err instanceof Error ? err.message : String(err) }\n  }\n}\n\nfunction findElement(selector?: string, description?: string): Element | null {\n  // Try CSS selector first\n  if (selector) {\n    try {\n      const el = document.querySelector(selector)\n      if (el) return el\n    } catch { /* invalid selector */ }\n  }\n\n  // Fallback: text content search\n  const searchText = description || selector\n  if (!searchText) return null\n\n  const all = document.querySelectorAll('a, button, input, select, textarea, [role=\"button\"]')\n  const lower = searchText.toLowerCase()\n  for (const el of all) {\n    const text = (el.textContent || '').toLowerCase()\n    const placeholder = (el.getAttribute('placeholder') || '').toLowerCase()\n    const ariaLabel = (el.getAttribute('aria-label') || '').toLowerCase()\n    const name = (el.getAttribute('name') || '').toLowerCase()\n    if (text.includes(lower) || placeholder.includes(lower) || ariaLabel.includes(lower) || name.includes(lower)) {\n      return el\n    }\n  }\n\n  return null\n}\n"],"mappings":"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","names":["node"]}