surf-cli 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Nico Bailon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,426 @@
1
+ # Surf
2
+
3
+ The CLI for AI agents to control Chrome. Zero config, agent-agnostic, battle-tested.
4
+
5
+ ```bash
6
+ surf go "https://example.com"
7
+ surf read
8
+ surf click e5
9
+ surf snap
10
+ ```
11
+
12
+ ## Why Surf
13
+
14
+ Browser automation for AI agents is harder than it looks. Most tools require complex setup, tie you to specific AI providers, or break on real-world pages.
15
+
16
+ Surf takes a different approach:
17
+
18
+ **Agent-Agnostic** - Pure CLI commands over Unix socket. Works with Claude Code, GPT, Gemini, Cursor, custom agents, shell scripts - anything that can run commands.
19
+
20
+ **Zero Config** - Install the extension, run commands. No MCP servers to configure, no relay processes, no subscriptions.
21
+
22
+ **Battle-Tested** - Built by reverse-engineering production browser extensions and methodically working through agent-hostile pages like Discord settings. Falls back gracefully when CDP fails.
23
+
24
+ **Smart Defaults** - Screenshots auto-resize to 1200px (saves tokens). Actions auto-capture screenshots (saves round-trips). Errors on restricted pages warn instead of fail.
25
+
26
+ **AI Without API Keys** - Query ChatGPT, Gemini, and Perplexity using your browser's logged-in session. No API keys, no rate limits, no cost.
27
+
28
+ **Network Capture** - Automatically logs all network requests while active. Filter, search, and replay API calls without manually setting up request interception.
29
+
30
+ ## Comparison
31
+
32
+ | Feature | Surf | Manus | Claude Extension | DevTools MCP | dev-browser |
33
+ |---------|------|-------|------------------|--------------|-------------|
34
+ | Agent-agnostic | Yes | No (Manus only) | No (Claude only) | Partial | No (Claude skill) |
35
+ | Zero config | Yes | No (subscription) | No (subscription) | No (MCP setup) | No (relay server) |
36
+ | Local-only | Yes | No (cloud) | Partial | Yes | Partial |
37
+ | CLI interface | Yes | No | No | No | No |
38
+ | Free | Yes | No | No | Yes | Yes |
39
+ | AI via browser cookies | Yes | No | No | No | No |
40
+
41
+ ## Installation
42
+
43
+ ### Quick Start
44
+
45
+ ```bash
46
+ # 1. Install globally
47
+ npm install -g surf-cli
48
+
49
+ # 2. Load extension in Chrome
50
+ # - Open chrome://extensions
51
+ # - Enable "Developer mode"
52
+ # - Click "Load unpacked"
53
+ # - Paste the path from: surf extension-path
54
+
55
+ # 3. Install native host (copy extension ID from chrome://extensions)
56
+ surf install <extension-id>
57
+
58
+ # 4. Restart Chrome and test
59
+ surf tab.list
60
+ ```
61
+
62
+ ### Multi-Browser Support
63
+
64
+ ```bash
65
+ surf install <extension-id> # Chrome (default)
66
+ surf install <extension-id> --browser brave # Brave
67
+ surf install <extension-id> --browser all # All supported browsers
68
+ ```
69
+
70
+ Supported: `chrome`, `chromium`, `brave`, `edge`, `arc`
71
+
72
+ ### Uninstall
73
+
74
+ ```bash
75
+ surf uninstall # Chrome only
76
+ surf uninstall --all # All browsers + wrapper files
77
+ ```
78
+
79
+ ### Development Setup
80
+
81
+ ```bash
82
+ git clone https://github.com/nicobailon/surf-cli.git
83
+ cd surf-cli
84
+ npm install
85
+ npm run build
86
+ # Then load dist/ as unpacked extension
87
+ ```
88
+
89
+ ## Usage
90
+
91
+ ```bash
92
+ surf <command> [args] [options]
93
+ surf --help # Basic help
94
+ surf --help-full # All 50+ commands
95
+ surf <command> --help # Command details
96
+ surf --find <query> # Search commands
97
+ ```
98
+
99
+ ### Navigation
100
+
101
+ ```bash
102
+ surf go "https://example.com"
103
+ surf back
104
+ surf forward
105
+ surf tab.reload --hard
106
+ ```
107
+
108
+ ### Reading Pages
109
+
110
+ ```bash
111
+ surf read # Accessibility tree + visible text content
112
+ surf read --no-text # Accessibility tree only (no text)
113
+ surf read --depth 3 # Limit tree depth (smaller output)
114
+ surf read --compact # Remove empty structural elements
115
+ surf read --depth 3 --compact # Both (60% smaller output)
116
+ surf page.text # Raw text content only
117
+ surf page.state # Modals, loading state, scroll position
118
+ ```
119
+
120
+ Element refs (`e1`, `e2`, `e3`...) are stable identifiers from the accessibility tree - semantic, predictable, and resilient to DOM changes.
121
+
122
+ ### Semantic Locators
123
+
124
+ Find and interact with elements by role, text, or label - no refs or selectors needed:
125
+
126
+ ```bash
127
+ # By ARIA role
128
+ surf locate.role button --name "Submit" # Find button
129
+ surf locate.role button --name "Submit" --action click # Find and click
130
+ surf locate.role textbox --action fill --value "hello" # Find and fill
131
+ surf locate.role link --all # List all links
132
+
133
+ # By text content
134
+ surf locate.text "Sign In" --action click # Click element with text
135
+ surf locate.text "Accept" --exact # Exact match only
136
+
137
+ # By form label
138
+ surf locate.label "Email" --action fill --value "test@example.com"
139
+ ```
140
+
141
+ ### Iframe Support
142
+
143
+ Work with content inside iframes:
144
+
145
+ ```bash
146
+ surf frame.list # List all frames
147
+ surf frame.switch --index 0 # Switch to first iframe
148
+ surf frame.switch --name "payment" # Switch by frame name
149
+ surf frame.switch --selector "#checkout-frame" # Switch by CSS selector
150
+
151
+ # Now all commands target the iframe
152
+ surf read # Read iframe content
153
+ surf click e5 # Click in iframe
154
+ surf locate.role button --action click
155
+
156
+ surf frame.main # Return to main page
157
+ ```
158
+
159
+ ### Interaction
160
+
161
+ ```bash
162
+ surf click e5 # Click by element ref
163
+ surf click --selector ".btn" # Click by CSS selector
164
+ surf click 100 200 # Click by coordinates
165
+ surf type "hello" --submit # Type and press Enter
166
+ surf type "email@example.com" --ref e12 # Type into specific element
167
+ surf key Escape # Press key
168
+ surf scroll.bottom # Scroll to bottom
169
+ ```
170
+
171
+ ### Screenshots
172
+
173
+ Screenshots are optimized for AI consumption by default:
174
+
175
+ ```bash
176
+ surf screenshot --output /tmp/shot.png # Auto-resized to 1200px max
177
+ surf screenshot --full --output /tmp/hd.png # Full resolution
178
+ surf screenshot --annotate --output /tmp/labeled.png # With element labels
179
+ surf screenshot --fullpage --output /tmp/full.png # Entire page
180
+ surf snap # Quick save to /tmp
181
+ ```
182
+
183
+ Actions like `click`, `type`, and `scroll` automatically capture a screenshot after execution - no extra command needed.
184
+
185
+ ### Tabs
186
+
187
+ ```bash
188
+ surf tab.list
189
+ surf tab.new "https://example.com"
190
+ surf tab.switch 123
191
+ surf tab.close 123
192
+ surf tab.name "dashboard" # Name current tab
193
+ surf tab.switch "dashboard" # Switch by name
194
+ surf tab.group --name "Work" --color blue
195
+ ```
196
+
197
+ ### Window Isolation
198
+
199
+ Keep using your browser while the agent works in a separate window:
200
+
201
+ ```bash
202
+ # Create isolated window for agent
203
+ surf window.new "https://example.com"
204
+ # Returns: Window 123456 (tab 789)
205
+
206
+ # All subsequent commands target that window
207
+ surf click e5 --window-id 123456
208
+ surf read --window-id 123456
209
+ surf tab.new "https://other.com" --window-id 123456
210
+
211
+ # Or manage windows directly
212
+ surf window.list # List all windows
213
+ surf window.list --tabs # Include tab details
214
+ surf window.focus 123456 # Bring window to front
215
+ surf window.close 123456 # Close window
216
+ ```
217
+
218
+ ### Device Emulation
219
+
220
+ Test responsive designs and mobile layouts:
221
+
222
+ ```bash
223
+ surf emulate.device --list # Show available devices
224
+ surf emulate.device "iPhone 14" # Emulate iPhone 14
225
+ surf emulate.device "Pixel 7" # Emulate Pixel 7
226
+ surf emulate.device reset # Return to desktop
227
+
228
+ # Custom viewport
229
+ surf emulate.viewport --width 375 --height 812
230
+ surf emulate.viewport --width 1920 --height 1080 --scale 2
231
+
232
+ # Touch emulation
233
+ surf emulate.touch # Enable touch
234
+ surf emulate.touch --enabled false # Disable touch
235
+ ```
236
+
237
+ Available devices: iPhone 12-14 (Pro/Max), iPhone SE, iPad (Pro/Mini), Pixel 5-7 (Pro), Galaxy S21-S23, Galaxy Tab S7, Nest Hub (Max).
238
+
239
+ ### Performance Tracing
240
+
241
+ Capture performance metrics and traces:
242
+
243
+ ```bash
244
+ surf perf.metrics # Current performance metrics
245
+ surf perf.start # Start tracing
246
+ surf perf.stop # Stop and get trace data
247
+ ```
248
+
249
+ ### AI Queries (No API Keys)
250
+
251
+ Query AI models using your browser's logged-in session:
252
+
253
+ ```bash
254
+ # ChatGPT
255
+ surf chatgpt "explain this code"
256
+ surf chatgpt "summarize" --with-page # Include page context
257
+ surf chatgpt "analyze" --model gpt-4o # Specify model
258
+ surf chatgpt "review" --file code.ts # Attach file
259
+
260
+ # Gemini
261
+ surf gemini "explain quantum computing"
262
+ surf gemini "summarize" --with-page # Include page context
263
+ surf gemini "analyze" --file data.csv # Attach file
264
+ surf gemini "a robot surfing" --generate-image /tmp/robot.png # Generate image
265
+ surf gemini "add sunglasses" --edit-image photo.jpg --output out.jpg
266
+ surf gemini "summarize" --youtube "https://youtube.com/..." # YouTube analysis
267
+ surf gemini "hello" --model gemini-2.5-flash # Model selection
268
+
269
+ # Perplexity
270
+ surf perplexity "what is quantum computing"
271
+ surf perplexity "explain this page" --with-page # Include page context
272
+ surf perplexity "deep dive" --mode research # Research mode (Pro)
273
+ surf perplexity "latest news" --model sonar # Model selection (Pro)
274
+ ```
275
+
276
+ Requires being logged into chatgpt.com, gemini.google.com, or perplexity.ai in Chrome.
277
+
278
+ ### Waiting
279
+
280
+ ```bash
281
+ surf wait 2 # Wait 2 seconds
282
+ surf wait.element ".loaded" # Wait for element
283
+ surf wait.network # Wait for network idle
284
+ surf wait.url "/dashboard" # Wait for URL pattern
285
+ ```
286
+
287
+ ### Other
288
+
289
+ ```bash
290
+ surf js "return document.title" # Execute JavaScript
291
+ surf search "login" # Find text in page
292
+ surf cookie.list # List cookies
293
+ surf zoom 1.5 # Set zoom to 150%
294
+ surf console # Read console messages
295
+ surf network # Read network requests
296
+ ```
297
+
298
+ ### Network Capture
299
+
300
+ Surf automatically captures all network requests while active. No explicit start needed.
301
+
302
+ ```bash
303
+ # Overview (token-efficient for LLMs)
304
+ surf network # Recent requests, compact table
305
+ surf network --urls # Just URLs (minimal output)
306
+ surf network --format curl # As curl commands
307
+
308
+ # Filtering
309
+ surf network --origin api.github.com # Filter by origin/domain
310
+ surf network --method POST # Only POST requests
311
+ surf network --type json # Only JSON responses
312
+ surf network --status 4xx,5xx # Only errors
313
+ surf network --since 5m # Last 5 minutes
314
+ surf network --exclude-static # Skip images/fonts/css/js
315
+
316
+ # Drill down
317
+ surf network.get r_001 # Full request/response details
318
+ surf network.body r_001 # Response body (for piping to jq)
319
+ surf network.curl r_001 # Generate curl command
320
+ surf network.origins # List captured domains
321
+
322
+ # Management
323
+ surf network.clear # Clear captured data
324
+ surf network.stats # Capture statistics
325
+ ```
326
+
327
+ Storage location: `/tmp/surf/` (override with `--network-path` or `SURF_NETWORK_PATH` env).
328
+ Auto-cleanup: 24 hours TTL, 200MB max.
329
+
330
+ ## Global Options
331
+
332
+ ```bash
333
+ --tab-id <id> # Target specific tab
334
+ --window-id <id> # Target specific window (isolate agent from your browsing)
335
+ --json # Output raw JSON
336
+ --soft-fail # Warn instead of error (exit 0) on restricted pages
337
+ --no-screenshot # Skip auto-screenshot after actions
338
+ --full # Full resolution screenshots (skip resize)
339
+ --network-path <path> # Custom path for network logs (default: /tmp/surf, or SURF_NETWORK_PATH env)
340
+ ```
341
+
342
+ ## Socket API
343
+
344
+ For programmatic integration, send JSON to `/tmp/surf.sock`:
345
+
346
+ ```bash
347
+ echo '{"type":"tool_request","method":"execute_tool","params":{"tool":"tab.list","args":{}},"id":"1"}' | nc -U /tmp/surf.sock
348
+ ```
349
+
350
+ ## Command Groups
351
+
352
+ | Group | Commands |
353
+ |-------|----------|
354
+ | `window.*` | `new`, `list`, `focus`, `close`, `resize` |
355
+ | `tab.*` | `list`, `new`, `switch`, `close`, `name`, `unname`, `named`, `group`, `ungroup`, `groups`, `reload` |
356
+ | `scroll.*` | `top`, `bottom`, `to`, `info` |
357
+ | `page.*` | `read`, `text`, `state` |
358
+ | `locate.*` | `role`, `text`, `label` |
359
+ | `frame.*` | `list`, `switch`, `main`, `js` |
360
+ | `wait.*` | `element`, `network`, `url`, `dom`, `load` |
361
+ | `cookie.*` | `list`, `get`, `set`, `clear` |
362
+ | `bookmark.*` | `add`, `remove`, `list` |
363
+ | `history.*` | `list`, `search` |
364
+ | `dialog.*` | `accept`, `dismiss`, `info` |
365
+ | `emulate.*` | `network`, `cpu`, `geo`, `device`, `viewport`, `touch` |
366
+ | `perf.*` | `start`, `stop`, `metrics` |
367
+ | `network.*` | `get`, `body`, `curl`, `origins`, `clear`, `stats`, `export`, `path` |
368
+
369
+ ## Aliases
370
+
371
+ | Alias | Command |
372
+ |-------|---------|
373
+ | `snap` | `screenshot` |
374
+ | `read` | `page.read` |
375
+ | `find` | `search` |
376
+ | `go` | `navigate` |
377
+
378
+ ## How It Works
379
+
380
+ ```
381
+ CLI (surf) → Unix Socket → Native Host → Chrome Extension → CDP/Scripting API
382
+ ```
383
+
384
+ Surf uses Chrome DevTools Protocol for most operations, with automatic fallback to `chrome.scripting` API when CDP is unavailable (restricted pages, certain contexts). Screenshots fall back to `captureVisibleTab` when CDP capture fails.
385
+
386
+ ## Limitations
387
+
388
+ - Cannot automate `chrome://` pages or the Chrome Web Store (Chrome restriction)
389
+ - First CDP operation on a new tab takes ~100-500ms (debugger attachment)
390
+ - Some operations on restricted pages return warnings instead of results
391
+
392
+ ## Linux Support (Experimental)
393
+
394
+ Surf should work on Linux with Chromium. Not yet tested in production.
395
+
396
+ ```bash
397
+ # Install dependencies
398
+ sudo apt install chromium-browser nodejs npm imagemagick
399
+
400
+ # For headless server: add Xvfb + VNC
401
+ sudo apt install xvfb tigervnc-standalone-server
402
+
403
+ # Install Surf and native host
404
+ npm install -g surf-cli
405
+ surf install <extension-id> --browser chromium
406
+ ```
407
+
408
+ **Notes:**
409
+ - Use Chromium (no official Chrome for Linux ARM64)
410
+ - Screenshot resize uses ImageMagick instead of macOS `sips`
411
+ - Headless servers need Xvfb + VNC for initial login setup
412
+
413
+ ## Development
414
+
415
+ ```bash
416
+ npm run dev # Watch mode
417
+ npm run build # Production build
418
+ ```
419
+
420
+ After changes:
421
+ - **Extension** (`src/`): Reload at `chrome://extensions`
422
+ - **Host** (`native/`): Restart `node native/host.cjs`
423
+
424
+ ## License
425
+
426
+ MIT
@@ -0,0 +1,11 @@
1
+ const J=new Set(["alert","alertdialog","application","article","banner","blockquote","button","caption","cell","checkbox","code","columnheader","combobox","complementary","contentinfo","definition","deletion","dialog","directory","document","emphasis","feed","figure","form","generic","grid","gridcell","group","heading","img","insertion","link","list","listbox","listitem","log","main","mark","marquee","math","menu","menubar","menuitem","menuitemcheckbox","menuitemradio","meter","navigation","none","note","option","paragraph","presentation","progressbar","radio","radiogroup","region","row","rowgroup","rowheader","scrollbar","search","searchbox","separator","slider","spinbutton","status","strong","subscript","superscript","switch","tab","table","tablist","tabpanel","term","textbox","time","timer","toolbar","tooltip","tree","treegrid","treeitem"]);function Q(r){const h=r.tagName.toLowerCase();if(["button","input","select","textarea"].includes(h))return!r.disabled;if(h==="a"&&r.hasAttribute("href"))return!0;if(r.hasAttribute("tabindex")){const i=parseInt(r.getAttribute("tabindex")||"",10);return!isNaN(i)&&i>=0}return r.getAttribute("contenteditable")==="true"}function Z(r){const h=r.getAttribute("role");if(!h)return null;const i=h.split(/\s+/).filter(m=>m);for(const m of i)if(J.has(m))return m;return null}function X(r){const h=r.tagName.toLowerCase(),i=r.getAttribute("type"),m={a:p=>p.hasAttribute("href")?"link":"generic",article:"article",aside:"complementary",button:"button",datalist:"listbox",dd:"definition",details:"group",dialog:"dialog",dt:"term",fieldset:"group",figure:"figure",footer:p=>p.closest("article, aside, main, nav, section")?"generic":"contentinfo",form:p=>p.hasAttribute("aria-label")||p.hasAttribute("aria-labelledby")?"form":"generic",h1:"heading",h2:"heading",h3:"heading",h4:"heading",h5:"heading",h6:"heading",header:p=>p.closest("article, aside, main, nav, section")?"generic":"banner",hr:"separator",img:p=>p.getAttribute("alt")===""?"presentation":"img",li:"listitem",main:"main",math:"math",menu:"list",meter:"meter",nav:"navigation",ol:"list",optgroup:"group",option:"option",output:"status",p:"paragraph",progress:"progressbar",search:"search",section:p=>p.hasAttribute("aria-label")||p.hasAttribute("aria-labelledby")?"region":"generic",select:p=>{const e=p;return e.hasAttribute("multiple")||e.size&&e.size>1?"listbox":"combobox"},table:"table",tbody:"rowgroup",td:"cell",textarea:"textbox",tfoot:"rowgroup",th:"columnheader",thead:"rowgroup",time:"time",tr:"row",ul:"list"};if(h==="input")return{button:"button",checkbox:"checkbox",email:"textbox",file:"button",image:"button",number:"spinbutton",radio:"radio",range:"slider",reset:"button",search:"searchbox",submit:"button",tel:"textbox",text:"textbox",url:"textbox"}[i||""]||"textbox";const g=m[h];return typeof g=="function"?g(r):g||"generic"}function U(r){const h=Z(r);return!h||(h==="none"||h==="presentation")&&Q(r)?X(r):h}window.__piElementMap||(window.__piElementMap={});let ee=0;function q(r,h,i){const m=r._piRef;if(m&&m.role===h&&m.name===i)return m.ref;const g=`e${++ee}`;return r._piRef={role:h,name:i,ref:g},g}function Y(){const r=[];return document.querySelectorAll('[role="dialog"], [role="alertdialog"], dialog[open]').forEach(i=>{var s,c;const m=window.getComputedStyle(i);if(!(m.display!=="none"&&m.visibility!=="hidden"&&m.opacity!=="0"&&i.offsetWidth>0&&i.offsetHeight>0))return;const p=i.getAttribute("role")||"dialog";let e=i.getAttribute("aria-label")||((c=(s=i.querySelector('[role="heading"], h1, h2, h3'))==null?void 0:s.textContent)==null?void 0:c.trim())||"Dialog";e.length>100&&(e=e.substring(0,100)+"..."),r.push({type:p,description:`${p}: ${e}`,clearedBy:"computer(action=key, text=Escape)"})}),r}const j={wait(r){return new Promise(h=>setTimeout(h,r))},async waitForSelector(r,h={}){const{state:i="visible",timeout:m=2e4}=h,g=e=>{if(!e)return!1;const s=window.getComputedStyle(e);return s.display!=="none"&&s.visibility!=="hidden"&&s.opacity!=="0"&&e.offsetWidth>0&&e.offsetHeight>0},p=()=>{const e=document.querySelector(r);switch(i){case"attached":return e;case"detached":return e?null:document.body;case"hidden":return e?g(e)?null:e:document.body;case"visible":default:return g(e)?e:null}};return new Promise((e,s)=>{const c=p();if(c){e(i==="detached"||i==="hidden"?null:c);return}const a=new MutationObserver(()=>{const u=p();u&&(a.disconnect(),clearTimeout(w),e(i==="detached"||i==="hidden"?null:u))}),w=setTimeout(()=>{a.disconnect(),s(new Error(`Timeout waiting for "${r}" to be ${i}`))},m);a.observe(document.documentElement,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["style","class","hidden"]})})},async waitForText(r,h={}){const{selector:i,timeout:m=2e4}=h,g=()=>{var s;const p=i?document.querySelector(i):document.body;if(!p)return null;const e=document.createTreeWalker(p,NodeFilter.SHOW_TEXT);for(;e.nextNode();)if((s=e.currentNode.textContent)!=null&&s.includes(r))return e.currentNode.parentElement;return null};return new Promise((p,e)=>{const s=g();if(s){p(s);return}const c=new MutationObserver(()=>{const w=g();w&&(c.disconnect(),clearTimeout(a),p(w))}),a=setTimeout(()=>{c.disconnect(),e(new Error(`Timeout waiting for text "${r}"`))},m);c.observe(document.documentElement,{childList:!0,subtree:!0,characterData:!0})})},async waitForHidden(r,h=2e4){await j.waitForSelector(r,{state:"hidden",timeout:h})},getByRole(r,h={}){var s,c,a,w;const{name:i}=h,m={button:["button",'input[type="button"]','input[type="submit"]','input[type="reset"]'],link:["a[href]"],textbox:["input:not([type])",'input[type="text"]','input[type="email"]','input[type="password"]','input[type="search"]','input[type="tel"]','input[type="url"]',"textarea"],checkbox:['input[type="checkbox"]'],radio:['input[type="radio"]'],combobox:["select"],heading:["h1","h2","h3","h4","h5","h6"],list:["ul","ol"],listitem:["li"],navigation:["nav"],main:["main"],banner:["header"],contentinfo:["footer"],form:["form"],img:["img"],table:["table"]},g=[];g.push(...document.querySelectorAll(`[role="${r}"]`));const p=m[r];if(p)for(const u of p)g.push(...document.querySelectorAll(`${u}:not([role])`));if(!i)return g[0]||null;const e=i.toLowerCase().trim();for(const u of g){const f=(s=u.getAttribute("aria-label"))==null?void 0:s.toLowerCase().trim(),b=(c=u.textContent)==null?void 0:c.toLowerCase().trim(),l=(a=u.getAttribute("title"))==null?void 0:a.toLowerCase().trim(),t=(w=u.getAttribute("placeholder"))==null?void 0:w.toLowerCase().trim();if(f===e||b===e||l===e||t===e||f!=null&&f.includes(e)||b!=null&&b.includes(e))return u}return null}};window.__piHelpers||(window.__piHelpers=j,window.piHelpers=j);function H(){return window.__piElementMap}function B(r="interactive",h=15,i,m=!1,g=!1){try{let p=function(n){return U(n)},e=function(n){var F,W;const d=n.tagName.toLowerCase(),_=n.getAttribute("aria-labelledby");if(_){const E=_.split(/\s+/).map(C=>{var P;const I=document.getElementById(C);return((P=I==null?void 0:I.textContent)==null?void 0:P.trim())||""}).filter(Boolean);if(E.length){const C=E.join(" ");return C.length>100?C.substring(0,100)+"...":C}}if(d==="select"){const E=n,C=E.querySelector("option[selected]")||(E.selectedIndex>=0?E.options[E.selectedIndex]:null);if((F=C==null?void 0:C.textContent)!=null&&F.trim())return C.textContent.trim()}const $=n.getAttribute("aria-label");if($!=null&&$.trim())return $.trim();const M=n.getAttribute("placeholder");if(M!=null&&M.trim())return M.trim();const O=n.getAttribute("title");if(O!=null&&O.trim())return O.trim();const T=n.getAttribute("alt");if(T!=null&&T.trim())return T.trim();if(n.id){const E=document.querySelector(`label[for="${n.id}"]`);if((W=E==null?void 0:E.textContent)!=null&&W.trim())return E.textContent.trim()}if(d==="input"){const E=n,C=n.getAttribute("type")||"",I=n.getAttribute("value");if(C==="submit"&&(I!=null&&I.trim()))return I.trim();if(E.value&&E.value.length<50&&E.value.trim())return E.value.trim()}if(["button","a","summary"].includes(d)){let E="";for(const C of n.childNodes)C.nodeType===Node.TEXT_NODE&&(E+=C.textContent);if(E.trim())return E.trim()}if(/^h[1-6]$/.test(d)){const E=n.textContent;if(E!=null&&E.trim()){const C=E.trim();return C.length>100?C.substring(0,100)+"...":C}}if(d==="img")return"";let N="";for(const E of n.childNodes)E.nodeType===Node.TEXT_NODE&&(N+=E.textContent);if(N!=null&&N.trim()&&N.trim().length>=3){const E=N.trim();return E.length>100?E.substring(0,100)+"...":E}return""},s=function(n){const d={},_=n.getAttribute("aria-checked");_==="true"?d.checked=!0:_==="false"?d.checked=!1:_==="mixed"?d.checked="mixed":n instanceof HTMLInputElement&&(n.type==="checkbox"||n.type==="radio")&&(n.type==="checkbox"&&n.indeterminate?d.checked="mixed":d.checked=n.checked);const $=n instanceof HTMLButtonElement||n instanceof HTMLInputElement||n instanceof HTMLSelectElement||n instanceof HTMLTextAreaElement;(n.getAttribute("aria-disabled")==="true"||$&&n.disabled||n.closest("fieldset:disabled"))&&(d.disabled=!0);const M=n.getAttribute("aria-expanded");M==="true"?d.expanded=!0:M==="false"&&(d.expanded=!1);const O=n.getAttribute("aria-pressed");O==="true"?d.pressed=!0:O==="false"?d.pressed=!1:O==="mixed"&&(d.pressed="mixed");const T=n.getAttribute("aria-selected");T==="true"?d.selected=!0:T==="false"&&(d.selected=!1);const N=n.getAttribute("aria-current");N&&N!=="false"&&(d.active=!0);const F=n.tagName.toLowerCase();if(/^h[1-6]$/.test(F))d.level=parseInt(F[1],10);else{const W=n.getAttribute("aria-level");W&&(d.level=parseInt(W,10))}return d},c=function(n){const d=[];return n.checked!==void 0&&d.push(n.checked==="mixed"?"[checked=mixed]":n.checked?"[checked]":"[unchecked]"),n.disabled&&d.push("[disabled]"),n.expanded!==void 0&&d.push(n.expanded?"[expanded]":"[collapsed]"),n.pressed!==void 0&&d.push(n.pressed==="mixed"?"[pressed=mixed]":n.pressed?"[pressed]":"[not-pressed]"),n.selected!==void 0&&d.push(n.selected?"[selected]":"[not-selected]"),n.active&&d.push("[active]"),n.level!==void 0&&d.push(`[level=${n.level}]`),d.join(" ")},a=function(n){const d=window.getComputedStyle(n);return d.display!=="none"&&d.visibility!=="hidden"&&d.opacity!=="0"&&n.offsetWidth>0&&n.offsetHeight>0},w=function(n){const d=n.tagName.toLowerCase();return["a","button","input","select","textarea","details","summary"].includes(d)||n.hasAttribute("onclick")||n.hasAttribute("tabindex")||n.getAttribute("role")==="button"||n.getAttribute("role")==="link"||n.getAttribute("contenteditable")==="true"},u=function(n){const d=n.tagName.toLowerCase();return["h1","h2","h3","h4","h5","h6","nav","main","header","footer","section","article","aside"].includes(d)||n.hasAttribute("role")},f=function(n){return window.getComputedStyle(n).cursor==="pointer"},b=function(n,d){const _=n.tagName.toLowerCase();if(["script","style","meta","link","title","noscript"].includes(_)||d.filter!=="all"&&n.getAttribute("aria-hidden")==="true"||d.filter!=="all"&&!a(n))return!1;if(d.filter!=="all"&&!d.refId){const M=n.getBoundingClientRect();if(!(M.top<window.innerHeight&&M.bottom>0&&M.left<window.innerWidth&&M.right>0))return!1}if(d.filter==="interactive")return w(n);if(w(n)||u(n)||e(n).length>0)return!0;const $=p(n);return d.compact&&new Set(["generic","group","region","article","section","complementary"]).has($)&&e(n).length===0?!1:$!=="generic"&&$!=="img"},l=function(n,d){const _=[],$={filter:r,refId:i||null,compact:g},M=H(),O=b(n,$)||i&&d===0;if(O){const T=p(n),N=e(n),F=s(n),W=q(n,T,N);window.__piRefs[W]=n,M[W]={element:new WeakRef(n),role:T,name:N};let C=`${" ".repeat(d)}${T}`;if(N){const K=N.replace(/\s+/g," ").replace(/"/g,'\\"');C+=` "${K}"`}C+=` [${W}]`;const I=c(F);I&&(C+=` ${I}`),f(n)&&(C+=" [cursor=pointer]");const P=n.getAttribute("href");P&&(C+=` href="${P}"`);const G=n.getAttribute("type");G&&(C+=` type="${G}"`);const V=n.getAttribute("placeholder");V&&(C+=` placeholder="${V}"`),_.push(C)}if(d<h)for(const T of n.children)_.push(...l(T,O?d+1:d));return _},t=function(n){return n.replace(/\[e\d+\]/g,"[REF]")},o=function(n){const d=new Map;for(const _ of n){if(!_.trim())continue;const $=t(_);d.set($,(d.get($)||0)+1)}return d},y=function(n,d){const _=n.split(`
2
+ `),$=d.split(`
3
+ `),M=o(_),O=o($),T=[],N=[];for(const E of $){if(!E.trim())continue;const C=t(E),I=M.get(C)||0;(O.get(C)||0)>I&&(T.push(E),M.set(C,I+1))}const F=o(_);for(const E of _){if(!E.trim())continue;const C=t(E),I=F.get(C)||0,P=O.get(C)||0;I>P&&(N.push(E),F.set(C,I-1))}if(T.length===0&&N.length===0)return{diff:"[NO CHANGES]",hasChanges:!1};const W=[];return N.length>0&&W.push(...N.map(E=>`- ${E}`)),T.length>0&&W.push(...T.map(E=>`+ ${E}`)),{diff:W.join(`
4
+ `),hasChanges:!0}};window.__piRefs={};const v=H();let A=null;if(i){const n=v[i];if(!n)return{error:`Element with ref_id '${i}' not found. Use read_page without ref_id to get current elements.`,pageContent:"",viewport:{width:window.innerWidth,height:window.innerHeight}};const d=n.element.deref();if(!d)return delete v[i],{error:`Element with ref_id '${i}' no longer exists. Use read_page without ref_id to get current elements.`,pageContent:"",viewport:{width:window.innerWidth,height:window.innerHeight}};A=d}else A=document.body;const S=A?l(A,0):[];for(const n of Object.keys(v))v[n].element.deref()||delete v[n];const k=S.join(`
5
+ `);if(k.length>5e4)return{error:`Output exceeds 50000 character limit (${k.length} characters). Try using filter="interactive" or specify a ref_id.`,pageContent:"",viewport:{width:window.innerWidth,height:window.innerHeight}};const L=Y();let D,R=!1;const x=window.__piLastSnapshot;return!m&&!i&&x&&Date.now()-x.timestamp<5e3&&(D=y(x.content,k).diff,R=!0),window.__piLastSnapshot={content:k,timestamp:Date.now()},{pageContent:k+`
6
+
7
+ [Viewport: ${window.innerWidth}x${window.innerHeight}]`,diff:R?D:void 0,viewport:{width:window.innerWidth,height:window.innerHeight},modalStates:L.length>0?L:void 0,modalLimitations:"Only custom modals ([role=dialog]) detected. Native alert/confirm/prompt dialogs and system file choosers cannot be detected from content scripts.",isIncremental:R}}catch(p){return{error:`Error generating accessibility tree: ${p instanceof Error?p.message:"Unknown error"}`,pageContent:"",viewport:{width:window.innerWidth,height:window.innerHeight}}}}function z(r){return r.length?/[\n\r]/.test(r)||/^[\s]/.test(r)||/[\s]$/.test(r)||/[:"{}[\]]/.test(r)?'"'+r.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r")+'"':r:'""'}function te(r="interactive",h=15){try{let i=function(t){return U(t)},m=function(t){var D,R;const o=t.tagName.toLowerCase(),y=t.getAttribute("aria-labelledby");if(y){const x=y.split(/\s+/).map(n=>{var _;const d=document.getElementById(n);return((_=d==null?void 0:d.textContent)==null?void 0:_.trim())||""}).filter(Boolean);if(x.length){const n=x.join(" ");return n.length>100?n.substring(0,100)+"...":n}}if(o==="select"){const x=t,n=x.querySelector("option[selected]")||(x.selectedIndex>=0?x.options[x.selectedIndex]:null);if((D=n==null?void 0:n.textContent)!=null&&D.trim())return n.textContent.trim()}const v=t.getAttribute("aria-label");if(v!=null&&v.trim())return v.trim();const A=t.getAttribute("placeholder");if(A!=null&&A.trim())return A.trim();const S=t.getAttribute("title");if(S!=null&&S.trim())return S.trim();const k=t.getAttribute("alt");if(k!=null&&k.trim())return k.trim();if(t.id){const x=document.querySelector(`label[for="${t.id}"]`);if((R=x==null?void 0:x.textContent)!=null&&R.trim())return x.textContent.trim()}if(o==="input"){const x=t,n=t.getAttribute("type")||"",d=t.getAttribute("value");if(n==="submit"&&(d!=null&&d.trim()))return d.trim();if(x.value&&x.value.length<50&&x.value.trim())return x.value.trim()}if(["button","a","summary"].includes(o)){let x="";for(const n of t.childNodes)n.nodeType===Node.TEXT_NODE&&(x+=n.textContent);if(x.trim())return x.trim()}if(/^h[1-6]$/.test(o)){const x=t.textContent;if(x!=null&&x.trim()){const n=x.trim();return n.length>100?n.substring(0,100)+"...":n}}if(o==="img")return"";let L="";for(const x of t.childNodes)x.nodeType===Node.TEXT_NODE&&(L+=x.textContent);if(L!=null&&L.trim()&&L.trim().length>=3){const x=L.trim();return x.length>100?x.substring(0,100)+"...":x}return""},g=function(t){const o={},y=t.getAttribute("aria-checked");y==="true"?o.checked=!0:y==="false"?o.checked=!1:y==="mixed"?o.checked="mixed":t instanceof HTMLInputElement&&(t.type==="checkbox"||t.type==="radio")&&(t.type==="checkbox"&&t.indeterminate?o.checked="mixed":o.checked=t.checked);const v=t instanceof HTMLButtonElement||t instanceof HTMLInputElement||t instanceof HTMLSelectElement||t instanceof HTMLTextAreaElement;(t.getAttribute("aria-disabled")==="true"||v&&t.disabled||t.closest("fieldset:disabled"))&&(o.disabled=!0);const A=t.getAttribute("aria-expanded");A==="true"?o.expanded=!0:A==="false"&&(o.expanded=!1);const S=t.getAttribute("aria-pressed");S==="true"?o.pressed=!0:S==="false"?o.pressed=!1:S==="mixed"&&(o.pressed="mixed");const k=t.getAttribute("aria-selected");k==="true"?o.selected=!0:k==="false"&&(o.selected=!1);const L=t.getAttribute("aria-current");L&&L!=="false"&&(o.active=!0);const D=t.tagName.toLowerCase();if(/^h[1-6]$/.test(D))o.level=parseInt(D[1],10);else{const R=t.getAttribute("aria-level");R&&(o.level=parseInt(R,10))}return o},p=function(t){const o=[];return t.checked!==void 0&&o.push(t.checked==="mixed"?"[checked=mixed]":t.checked?"[checked]":"[unchecked]"),t.disabled&&o.push("[disabled]"),t.expanded!==void 0&&o.push(t.expanded?"[expanded]":"[collapsed]"),t.pressed!==void 0&&o.push(t.pressed==="mixed"?"[pressed=mixed]":t.pressed?"[pressed]":"[not-pressed]"),t.selected!==void 0&&o.push(t.selected?"[selected]":"[not-selected]"),t.active&&o.push("[active]"),t.level!==void 0&&o.push(`[level=${t.level}]`),o.join(" ")},e=function(t){const o=window.getComputedStyle(t);return o.display!=="none"&&o.visibility!=="hidden"&&o.opacity!=="0"&&t.offsetWidth>0&&t.offsetHeight>0},s=function(t){const o=t.tagName.toLowerCase();return["a","button","input","select","textarea","details","summary"].includes(o)||t.hasAttribute("onclick")||t.hasAttribute("tabindex")||t.getAttribute("role")==="button"||t.getAttribute("role")==="link"||t.getAttribute("contenteditable")==="true"},c=function(t){const o=t.tagName.toLowerCase();return["h1","h2","h3","h4","h5","h6","nav","main","header","footer","section","article","aside"].includes(o)||t.hasAttribute("role")},a=function(t){return window.getComputedStyle(t).cursor==="pointer"},w=function(t,o,y,v){let A=t;o&&(A+=" "+z(o));const S=q(y,t,o);window.__piRefs[S]=y,A+=` [ref=${S}]`;const k=p(v);return k&&(A+=` ${k}`),a(y)&&(A+=" [cursor=pointer]"),A},u=function(t){const o={},y=t.getAttribute("href");y&&(o.url=y);const v=t.getAttribute("placeholder");return v&&(o.placeholder=v),o},f=function(t,o,y){if(o>h)return;const v=t.tagName.toLowerCase();if(["script","style","meta","link","title","noscript"].includes(v)||r!=="all"&&t.getAttribute("aria-hidden")==="true"||r!=="all"&&!e(t))return;if(r!=="all"){const n=t.getBoundingClientRect();if(!(n.top<window.innerHeight&&n.bottom>0&&n.left<window.innerWidth&&n.right>0))return}const A=i(t),S=m(t),k=g(t),L=s(t),D=c(t),R=S.length>0;let x;if(r==="interactive"?x=L:r==="all"?x=!0:x=L||D||R||A!=="generic"&&A!=="img",x){const n=" ".repeat(o),d=w(A,S,t,k),_=u(t),$=[];for(const T of t.children)$.push(T);const M=$.length>0,O=Object.keys(_).length>0;if(!M&&!O)b.push(`${n}- ${d}`);else{b.push(`${n}- ${d}:`);for(const[T,N]of Object.entries(_))b.push(`${n} - /${T}: ${z(N)}`);for(const T of $)f(T,o+1,!0)}}else for(const n of t.children)f(n,o,y)};window.__piRefs={};const b=[];f(document.body,0,!1);const l=b.join(`
8
+ `);return l.length>5e4?{error:`Output exceeds 50000 character limit (${l.length} characters). Try using filter="interactive".`,yaml:"",viewport:{width:window.innerWidth,height:window.innerHeight}}:{yaml:l+`
9
+
10
+ [Viewport: ${window.innerWidth}x${window.innerHeight}]`,viewport:{width:window.innerWidth,height:window.innerHeight}}}catch(i){return{error:`Error generating YAML tree: ${i instanceof Error?i.message:"Unknown error"}`,yaml:"",viewport:{width:window.innerWidth,height:window.innerHeight}}}}function ne(r){const h=H(),i=h[r];let m;if(i&&(m=i.element.deref(),m||delete h[r]),!m&&window.__piRefs&&(m=window.__piRefs[r]),!m)return{x:0,y:0,error:`Element ${r} not found. Use read_page to get current elements.`};const g=m.getBoundingClientRect(),p=Math.round(g.left+g.width/2),e=Math.round(g.top+g.height/2);return{x:p,y:e}}function re(r,h){var e;const i=H(),m=i[r];let g;if(m&&(g=m.element.deref(),g||delete i[r]),!g&&window.__piRefs&&(g=window.__piRefs[r]),!g)return{success:!1,error:`Element ${r} not found. Use read_page to get current elements.`};const p=g.tagName.toLowerCase();try{if(p==="input"){const s=g,c=s.type.toLowerCase();c==="checkbox"||c==="radio"?(s.checked=!!h,s.dispatchEvent(new Event("change",{bubbles:!0}))):(s.value=String(h),s.dispatchEvent(new Event("input",{bubbles:!0})),s.dispatchEvent(new Event("change",{bubbles:!0})))}else if(p==="textarea"){const s=g;s.value=String(h),s.dispatchEvent(new Event("input",{bubbles:!0})),s.dispatchEvent(new Event("change",{bubbles:!0}))}else if(p==="select"){const s=g,c=String(h);let a=!1;for(const w of s.options)if(w.value===c||((e=w.textContent)==null?void 0:e.trim())===c){s.value=w.value,a=!0;break}if(!a)return{success:!1,error:`Option "${h}" not found in select element ${r}`};s.dispatchEvent(new Event("change",{bubbles:!0}))}else if(g.getAttribute("contenteditable")==="true")g.textContent=String(h),g.dispatchEvent(new Event("input",{bubbles:!0}));else return{success:!1,error:`Element ${r} (${p}) is not a form field`};return{success:!0}}catch(s){return{success:!1,error:`Failed to set value: ${s instanceof Error?s.message:"Unknown error"}`}}}function ie(){var r;try{const h=document.querySelector("article"),i=document.querySelector("main");return{text:((r=(h||i||document.body).textContent)==null?void 0:r.replace(/\s+/g," ").trim().substring(0,5e4))||"",title:document.title,url:window.location.href}}catch(h){return{text:"",title:"",url:"",error:`Failed to extract text: ${h instanceof Error?h.message:"Unknown error"}`}}}function oe(r){const h=H(),i=h[r];let m;return i&&(m=i.element.deref(),m||delete h[r]),!m&&window.__piRefs&&(m=window.__piRefs[r]),m?(m.scrollIntoView({behavior:"smooth",block:"center"}),{success:!0}):{success:!1,error:`Element ${r} not found. Run read_page to get current element refs.`}}function se(r,h,i,m="screenshot.png"){try{const g=atob(r),p=new ArrayBuffer(g.length),e=new Uint8Array(p);for(let f=0;f<g.length;f++)e[f]=g.charCodeAt(f);const s=new Blob([p],{type:"image/png"}),c=new File([s],m,{type:"image/png"});let a=null;if(h){const f=H(),b=f[h];if(b&&(a=b.element.deref(),a||delete f[h]),!a&&window.__piRefs&&(a=window.__piRefs[h]),!a)return{success:!1,error:`Element ${h} not found. Run read_page to get current element refs.`}}else if(i&&(a=document.elementFromPoint(i[0],i[1]),!a))return{success:!1,error:`No element at (${i[0]}, ${i[1]})`};if(!a)return{success:!1,error:"No target element"};if(a.tagName==="INPUT"&&a.type==="file"){const f=a,b=new DataTransfer;return b.items.add(c),f.files=b.files,f.dispatchEvent(new Event("change",{bubbles:!0})),{success:!0}}const w=new DataTransfer;w.items.add(c);const u=new DragEvent("drop",{bubbles:!0,cancelable:!0,dataTransfer:w});return a.dispatchEvent(u),{success:!0}}catch(g){return{success:!1,error:g instanceof Error?g.message:"Upload failed"}}}chrome.runtime.onMessage.addListener((r,h,i)=>{var m,g,p;switch(r.type){case"GENERATE_ACCESSIBILITY_TREE":{const e=r.options||{};if(e.format==="yaml"){const s=te(e.filter||"interactive",e.depth??15),c=Y();s.error?i({error:s.error,pageContent:"",viewport:s.viewport}):i({pageContent:s.yaml,viewport:s.viewport,modalStates:c.length>0?c:void 0,modalLimitations:"Only custom modals ([role=dialog]) detected. Native alert/confirm/prompt dialogs and system file choosers cannot be detected from content scripts."})}else{const s=B(e.filter||"interactive",e.depth??15,e.refId,e.forceFullSnapshot??!1,e.compact??!1);i(s)}break}case"GET_ELEMENT_COORDINATES":{const e=ne(r.ref);i(e);break}case"CLICK_ELEMENT":{const e=H(),s=e[r.ref];let c;if(s&&(c=s.element.deref(),c||delete e[r.ref]),!c&&window.__piRefs&&(c=window.__piRefs[r.ref]),!c){i({error:`Element ${r.ref} not found. Use read_page to get current elements.`});break}if(r.button==="triple"){const a=new MouseEvent("click",{bubbles:!0,cancelable:!0,view:window,detail:3});c.dispatchEvent(a)}else r.button==="double"?c.dispatchEvent(new MouseEvent("dblclick",{bubbles:!0,cancelable:!0,view:window})):r.button==="right"?c.dispatchEvent(new MouseEvent("contextmenu",{bubbles:!0,cancelable:!0,view:window})):c.click();i({success:!0});break}case"FORM_INPUT":{const e=re(r.ref,r.value);i(e);break}case"EVAL_IN_PAGE":{try{const e=document.createElement("script");e.textContent=`(function() { ${r.code} })();`,document.documentElement.appendChild(e),e.remove(),i({success:!0})}catch(e){i({success:!1,error:e instanceof Error?e.message:String(e)})}break}case"GET_PAGE_TEXT":{const e=ie();i(e);break}case"GET_FRAME_BY_SELECTOR":{try{const e=document.querySelector(r.selector);if(!e||e.tagName.toLowerCase()!=="iframe"){i({error:`No iframe found with selector "${r.selector}"`});break}i({url:e.src,name:e.name||void 0})}catch(e){i({error:e instanceof Error?e.message:String(e)})}break}case"GET_FRAME_NAME":{try{i({name:window.name||null})}catch{i({name:null})}break}case"LOCATE_ROLE":{try{const{role:e,name:s,all:c}=r,a=H(),u={button:["button",'input[type="button"]','input[type="submit"]','input[type="reset"]','[role="button"]'],link:["a[href]",'[role="link"]'],textbox:["input:not([type])",'input[type="text"]','input[type="email"]','input[type="password"]','input[type="search"]','input[type="tel"]','input[type="url"]',"textarea",'[role="textbox"]'],checkbox:['input[type="checkbox"]','[role="checkbox"]'],radio:['input[type="radio"]','[role="radio"]'],combobox:["select",'[role="combobox"]'],listbox:['[role="listbox"]',"select[multiple]"],option:["option",'[role="option"]'],heading:["h1","h2","h3","h4","h5","h6",'[role="heading"]'],navigation:["nav",'[role="navigation"]'],main:["main",'[role="main"]'],img:["img[alt]",'[role="img"]'],dialog:["dialog",'[role="dialog"]','[role="alertdialog"]'],tab:['[role="tab"]'],tabpanel:['[role="tabpanel"]'],menu:['[role="menu"]'],menuitem:['[role="menuitem"]']}[e]||[`[role="${e}"]`],f=[];for(const o of u)try{f.push(...document.querySelectorAll(o))}catch{}const b=f.filter(o=>{const y=window.getComputedStyle(o);return y.display!=="none"&&y.visibility!=="hidden"&&o.offsetWidth>0&&o.offsetHeight>0});let l=b;if(s){const o=s.toLowerCase();l=b.filter(y=>{var D,R,x,n,d;const v=(D=y.getAttribute("aria-label"))==null?void 0:D.toLowerCase(),A=(R=y.textContent)==null?void 0:R.trim().toLowerCase(),S=(x=y.getAttribute("title"))==null?void 0:x.toLowerCase(),k=(n=y.placeholder)==null?void 0:n.toLowerCase(),L=(d=y.value)==null?void 0:d.toLowerCase();return(v==null?void 0:v.includes(o))||(A==null?void 0:A.includes(o))||(S==null?void 0:S.includes(o))||(k==null?void 0:k.includes(o))||(L==null?void 0:L.includes(o))})}if(l.length===0){i({error:`No element found with role "${e}"${s?` and name "${s}"`:""}`});break}const t=l.map(o=>{var v;const y=q(o,e,s||"");return window.__piRefs=window.__piRefs||{},window.__piRefs[y]=o,a[y]={element:new WeakRef(o),role:e,name:s||""},{ref:y,text:(v=o.textContent)==null?void 0:v.trim().slice(0,50)}});i(c?{matches:t}:{ref:t[0].ref,text:t[0].text})}catch(e){i({error:e instanceof Error?e.message:String(e)})}break}case"LOCATE_TEXT":{try{const{text:e,exact:s}=r,c=H(),a=document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT),w=[];for(;a.nextNode();){const l=a.currentNode.textContent||"";if(s?l.trim()===e:l.toLowerCase().includes(e.toLowerCase())){const o=a.currentNode.parentElement;if(o&&!w.includes(o)){const y=window.getComputedStyle(o);y.display!=="none"&&y.visibility!=="hidden"&&w.push(o)}}}if(w.length===0){i({error:`No element found with text "${e}"`});break}const u=w.sort((l,t)=>{var o,y;return(((o=l.textContent)==null?void 0:o.length)||0)-(((y=t.textContent)==null?void 0:y.length)||0)})[0],f=U(u),b=q(u,f,e);window.__piRefs=window.__piRefs||{},window.__piRefs[b]=u,c[b]={element:new WeakRef(u),role:f,name:e},i({ref:b,text:(m=u.textContent)==null?void 0:m.trim().slice(0,50)})}catch(e){i({error:e instanceof Error?e.message:String(e)})}break}case"LOCATE_LABEL":{try{const{label:e}=r,s=H(),c=document.querySelectorAll("label");let a=null;for(const f of c){const b=(g=f.textContent)==null?void 0:g.trim().toLowerCase();if(b!=null&&b.includes(e.toLowerCase())){const l=f.getAttribute("for");if(l&&(a=document.getElementById(l)),a||(a=f.querySelector("input, select, textarea")),a)break}}if(!a){const f=e.toLowerCase();a=document.querySelector(`input[aria-label*="${e}" i], input[placeholder*="${e}" i], textarea[aria-label*="${e}" i], textarea[placeholder*="${e}" i], select[aria-label*="${e}" i]`)}if(!a){i({error:`No form field found with label "${e}"`});break}const w=U(a),u=q(a,w,e);window.__piRefs=window.__piRefs||{},window.__piRefs[u]=a,s[u]={element:new WeakRef(a),role:w,name:e},i({ref:u,label:e})}catch(e){i({error:e instanceof Error?e.message:String(e)})}break}case"GET_ELEMENT_TEXT":{try{const{ref:e}=r,s=H(),c=s[e];let a;if(c&&(a=c.element.deref(),a||delete s[e]),!a&&window.__piRefs&&(a=window.__piRefs[e]),!a){i({error:`Element ${e} not found`});break}i({text:((p=a.textContent)==null?void 0:p.trim())||""})}catch(e){i({error:e instanceof Error?e.message:String(e)})}break}case"SCROLL_TO_ELEMENT":{const e=oe(r.ref);i(e);break}case"UPLOAD_IMAGE":{const e=se(r.base64,r.ref,r.coordinate,r.filename);i(e);break}case"WAIT_FOR_ELEMENT":{const{selector:e,state:s="visible",timeout:c=2e4}=r,a=Math.min(c,6e4),w=l=>{if(!l)return!1;const t=window.getComputedStyle(l);return t.display!=="none"&&t.visibility!=="hidden"&&t.opacity!=="0"&&l.offsetWidth>0&&l.offsetHeight>0},u=()=>{const l=document.querySelector(e);switch(s){case"attached":return!!l;case"detached":return!l;case"hidden":return!l||!w(l);case"visible":default:return w(l)}},f=Date.now();return new Promise(l=>{if(u()){l({success:!0,waited:Date.now()-f});return}const t=new MutationObserver(()=>{u()&&(t.disconnect(),clearTimeout(o),l({success:!0,waited:Date.now()-f}))}),o=setTimeout(()=>{t.disconnect(),l({success:!1,waited:Date.now()-f,error:`Timeout waiting for "${e}" to be ${s}`})},a);t.observe(document.documentElement,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["style","class","hidden","disabled"]})}).then(l=>{if(!l.success){i({error:l.error,waited:l.waited,pageContent:"",viewport:{width:window.innerWidth,height:window.innerHeight}});return}const t=B("interactive",15,void 0,!0);i({...t,waited:l.waited})}),!0}case"WAIT_FOR_URL":{const{pattern:e,timeout:s=2e4}=r,c=Math.min(s,6e4),a=f=>{if(e.includes("*")){const b=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,"<<<GLOBSTAR>>>").replace(/\*/g,"[^/]*").replace(/<<<GLOBSTAR>>>/g,".*");return new RegExp(`^${b}$`).test(f)}return f.includes(e)},w=Date.now();return new Promise(f=>{if(a(window.location.href)){f({success:!0,waited:Date.now()-w});return}let b=!1;const l=()=>{b||a(window.location.href)&&(b=!0,clearInterval(t),clearTimeout(o),window.removeEventListener("popstate",l),window.removeEventListener("hashchange",l),f({success:!0,waited:Date.now()-w}))},t=setInterval(l,100),o=setTimeout(()=>{b||(b=!0,clearInterval(t),window.removeEventListener("popstate",l),window.removeEventListener("hashchange",l),f({success:!1,waited:Date.now()-w,error:`Timeout waiting for URL to match "${e}". Current: ${window.location.href}`}))},c);window.addEventListener("popstate",l),window.addEventListener("hashchange",l)}).then(f=>{if(!f.success){i({error:f.error,waited:f.waited,pageContent:"",viewport:{width:window.innerWidth,height:window.innerHeight}});return}const b=B("interactive",15,void 0,!0);i({...b,waited:f.waited})}),!0}case"WAIT_FOR_DOM_STABLE":{const{stable:e=100,timeout:s=5e3}=r,c=Math.min(s,3e4),a=Date.now();return new Promise(u=>{let f=Date.now(),b=!1;const l=()=>{if(b)return;Date.now()-f>=e&&(b=!0,t.disconnect(),clearTimeout(o),clearInterval(y),u({success:!0,waited:Date.now()-a}))},t=new MutationObserver(()=>{f=Date.now()}),o=setTimeout(()=>{b||(b=!0,t.disconnect(),clearInterval(y),u({success:!1,waited:Date.now()-a,error:`Timeout: DOM did not stabilize within ${c}ms`}))},c),y=setInterval(l,Math.max(10,Math.min(50,e/2)));t.observe(document.documentElement,{childList:!0,subtree:!0,attributes:!0,characterData:!0}),l()}).then(u=>{if(!u.success){i({error:u.error,waited:u.waited,pageContent:"",viewport:{width:window.innerWidth,height:window.innerHeight}});return}const f=B("interactive",15,void 0,!0);i({...f,waited:u.waited})}),!0}case"FORM_FILL":{const{data:e}=r;if(!Array.isArray(e))return i({error:"data must be an array of {ref, value} pairs"}),!0;const s=H(),c=[];for(const w of e){const{ref:u,value:f}=w;if(!u){c.push({ref:u||"unknown",success:!1,error:"Missing ref"});continue}const b=s[u];if(!b){c.push({ref:u,success:!1,error:"Element not found (run page.read first)"});continue}const l=b.element.deref();if(!l){delete s[u],c.push({ref:u,success:!1,error:"Element no longer exists"});continue}try{if(l instanceof HTMLInputElement){const t=l.type.toLowerCase();if(t==="checkbox"||t==="radio"){const o=f===!0||f==="true"||f==="1"||f==="checked";l.checked=o,l.dispatchEvent(new Event("change",{bubbles:!0}))}else l.focus(),l.value=String(f),l.dispatchEvent(new Event("input",{bubbles:!0})),l.dispatchEvent(new Event("change",{bubbles:!0}));c.push({ref:u,success:!0})}else l instanceof HTMLTextAreaElement?(l.focus(),l.value=String(f),l.dispatchEvent(new Event("input",{bubbles:!0})),l.dispatchEvent(new Event("change",{bubbles:!0})),c.push({ref:u,success:!0})):l instanceof HTMLSelectElement?(l.value=String(f),l.dispatchEvent(new Event("change",{bubbles:!0})),c.push({ref:u,success:!0})):l.isContentEditable?(l.focus(),l.textContent=String(f),l.dispatchEvent(new Event("input",{bubbles:!0})),c.push({ref:u,success:!0})):c.push({ref:u,success:!1,error:"Element is not fillable"})}catch(t){c.push({ref:u,success:!1,error:t instanceof Error?t.message:String(t)})}}const a=c.filter(w=>!w.success);return i({success:a.length===0,filled:c.filter(w=>w.success).length,failed:a.length,results:c}),!0}case"GET_FILE_INPUT_SELECTOR":{const{ref:e}=r;if(!e)return i({error:"No ref provided"}),!0;const s=H(),c=s[e];if(!c)return i({error:"Element not found (run page.read first)"}),!0;const a=c.element.deref();if(!a)return delete s[e],i({error:"Element no longer exists"}),!0;if(!(a instanceof HTMLInputElement)||a.type!=="file")return i({error:"Element is not a file input"}),!0;const w=`__pi_file_${Date.now()}`;return a.setAttribute("data-pi-file-id",w),i({selector:`[data-pi-file-id="${w}"]`}),!0}case"WAIT_FOR_NETWORK_IDLE":{const{timeout:e=1e4}=r,s=Math.min(e,6e4),c=["doubleclick.net","googlesyndication.com","googletagmanager.com","google-analytics.com","facebook.net","connect.facebook.net","analytics","ads","tracking","pixel","hotjar.com","clarity.ms","mixpanel.com","segment.com","newrelic.com","nr-data.net","/tracker/","/collector/","/beacon/","/telemetry/","/log/","/events/","/track.","/metrics/"],a=["img","image","font","icon"],w=t=>c.some(o=>t.includes(o)),u=t=>{const o=t.initiatorType||"unknown";return!!(a.includes(o)||/\.(jpg|jpeg|png|gif|webp|svg|ico|woff|woff2|ttf|eot)(\?|$)/i.test(t.name))},f=()=>{const t=performance.now();return performance.getEntriesByType("resource").filter(y=>{if(y.responseEnd!==0||y.name.startsWith("data:")||y.name.length>500||w(y.name))return!1;const v=t-y.startTime;return!(v>1e4||u(y)&&v>3e3)})},b=Date.now();return new Promise(t=>{const o=()=>{const y=f(),v=Date.now()-b;if(y.length===0){t({success:!0,waited:v});return}if(v>=s){t({success:!1,waited:v,pendingCount:y.length});return}setTimeout(o,100)};o()}).then(t=>{if(!t.success){i({error:`Network not idle after ${t.waited}ms (${t.pendingCount} requests pending)`,waited:t.waited,pageContent:"",viewport:{width:window.innerWidth,height:window.innerHeight}});return}const o=B("interactive",15,void 0,!0);i({...o,waited:t.waited})}),!0}case"SEARCH_PAGE":{const{term:e,caseSensitive:s,limit:c}=r,a=ce(e,s||!1,c||10);i({query:e,count:a.length,matches:a});break}case"GET_ELEMENT_BOUNDS_FOR_ANNOTATION":{const e=H(),s=[];for(const[c,a]of Object.entries(e)){const w=a.element.deref();if(!w)continue;const u=w.getBoundingClientRect();u.width<=0||u.height<=0||u.bottom<0||u.top>window.innerHeight||u.right<0||u.left>window.innerWidth||s.push({ref:c,tag:w.tagName.toLowerCase(),bounds:{x:u.x,y:u.y,width:u.width,height:u.height}})}i({elements:s});break}default:return!1}return!1});function ce(r,h,i){const m=[],g=h?r:r.toLowerCase(),p=document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT),e=H();let s=0;for(;p.nextNode()&&m.length<i;){const c=p.currentNode,a=c.textContent||"",w=h?a:a.toLowerCase();let u=0;for(;(u=w.indexOf(g,u))!==-1&&m.length<i;){const f=c.parentElement;if(!f){u++;continue}const b=document.createRange();b.setStart(c,u),b.setEnd(c,Math.min(u+r.length,a.length));const l=b.getBoundingClientRect();if(l.width===0||l.height===0){u++;continue}const t=c.textContent||"",o=Math.max(0,u-30),y=Math.min(t.length,u+r.length+30),v=t.slice(o,y).trim();let A=null;for(const[S,k]of Object.entries(e)){const L=k.element.deref();if(L&&(L===f||L.contains(f))){A=S;break}}m.push({ref:`m${++s}`,text:t.slice(u,u+r.length),context:v,bounds:{x:Math.round(l.x),y:Math.round(l.y),width:Math.round(l.width),height:Math.round(l.height)},elementRef:A}),u++}}return m}
11
+ //# sourceMappingURL=accessibility-tree.js.map