@gaiventures/pilotfish-mcp 0.1.9 → 0.1.10

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.
Files changed (3) hide show
  1. package/README.md +3 -2
  2. package/cli.js +5 -5
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -105,8 +105,9 @@ one it re-reads (a prompt-cache write against a read). So an action on a page th
105
105
  under its unchanged parent line marked `~ `, with what is gone listed by ref. Refs are stable across snapshots, so the
106
106
  lines not repeated still work; a change of more than half of the screen gets the whole screen, and
107
107
  `browser_snapshot` always does. The same goes for what the agent reads before any page: each tool's use is said once,
108
- in its own description, and the server instructions keep only the two rules that span the tools (a "cheapest",
109
- "latest" or "most" is the site's own sort; what to do on `NEEDS A PERSON`).
108
+ in its own description, with the rules in the description of the tool they are used with (a "cheapest", "latest" or
109
+ "most" is the site's own sort: `browser_navigate`; what to do on `NEEDS A PERSON`: `browser_resume`). Server
110
+ instructions are sent only for the settings that need them (secrets, allowed hosts).
110
111
 
111
112
  **Pages only a person can pass.** After every acting tool the page is classified. A sign-in with no credentials, a
112
113
  one-time code or 2FA prompt, a device approval, a sign-in link, a passkey, 3-D Secure or a CAPTCHA adds a
package/cli.js CHANGED
@@ -108,7 +108,7 @@ var hr=Object.defineProperty;var l=(n,e)=>hr(n,"name",{value:e,configurable:!0})
108
108
  }
109
109
  return true;
110
110
  }`,Zr=4e3;function ea(n){let e=(n??Math.floor(Math.random()*4294967296))>>>0,t=l(()=>{e=e+1831565813>>>0;let s=e;return s=Math.imul(s^s>>>15,s|1),s^=s+Math.imul(s^s>>>7,s|61),((s^s>>>14)>>>0)/4294967296},"rng");return{speed:Z(.85,1.2,t),dwellMs:Z(60,160,t),restMs:Z(500,1400,t),driftPx:Z(2,6,t)}}l(ea,"motorProfile");function Ln(n,e,{targetWidth:t=40,viewport:s,rng:o=Math.random,speed:i=1}={}){let r={x:Math.round(e.x),y:Math.round(e.y)},a=r.x-n.x,c=r.y-n.y,d=Math.hypot(a,c);if(d<2)return[{...r,delayMs:0}];let u=Math.log2(d/Math.max(4,t)+1),h=Math.min(1e3,Math.max(120,(150+110*u)*i*Z(.85,1.15,o))),p=a/d,f=c/d,b=d>80,k=o()>=.35,m=b?k?Math.min(25,d*Z(.02,.06,o),Math.max(2,t*.4)):-Math.min(25,d*Z(.02,.06,o)):0,y=b?Math.max(-t*.3,Math.min(t*.3,Z(-.015,.015,o)*d)):0,v={x:r.x+p*m-f*y,y:r.y+f*m+p*y},_=b?h*Z(.72,.85,o):h,E=Z(-.2,.2,o)*Math.min(d,600),D={x:n.x+a*.3-f*E,y:n.y+c*.3+p*E},M={x:n.x+a*.75-f*E*.5,y:n.y+c*.75+p*E*.5},x=l(g=>{let C=Math.pow(g,.8);return C*C*C*(10-15*C+6*C*C)},"jerk"),T=l(g=>{if(g<_){let A=x(g/_),P=1-A;return{x:P*P*P*n.x+3*P*P*A*D.x+3*P*A*A*M.x+A*A*A*v.x,y:P*P*P*n.y+3*P*P*A*D.y+3*P*A*A*M.y+A*A*A*v.y}}let C=x(Math.min(1,(g-_)/Math.max(1,h-_)));return{x:v.x+(r.x-v.x)*C,y:v.y+(r.y-v.y)*C}},"at"),w=s?s.width-1:1/0,S=s?s.height-1:1/0,I=Z(8,16,o),$=[];for(let g=Math.round(I);g<h;g+=Math.max(6,Math.round(I+Z(-1,1,o)))){let C=T(g),A={x:Math.min(w,Math.max(0,Math.round(C.x))),y:Math.min(S,Math.max(0,Math.round(C.y)))},P=$.at(-1);(!P||P.x!==A.x||P.y!==A.y)&&$.push({...A,t:g})}return(!$.length||$.at(-1).x!==r.x||$.at(-1).y!==r.y)&&$.push({...r,t:h}),$.map((g,C)=>({x:g.x,y:g.y,delayMs:C+1<$.length?Math.max(1,Math.round($[C+1].t-g.t)):0}))}l(Ln,"humanPath");function Fn(n,e){let t=Math.min(e.width*.15,5),s=Math.min(e.height*.15,5);return{x:Math.round(n.x+Z(-t,t)),y:Math.round(n.y+Z(-s,s))}}l(Fn,"nearCentre");function Hn(n,e=Math.random,t="windows"){if(!n)return[];let s=Math.sign(n);if(t==="windows")return Array.from({length:Math.max(1,Math.round(Math.abs(n)/100))},()=>s*100);let o=Z(.86,.92,e),i=[],r=Math.abs(n),a=r*(1-o);for(;r>=1;){let c=Math.min(r,Math.max(1,Math.round(a*10)/10));i.push(s*c),r=Math.round((r-c)*10)/10,a*=o}return r>0&&(i.length?i[i.length-1]=Math.round((i[i.length-1]+s*r)*10)/10:i.push(s*r)),i}l(Hn,"wheelTicks");function St(n){try{return new URL(n).hostname}catch{return""}}l(St,"safeHost");function H(n){return new Promise(e=>setTimeout(e,n))}l(H,"sleep");var oa=["challenges.cloudflare.com","www.recaptcha.net","recaptcha.net","www.google.com","www.gstatic.com","*.awswaf.com","*.geetest.com","*.geevisit.com","*.px-cloud.net","*.px-cdn.net","*.px-client.net","*.perimeterx.net","*.pxchk.net","*.humansecurity.com","*.captcha-delivery.com","*.datadome.co","*.hcaptcha.com","*.arkoselabs.com","*.funcaptcha.com"];function zn(n){let e=n.toLowerCase().replace(/\.$/,"");return oa.some(t=>t.startsWith("*.")?e===t.slice(2)||e.endsWith(t.slice(1)):e===t)}l(zn,"isCheckServiceHost");function _e(n){let e=n.trim();if(e.includes("://"))try{let s=new URL(e);return s.protocol==="http:"||s.protocol==="https:"?L(s.hostname):""}catch{return""}let t=/^\[?([a-z0-9.:-]+?)\]?(?::\d+)?$/i.exec(e)?.[1];return t&&t.includes(".")?L(t.toLowerCase()):t==="localhost"?"localhost":""}l(_e,"siteOfTarget");var Tt=class{static{l(this,"Browser")}connection;launched;options;pages=new Map;emitter=new na().setMaxListeners(500);downloadsByGuid=new Map;closedReason=null;cleanedUp=!1;cleanups=[];contextForwarders=new Map;viewport;sessionInitializers=[];initializing=new Set;addSessionInitializer(e){this.sessionInitializers.push(e);for(let t of this.pages.values())for(let s of t.frames()){let o=e(s).catch(()=>{}).finally(()=>this.initializing.delete(o));this.initializing.add(o)}return()=>{let t=this.sessionInitializers.indexOf(e);t>=0&&this.sessionInitializers.splice(t,1)}}async initialized(){for(;this.initializing.size;)await Promise.all([...this.initializing])}constructor(e,t,s){this.connection=e,this.launched=t,this.options=s,this.viewport=s.viewport??{width:1280,height:800},e.onClose(o=>{this.closedReason=o,this.clockTimer&&clearTimeout(this.clockTimer);for(let i of this.pages.values())i._markClosed(o);this.pages.clear(),this.emitter.emit("disconnected",o)}),this.addSessionInitializer(o=>this.applyClock(o.session)),jn(this,async o=>{let i=o.targetInfo?.type;i!=="shared_worker"&&i!=="service_worker"||(this.clockWorkers.add(o),this.applyClock(o))})}clockWorkers=new Set;async applyClock(e){let t=this.options.timezoneId,s=this.options.locale,o={timeoutMs:5e3};await Promise.all([t?e.send("Emulation.setTimezoneOverride",{timezoneId:t},o).catch(()=>{}):void 0,s?e.send("Emulation.setLocaleOverride",{locale:s},o).catch(()=>{}):void 0])}clockTimer=null;reapplyClock(){let e=l(()=>[...this.allPages().flatMap(s=>s.frames().map(o=>o.session)),...[...this.clockWorkers].filter(s=>!s.closed)],"sessions");return Promise.all(e().map(s=>this.applyClock(s))).then(()=>Promise.all(e().map(s=>this.applyClock(s)))).then(()=>{})}_clockOwnerMayHaveLeft(){this.clockTimer||this.closed||(this.clockTimer=setTimeout(()=>{this.clockTimer=null,this.closed||this.reapplyClock()},100),this.clockTimer.unref?.())}get downloadsDir(){return this.options.downloadsPath??(this.launched?qn(this.launched.userDataDir,"downloads"):void 0)}downloadsSince(e){return[...this.downloadsByGuid.values()].filter(t=>t.startedAt>=e)}get root(){return this.connection.rootSession}get closed(){return this.closedReason!==null}on(e,t){return this.emitter.on(e,t),()=>this.emitter.off(e,t)}async _initialize(){this.connection.onSession((o,i)=>{i==="attached"?this.onAttached(o):i==="detached"&&this.clockWorkers.delete(o)&&this._clockOwnerMayHaveLeft()}),this.root.on("Target.targetDestroyed",o=>{let i=this.pages.get(o.targetId);i&&(this.pages.delete(o.targetId),i._markClosed("target destroyed"),this.emitter.emit("pageClosed",i),this._clockOwnerMayHaveLeft())}),this.root.on("Target.targetCrashed",o=>{this.pages.get(o.targetId)?._markCrashed(`${o.status} (${o.errorCode})`)}),this.root.on("Browser.downloadWillBegin",o=>{let i={guid:o.guid,url:o.url,suggestedFilename:o.suggestedFilename,frameId:o.frameId,state:"inProgress",receivedBytes:0,totalBytes:0,startedAt:Date.now(),path:this.downloadsDir?qn(this.downloadsDir,o.guid):void 0};this.downloadsByGuid.set(o.guid,i),this.emitter.emit("download",i)}),this.root.on("Browser.downloadProgress",o=>{let i=this.downloadsByGuid.get(o.guid);i&&(i.state=o.state,i.receivedBytes=o.receivedBytes,i.totalBytes=o.totalBytes,o.state!=="inProgress"&&this.emitter.emit("download",i))}),await this.root.send("Target.setDiscoverTargets",{discover:!0}),await this.root.send("Target.setAutoAttach",{autoAttach:!0,waitForDebuggerOnStart:!0,flatten:!0});let e=this.downloadsDir;if(e){try{ta(e,{recursive:!0})}catch{}await this.root.send("Browser.setDownloadBehavior",{behavior:"allowAndName",downloadPath:e,eventsEnabled:!0}).catch(()=>{})}let t=l(o=>Promise.race([o.whenReady,new Promise(i=>setTimeout(i,15e3).unref?.())]),"ready"),s=this.allPages()[0];s?await t(s):await new Promise(o=>{let i=setTimeout(o,15e3);i.unref?.();let r=this.on("page",a=>{r(),clearTimeout(i),t(a).then(o)})})}onAttached(e){let t=e.targetInfo;if(t&&e.parentSessionId===void 0){if(t.type==="page"){let s=new at(this,e,t);this.pages.set(t.targetId,s),s._initialize().then(()=>this.emitter.emit("page",s),o=>{this.pages.delete(t.targetId),s._markClosed(`init failed: ${o.message}`)});return}if(t.type!=="iframe"){if(t.type==="shared_worker"||t.type==="service_worker"){$t(this,e);return}e.send("Runtime.runIfWaitingForDebugger").catch(()=>{})}}}allPages(){return[...this.pages.values()].filter(e=>!e.closed)}async newPage(e={}){let{targetId:t}=await this.root.send("Target.createTarget",{url:e.url??"about:blank",browserContextId:e.browserContextId,...e.background?{background:!0}:{}});return this.waitForPage(t)}async waitForPage(e,t=15e3){let s=this.pages.get(e);return s?.ready?s:new Promise((o,i)=>{let r=setTimeout(()=>{a(),i(new Error(`Page ${e} did not initialize within ${t}ms`))},t),a=this.on("page",c=>{c.targetId===e&&(clearTimeout(r),a(),o(c))})})}async createContext(e={}){let t=e.proxyServer&&st(e.proxyServer)?await nt(e.proxyServer):null;try{let{browserContextId:s}=await this.root.send("Target.createBrowserContext",{disposeOnDetach:!0,proxyServer:t?.url??e.proxyServer,...e.proxyServer?{proxyBypassList:[...tt,...e.proxyBypassList?[e.proxyBypassList]:[]].join(";")}:e.proxyBypassList?{proxyBypassList:e.proxyBypassList}:{}});return t&&(this.contextForwarders.set(s,t),this.gate(t)),s}catch(s){throw await t?.close(),s}}async disposeContext(e){await this.root.send("Target.disposeBrowserContext",{browserContextId:e}),await this.contextForwarders.get(e)?.close(),this.contextForwarders.delete(e)}addCleanup(e){this.cleanups.push(e)}byteCounters=[];addByteCounter(e){this.byteCounters.push(e)}useForwarder(e){this.addCleanup(()=>e.close()),this.addByteCounter(()=>e.bytes()),this.gate(e),this.launchForwarder=e}gated=new Set;launchForwarder=null;gate(e){this.gated.add(e),e.onData=()=>this.checkTraffic(),e.route=t=>this.proxiedHost(t),e.onUpstreamDown=t=>this.emitter.emit("upstreamDown",t),e.onTunnelFailed=t=>this.emitter.emit("tunnelFailed",t),e.onRefused=t=>this.refusedTunnel(t),this.holding&&e.hold()}refused=new Set;refusedPolicy=null;setRefusedPolicy(e){this.refusedPolicy=e}refusedTunnel(e){let t=L(e.host)||e.host.toLowerCase();if(this.refused.has(t))return!0;let s=this.refusedPolicy?.(t,e.host)==="direct";return s&&this.leaveExit(t),this.emitter.emit("proxyRefused",{host:e.host,site:t,answer:e.answer,direct:s}),s}routeDirect(e){let t=_e(e);t&&this.leaveExit(t)}leaveExit(e){this.refused.add(e),this.routing?.sites.delete(e);for(let t of this.gated)t.dropProxied(s=>(L(s)||s.toLowerCase())===e)}get paidProxy(){return this.gated.size>0}routing=null;offDocuments=null;never=new Set;pinned=new Map;get exitCountry(){return this.options.region?.country?.toLowerCase()??null}offExit(e){if(this.never.has(e)||this.refused.has(e))return!0;if(!this.pinned.has(e))return!1;let t=this.pinned.get(e);return!t||t!==this.exitCountry}pinnedHere(e){let t=_e(e);return!!t&&this.pinned.has(t)&&!this.offExit(t)}routable(e){let t=_e(e);return!!t&&!this.offExit(t)}setProxyRouting(e){e?.never&&(this.never=new Set(e.never.map(_e).filter(Boolean))),this.routing=e?{sites:new Set((e.sites??[]).map(_e).filter(t=>t&&!this.offExit(t))),documents:e.documents??"none",directOnly:!1}:null,this.routing&&(this.routing.documents!=="none"||this._exit?.looked===!1||[...this.pinned.keys()].some(t=>!this.offExit(t)))&&this.watchDocuments();for(let t of this.gated)t.dropDirect(s=>this.proxiedHost(s)),t.dropProxied(s=>!this.proxiedHost(s));(!this.routing||this.routing.sites.size>0)&&this.lookUpExit()}watchDocuments(){this.offDocuments||(this.offDocuments=this.addSessionInitializer(async e=>{let t=new Set;await ke.for(e.session).add({types:["Document"],onRequest:l(async o=>{let i=this.routing?.documents,r=e.isMain&&o.frameId===e.page.targetId;be(o.request.url)||!!o.networkId&&t.has(o.networkId)||!!o.redirectedRequestId&&t.has(o.redirectedRequestId)?(o.networkId&&t.add(o.networkId),t.add(o.requestId)):(i==="all"||i==="main"&&r||i==="frames"&&!r||this.pinnedHere(o.request.url))&&this.routeSite(o.request.url),this.exitLookup&&this.proxiedUrl(o.request.url)&&await this.exitLookup},"onRequest")}).enabled}))}pinSite(e,t){let s=_e(e);if(s&&(this.pinned.set(s,t?.toLowerCase()||null),!!this.routing)){if(!this.offExit(s))return this.watchDocuments();if(this.routing.sites.delete(s))for(let o of this.gated)o.dropProxied(i=>L(i)===s)}}proxiedHost(e){if(!this.paidProxy||this.refused.size&&this.refused.has(L(e)||e.toLowerCase()))return!1;let t=this.routing;return t?t.directOnly?!1:t.sites.has(L(e))||t.sites.size>0&&zn(e):!0}proxiedUrl(e){try{let t=new URL(e);return(t.protocol==="http:"||t.protocol==="https:"||t.protocol==="ws:"||t.protocol==="wss:")&&this.proxiedHost(t.hostname)}catch{return!1}}get proxiedSites(){return this.routing?[...this.routing.sites]:null}routeSite(e){if(!this.routing||this.routing.directOnly)return;let t=_e(e);if(!t||this.routing.sites.has(t)||this.offExit(t))return;let s=this.routing.sites.size===0;this.routing.sites.add(t);for(let o of this.gated)o.dropDirect(i=>L(i)===t||s&&zn(i));this.lookUpExit()}setDirectOnly(){this.routing={sites:new Set,documents:this.routing?.documents??"none",directOnly:!0};for(let e of this.gated)e.dropProxied()}_exit=null;exitLookup=null;exitCoords=null;offGeolocation=null;rotateExit(){let e=this._exit,t=this.launchForwarder;if(!e?.reroll||!t)return!1;let s=e.reroll();if(e.upstream=s.url,t.setUpstream(s.url),this.options.region){let{exitOs:o,...i}=this.options.region;this.options.region={...i,session:s.session}}return t.dropProxied(),e.looked=!1,e.picked=!1,(!this.routing||this.routing.sites.size>0)&&this.lookUpExit(),!0}exitReady(){return this.exitApplied??Promise.resolve()}exitApplied=null;lookUpExit(){let e=this._exit,t=!!e?.pick&&!e.picked;if(!e||e.looked||!e.lookup&&!t)return this.exitLookup;e.looked=!0;let s=Promise.resolve(),i=l(async()=>{if(!t||!e.pick)return;e.picked=!0;let r=e.upstream,a=await e.pick(r).catch(()=>null);if(!a||this._exit!==e||e.upstream!==r)return;let c=this.launchForwarder;a.session&&c&&(e.upstream=a.upstream,c.setUpstream(a.upstream),c.dropProxied()),this.options.region&&(this.options.region={...this.options.region,...a.session&&c?{session:a.session}:{},exitOs:a.pick})},"matchOs")().then(()=>{let r=e.upstream;if(e.lookup)return e.lookup(r).then(a=>{a&&this._exit?.upstream===r&&(s=this._applyExitGeo(a,{geolocation:!0}))},a=>{Ne(a)&&this.emitter.emit("upstreamDown","login")})});return this.exitLookup=i,this.exitApplied=i.then(()=>s),i}async _applyExitGeo(e,t){let s=this.options.region;s&&(this.options.region={...s,...e.ip?{exitIp:e.ip}:{},...e.timezone?{exitTimezone:e.timezone}:{},...e.city?{exitCity:e.city}:{},...e.latitude!==void 0&&e.longitude!==void 0?{exitLat:e.latitude,exitLong:e.longitude}:{}});let o=[];if(e.timezone&&e.timezone!==this.options.timezoneId&&!this.options.keepTimezone&&(this.options.timezoneId=e.timezone,o.push(this.reapplyClock())),t.geolocation&&this.options.stealth&&e.latitude!==void 0&&e.longitude!==void 0){let i={latitude:e.latitude,longitude:e.longitude};this.exitCoords=i;let r=l(a=>a.send("Emulation.setGeolocationOverride",{...i,accuracy:50}).catch(()=>{}),"apply");this.offGeolocation||(this.offGeolocation=this.addSessionInitializer(async a=>{this.exitCoords&&await r(a.session)}));for(let a of this.allPages())for(let c of a.frames())o.push(r(c.session))}await Promise.all(o)}limit=null;softFired=!1;hardFired=!1;holding=!1;challenges=new Set;suspended=null;allowanceUsed=0;lastUsed=0;setTrafficLimit(e,t=()=>{}){this.limit=typeof e=="number"?{softBytes:1/0,hardBytes:e,challengeAllowanceBytes:0,onHard:t}:e,this.softFired=!1,this.hardFired=!1,this.allowanceUsed=0,this.lastUsed=this.proxyBytes(),this.setHeld(!1,!0),this.checkTraffic()}challengeActive(e,t){let s=_e(e)||e.toLowerCase();t?this.challenges.add(s):this.challenges.delete(s),this.checkTraffic()}suspendHold(e){this.suspended=e||"suspended",this.checkTraffic()}resumeHold(){this.suspended=null,this.checkTraffic()}isHeld(){return this.holding}setHeld(e,t=!1){if(!(e===this.holding&&!t)){this.holding=e;for(let s of this.gated)e?s.hold():s.release()}}checkTraffic(){let e=this.proxyBytes(),t=Math.max(0,e-this.lastUsed);this.lastUsed=e;let s=this.limit;if(!s||(!this.softFired&&e>=s.softBytes&&(this.softFired=!0,s.onSoft?.(e)),e<s.hardBytes))return;let o=this.challenges.size>0,i=o||!!this.suspended;if(i&&(this.allowanceUsed+=Math.min(t,e-s.hardBytes)),!(this.hardFired&&this.holding&&!o||!i||this.allowanceUsed>s.challengeAllowanceBytes))return this.setHeld(!1);this.setHeld(!0),!this.hardFired&&(this.hardFired=!0,s.onHard?.(e))}async cancelDownload(e){await this.root.send("Browser.cancelDownload",{guid:e}).catch(()=>{})}proxyBytes(){let e=0;for(let t of this.byteCounters)e+=t();for(let t of this.contextForwarders.values())e+=t.bytes();return e}proxyBytesByHost(){let e=new Map;for(let t of[...this.launchForwarder?[this.launchForwarder]:[],...this.contextForwarders.values()])for(let[s,o]of t.bytesByHost())e.set(s,(e.get(s)??0)+o);return e}exitProxyUrl(){return this.launchForwarder?.url??null}async version(){return this.root.send("Browser.getVersion")}async close(){if(this.cleanedUp)return;if(this.cleanedUp=!0,!this.closedReason)try{await Promise.race([this.root.send("Browser.close",void 0,{timeoutMs:3e3}),new Promise(t=>setTimeout(t,3e3))])}catch{}this.connection.close("browser closed");let e=this.launched?.process;e&&e.exitCode===null&&await new Promise(t=>{let s=setTimeout(()=>{try{e.kill("SIGKILL")}catch{}t()},3e3);e.once("exit",()=>{clearTimeout(s),t()})});for(let t of this.contextForwarders.values())await t.close().catch(()=>{});this.contextForwarders.clear();for(let t of this.cleanups.splice(0))await Promise.resolve(t()).catch(()=>{});this.launched?.ownsUserDataDir&&await sa(this.launched.userDataDir,{recursive:!0,force:!0,maxRetries:10,retryDelay:200}).catch(()=>{})}};var Wn=l(n=>{throw new Error(n+" is not part of the open pilotfish MCP (the hosted service only)")},"refuse");function Gn(){return Wn("buildUserAgent")}l(Gn,"buildUserAgent");function Vn(){return Wn("stealthChromePath")}l(Vn,"stealthChromePath");import{existsSync as Kn,mkdirSync as ia,readFileSync as Jn,writeFileSync as Yn}from"node:fs";import{join as It}from"node:path";var ra={net:{network_prediction_options:2}},ms="pilotfish-model-manifest.json";function aa(n,e){let t="";for(let s of n)s.startsWith("--profile-directory=")&&(t=s.slice(20).replace(/^"|"$/g,"")||t);if(t)return t;try{let s=e?JSON.parse(Jn(It(e,"Local State"),"utf8"))?.profile?.last_used:void 0;if(typeof s=="string"&&s&&!/[\\/]|^\.\.?$|^System Profile$/.test(s))return s}catch{}return"Default"}l(aa,"profileDirectory");function fs(n,e){let t={...n};for(let[s,o]of Object.entries(e)){let i=t[s];t[s]=o&&typeof o=="object"&&!Array.isArray(o)?fs(i&&typeof i=="object"&&!Array.isArray(i)?i:{},o):o}return t}l(fs,"merged");function Xn(n,e=[],t){try{let s=It(n,ms);Kn(s)||Yn(s,`{}
111
- `);let o=It(n,aa(e,n)),i=It(o,"Preferences"),r={};if(Kn(i)){let c=JSON.parse(Jn(i,"utf8"));if(!c||typeof c!="object"||Array.isArray(c))return!1;r=c}else ia(o,{recursive:!0});let a=fs(fs(r,ra),t??{});return JSON.stringify(a)!==JSON.stringify(r)&&Yn(i,JSON.stringify(a)),!0}catch{return!1}}l(Xn,"seedProfile");var Qn=l(n=>{throw new Error(n+" is not part of the open pilotfish MCP (the hosted service only)")},"refuse");function Zn(){return Qn("osFontConfig")}l(Zn,"osFontConfig");function eo(){return Qn("osFontPrefs")}l(eo,"osFontPrefs");function ma(n,e){return n==="darwin"?["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"]:n==="linux"?["/usr/bin/google-chrome","/usr/bin/google-chrome-stable","/opt/google/chrome/chrome"]:["C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe","C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",...e.LOCALAPPDATA?[he(e.LOCALAPPDATA,"Google","Chrome","Application","chrome.exe")]:[]]}l(ma,"googleChromeCandidates");function ga(n){return n==="darwin"?["/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary","/Applications/Chromium.app/Contents/MacOS/Chromium","/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge","/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"]:n==="linux"?["/usr/bin/chromium","/usr/bin/chromium-browser","/usr/bin/microsoft-edge"]:["C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe"]}l(ga,"otherChromiumCandidates");function wa(n,e,t){let s=[e.PLAYWRIGHT_BROWSERS_PATH,n==="darwin"?he(to(),"Library/Caches/ms-playwright"):void 0,n==="linux"?he(to(),".cache/ms-playwright"):void 0,n==="win32"?he(e.LOCALAPPDATA??"","ms-playwright"):void 0].filter(i=>!!i&&t(i)),o=[];for(let i of s){let r=[];try{r=oo(i)}catch{continue}let a=r.filter(c=>/^chromium-\d+$/.test(c)).sort().reverse();for(let c of a){let d=he(i,c);o.push(he(d,"chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing"),he(d,"chrome-mac/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing"),he(d,"chrome-linux/chrome"),he(d,"chrome-linux64/chrome"),he(d,"chrome-win/chrome.exe"),he(d,"chrome-win64/chrome.exe"))}}return o}l(wa,"playwrightChromiumCandidates");function io(n={}){let e=n.env??process.env,t=n.exists??no,s=n.os??ct(),o=e.PILOTFISH_CHROME;if(o&&t(o))return o;for(let i of[...ma(s,e),...wa(s,e,t),...ga(s)])if(t(i))return i}l(io,"findChromium");function ro(n,e){let{width:t,height:s}=n.viewport??{width:1280,height:800},o=["--remote-debugging-pipe",`--user-data-dir=${e}`,`--window-size=${t},${s}`,"--no-first-run","--no-default-browser-check","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-renderer-backgrounding","--disable-breakpad","--disable-component-update","--disable-default-apps","--disable-domain-reliability","--disable-sync","--disable-client-side-phishing-detection","--disable-hang-monitor","--disable-popup-blocking","--disable-prompt-on-repost","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--metrics-recording-only","--password-store=basic","--use-mock-keychain","--mute-audio","--disable-infobars","--disable-blink-features=AutomationControlled",`--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,DestroyProfileOnBrowserClose,HttpsUpgrades,PrivacySandboxSettings4,LensOverlay,AimEnabled,SafeBrowsingHashPrefixRealTimeLookups,NetworkTimeServiceQuerying${n.stealth?",IsolateSandboxedIframes":""}`,"--enable-features=NetworkService,NetworkServiceInProcess2",`--optimization-guide-manifest-override=${he(e,ms)}`];n.headless!==!1?o.push("--headless=new",...n.stealth?[]:["--hide-scrollbars"]):o.push("--test-type"),ct()==="linux"&&o.push("--disable-dev-shm-usage"),n.headless===!1&&ct()==="linux"&&o.push("--enable-unsafe-swiftshader"),n.stealth&&n.persona?.webgpu&&ct()==="linux"&&o.push("--enable-unsafe-webgpu","--disable-dawn-features=allow_unsafe_apis"),n.stealth&&n.persona&&ct()==="linux"&&o.push("--use-fake-device-for-media-stream"),n.stealth&&o.push("--force-webrtc-ip-handling-policy=disable_non_proxied_udp","--webrtc-ip-handling-policy=disable_non_proxied_udp"),n.userAgent&&o.push(`--user-agent=${n.userAgent}`),n.proxyServer&&o.push(`--proxy-server=${n.proxyServer}`),n.proxyServer&&o.push(`--proxy-bypass-list=${[...tt,...n.proxyBypass??[]].join(";")}`),n.locale&&o.push(`--lang=${n.locale}`);let i=n.persona?.languages.length?n.persona.languages.join(","):n.locale?va(n.locale):void 0;return i&&o.push(`--accept-lang=${i}`),n.ignoreHTTPSErrors&&o.push("--ignore-certificate-errors"),o.push(...n.args??[]),o.push("about:blank"),o}l(ro,"defaultArgs");var ws=new Map;function ya(n){let e=n.map(s=>/^(\d+)\.(\d+)\.(\d+)\.(\d+)(?:\.manifest)?$/.exec(s)).filter(s=>!!s);e.sort((s,o)=>{for(let i=1;i<=4;i++)if(Number(s[i])!==Number(o[i]))return Number(o[i])-Number(s[i]);return 0});let t=e[0];return t?`${t[1]}.${t[2]}.${t[3]}.${t[4]}`:null}l(ya,"newestVersionName");async function ao(n){let e="";try{let o=ua(n);e=`${o.size}:${o.mtimeMs}`}catch{if(process.platform==="win32"||/[\\/]/.test(n))return null}if(process.platform!=="win32")return new Promise(o=>{ca(n,["--version"],{timeout:1e4,windowsHide:!0},(i,r)=>{o(i?null:/(\d+\.\d+\.\d+\.\d+)/.exec(String(r))?.[1]??null)})});let t=ws.get(n);if(t&&t.stamp===e)return t.version;let s=null;try{s=ya(oo(fa(n)))}catch{s=null}return ws.set(n,{stamp:e,version:s}),s}l(ao,"chromeVersionOf");var so=new Set;function ba(n){so.has(n)||/[\\/]/.test(n)&&!no(n)||(so.add(n),process.emitWarning(`pilotfish: could not read the Chrome version of ${n} before launch: it starts with its own user agent, so the script fetches of service and nested workers, and a service worker's update checks, carry the host's`,{code:"PILOTFISH_UA_VERSION"}))}l(ba,"warnUnreadVersion");function ka(n,e){let t=ws.get(n);t&&(t.version=e)}l(ka,"noteLiveVersion");function va(n){let e=n.split("-")[0];return e&&e!==n?`${n},${e}`:n}l(va,"acceptLanguage");async function gs(n){await ha(n,{recursive:!0,force:!0,maxRetries:10,retryDelay:200}).catch(()=>{})}l(gs,"removeProfile");async function bs(n={}){let e=n,t=n.region?.proxy&&(!n.timezoneId||n.keepTimezone)?vn(n.region.proxy):null,s=e.proxyRouting,o=!!s&&!(s.sites??[]).length&&s.documents!=="all"&&s.documents!=="main",i=n.region?.city&&n.region.proxy&&Sn(n.region.proxy)&&t&&!o?n.region.city:void 0;if(e.region){let N=_n(e.region.country),U=e.region.session??xt(),{city:se,...te}=e.region;e={...e,region:{...te,country:N.country,session:U,...i?{city:i}:{}},timezoneId:e.timezoneId??N.timezoneId,locale:e.locale??N.locale,...e.region.proxy?{proxyServer:ot(e.region.proxy,{country:N.country,session:U,...i?{city:i}:{}})}:{}}}let r=e.proxyServer&&st(e.proxyServer)?e.proxyServer:null,a=r?await nt(r,{allowPrivateDirect:!!e.proxyRouting?.privateNetwork}):null;a&&(e={...e,proxyServer:a.url});let c=e.region?.proxy?{proxy:e.region.proxy,country:e.region.country}:null,d=c?()=>(N=>({url:ot(c.proxy,{country:c.country,session:N,...i?{city:i}:{}}),session:N}))(xt()):null,u=t&&r?N=>xn(N,t,3e3):null,h=l(N=>u(N).then(U=>({geo:U}),U=>({error:U})),"look"),p=l(()=>(i=void 0,r=ot(c.proxy,{country:c.country,session:e.region.session}),a.setUpstream(r),a.dropProxied(),h(r)),"byCountry"),f=null,b=l(()=>{i=void 0;let N=d();return f=N.session,r=N.url,a.setUpstream(r),a.dropProxied(),h(r)},"fresh"),k=l(N=>"error"in N&&!Ne(N.error),"dead"),m=e.region?.matchOs&&c&&a?Tn():null,y=new Map,v=l(N=>{let U=y.get(N);return U||y.set(N,U=$n(N,m)),U},"classifyOnce"),_=l(()=>{let N=xt();return{url:ot(c.proxy,{country:c.country,session:N}),session:N}},"drawByCountry"),E=m&&e.region?.matchOs?(N=>async U=>{let se={},Pe=await In({persona:N,current:U,draw:_,classify:l(async wt=>{let W=await v(wt),$e=W.os??W.why??"none";return se[$e]=(se[$e]??0)+1,W.os},"classify")});return{...Pe,pick:{...Pe.pick,seen:se}}})(e.region.matchOs):null,D=null,M=l(async N=>{if(!E||!r||"error"in N&&Ne(N.error))return N;let U=await E(r);return D=U.pick,U.session?(i=void 0,f=U.session,r=U.upstream,a.setUpstream(r),a.dropProxied(),h(r)):N},"matched");E&&r&&u&&!o&&v(r);let x=u&&r&&!o?h(r).then(N=>i&&c&&a&&!("geo"in N&&N.geo)?p():N).then(N=>d&&a&&k(N)?b():N).then(M):null,T=l(async()=>{let N=await x,U=N&&"error"in N?Ne(N.error):null;return U?new Et(U):null},"refusedLogin"),w=e.executablePath??(e.stealth?Vn():void 0)??io();if(!w)throw await a?.close().catch(()=>{}),await T()??new Error("No Chromium executable found. Set PILOTFISH_CHROME, pass executablePath, install Google Chrome, or run `npx playwright install chromium`.");let S=null;e.persona&&!e.userAgent&&(S=(await ao(w))?.split(".")[0]??null,S?e={...e,userAgent:Gn(e.persona,S)}:ba(w));let I=!e.userDataDir,$=e.userDataDir??da(he(pa(),"pilotfish-profile-")),g=ro(e,$),C=e.persona?Zn(e.persona.os):void 0;Xn($,g,C&&e.persona?eo(e.persona.os):void 0)||process.emitWarning(`pilotfish: could not set the launch prefs in ${$} (its Preferences is not valid JSON, or not writable): Chrome starts with "Preload pages" on, and may connect to a site directly before its first page is routed`,{code:"PILOTFISH_PROFILE_PREFS"});let A=null;if(e.headless===!1&&process.platform==="linux"&&!process.env.DISPLAY){let{width:N,height:U}=e.viewport??{width:1280,height:800};try{A=await En({width:N,height:U},void 0,e.screen)}catch(se){throw await a?.close().catch(()=>{}),I&&await gs($),await T()??se}}let P;try{P=la(w,g,{stdio:["ignore",e.dumpio?"inherit":"ignore",e.dumpio?"inherit":"pipe","pipe","pipe"],env:{...process.env,...e.timezoneId?{TZ:e.timezoneId}:{},...A?.env??{},...C?{FONTCONFIG_FILE:C}:{}},detached:process.platform!=="win32"})}catch(N){throw await a?.close().catch(()=>{}),A?.close(),I&&await gs($),await T()??N}let V=[];e.dumpio||P.stderr?.on("data",N=>{V.length<200&&V.push(N.toString())});let z=new Ze(P.stdio[3],P.stdio[4]),R=new Qe(z,{...e.connection,onWire:e.debugWire?(N,U)=>process.stderr.write(`${N==="send"?"\u2192":"\u2190"} ${U.slice(0,400)}
111
+ `);let o=It(n,aa(e,n)),i=It(o,"Preferences"),r={};if(Kn(i)){let c=JSON.parse(Jn(i,"utf8"));if(!c||typeof c!="object"||Array.isArray(c))return!1;r=c}else ia(o,{recursive:!0});let a=fs(fs(r,ra),t??{});return JSON.stringify(a)!==JSON.stringify(r)&&Yn(i,JSON.stringify(a)),!0}catch{return!1}}l(Xn,"seedProfile");var Qn=l(n=>{throw new Error(n+" is not part of the open pilotfish MCP (the hosted service only)")},"refuse");function Zn(){return Qn("osFontConfig")}l(Zn,"osFontConfig");function eo(){return Qn("osFontPrefs")}l(eo,"osFontPrefs");function ma(n,e){return n==="darwin"?["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"]:n==="linux"?["/usr/bin/google-chrome","/usr/bin/google-chrome-stable","/opt/google/chrome/chrome"]:["C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe","C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",...e.LOCALAPPDATA?[he(e.LOCALAPPDATA,"Google","Chrome","Application","chrome.exe")]:[]]}l(ma,"googleChromeCandidates");function ga(n){return n==="darwin"?["/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary","/Applications/Chromium.app/Contents/MacOS/Chromium","/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge","/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"]:n==="linux"?["/usr/bin/chromium","/usr/bin/chromium-browser","/usr/bin/microsoft-edge"]:["C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe"]}l(ga,"otherChromiumCandidates");function wa(n,e,t){let s=[e.PLAYWRIGHT_BROWSERS_PATH,n==="darwin"?he(to(),"Library/Caches/ms-playwright"):void 0,n==="linux"?he(to(),".cache/ms-playwright"):void 0,n==="win32"?he(e.LOCALAPPDATA??"","ms-playwright"):void 0].filter(i=>!!i&&t(i)),o=[];for(let i of s){let r=[];try{r=oo(i)}catch{continue}let a=r.filter(c=>/^chromium-\d+$/.test(c)).sort().reverse();for(let c of a){let d=he(i,c);o.push(he(d,"chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing"),he(d,"chrome-mac/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing"),he(d,"chrome-linux/chrome"),he(d,"chrome-linux64/chrome"),he(d,"chrome-win/chrome.exe"),he(d,"chrome-win64/chrome.exe"))}}return o}l(wa,"playwrightChromiumCandidates");function io(n={}){let e=n.env??process.env,t=n.exists??no,s=n.os??ct(),o=e.PILOTFISH_CHROME;if(o&&t(o))return o;for(let i of[...ma(s,e),...wa(s,e,t),...ga(s)])if(t(i))return i}l(io,"findChromium");function ro(n,e){let{width:t,height:s}=n.viewport??{width:1280,height:800},o=["--remote-debugging-pipe",`--user-data-dir=${e}`,`--window-size=${t},${s}`,"--no-first-run","--no-default-browser-check","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-renderer-backgrounding","--disable-breakpad","--disable-component-update","--disable-default-apps","--disable-domain-reliability","--disable-sync","--disable-client-side-phishing-detection","--disable-hang-monitor","--disable-popup-blocking","--disable-prompt-on-repost","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--metrics-recording-only","--password-store=basic","--use-mock-keychain","--mute-audio","--disable-infobars","--disable-blink-features=AutomationControlled",`--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,DestroyProfileOnBrowserClose,HttpsUpgrades,PrivacySandboxSettings4,LensOverlay,AimEnabled,SafeBrowsingHashPrefixRealTimeLookups,NetworkTimeServiceQuerying${n.stealth?",IsolateSandboxedIframes":""}`,"--enable-features=NetworkService,NetworkServiceInProcess2",`--optimization-guide-manifest-override=${he(e,ms)}`];n.headless!==!1?o.push("--headless=new",...n.stealth?[]:["--hide-scrollbars"]):o.push("--test-type"),ct()==="linux"&&o.push("--disable-dev-shm-usage"),n.headless===!1&&ct()==="linux"&&o.push("--enable-unsafe-swiftshader"),n.stealth&&n.persona?.webgpu&&ct()==="linux"&&o.push("--enable-unsafe-webgpu","--disable-dawn-features=allow_unsafe_apis"),n.stealth&&n.persona&&ct()==="linux"&&o.push("--use-fake-device-for-media-stream"),n.stealth&&o.push("--force-webrtc-ip-handling-policy=disable_non_proxied_udp","--webrtc-ip-handling-policy=disable_non_proxied_udp"),n.userAgent&&o.push(`--user-agent=${n.userAgent}`),n.proxyServer&&o.push(`--proxy-server=${n.proxyServer}`),n.proxyServer&&o.push(`--proxy-bypass-list=${[...tt,...n.proxyBypass??[]].join(";")}`),n.locale&&o.push(`--lang=${n.locale}`);let i=n.persona?.languages.length?n.persona.languages.join(","):n.locale?va(n.locale):void 0;return i&&o.push(`--accept-lang=${i}`),n.ignoreHTTPSErrors&&o.push("--ignore-certificate-errors"),o.push(...n.args??[]),o.push("about:blank"),o}l(ro,"defaultArgs");var ws=new Map;function ya(n){let e=n.map(s=>/^(\d+)\.(\d+)\.(\d+)\.(\d+)(?:\.manifest)?$/.exec(s)).filter(s=>!!s);e.sort((s,o)=>{for(let i=1;i<=4;i++)if(Number(s[i])!==Number(o[i]))return Number(o[i])-Number(s[i]);return 0});let t=e[0];return t?`${t[1]}.${t[2]}.${t[3]}.${t[4]}`:null}l(ya,"newestVersionName");async function ao(n){let e="";try{let o=ua(n);e=`${o.size}:${o.mtimeMs}`}catch{if(process.platform==="win32"||/[\\/]/.test(n))return null}if(process.platform!=="win32")return new Promise(o=>{ca(n,["--version"],{timeout:1e4,windowsHide:!0},(i,r)=>{o(i?null:/(\d+\.\d+\.\d+\.\d+)/.exec(String(r))?.[1]??null)})});let t=ws.get(n);if(t&&t.stamp===e)return t.version;let s=null;try{s=ya(oo(fa(n)))}catch{s=null}return ws.set(n,{stamp:e,version:s}),s}l(ao,"chromeVersionOf");var so=new Set;function ba(n){so.has(n)||/[\\/]/.test(n)&&!no(n)||(so.add(n),process.emitWarning(`pilotfish: could not read the Chrome version of ${n} before launch: it starts with its own user agent, so the script fetches of service and nested workers, and a service worker's update checks, carry the host's`,{code:"PILOTFISH_UA_VERSION"}))}l(ba,"warnUnreadVersion");function ka(n,e){let t=ws.get(n);t&&(t.version=e)}l(ka,"noteLiveVersion");function va(n){let e=n.split("-")[0];return e&&e!==n?`${n},${e}`:n}l(va,"acceptLanguage");async function gs(n){await ha(n,{recursive:!0,force:!0,maxRetries:10,retryDelay:200}).catch(()=>{})}l(gs,"removeProfile");async function bs(n={}){let e=n,t=n.region?.proxy&&(!n.timezoneId||n.keepTimezone)?vn(n.region.proxy):null,s=e.proxyRouting,o=!!s&&!(s.sites??[]).length&&s.documents!=="all"&&s.documents!=="main",i=n.region?.city&&n.region.proxy&&Sn(n.region.proxy)&&t&&!o?n.region.city:void 0;if(e.region){let N=_n(e.region.country),U=e.region.session??xt(),{city:se,...te}=e.region;e={...e,region:{...te,country:N.country,session:U,...i?{city:i}:{}},timezoneId:e.timezoneId??N.timezoneId,locale:e.locale??N.locale,...e.region.proxy?{proxyServer:ot(e.region.proxy,{country:N.country,session:U,...i?{city:i}:{}})}:{}}}let r=e.proxyServer&&st(e.proxyServer)?e.proxyServer:null,a=r?await nt(r,{allowPrivateDirect:!!e.proxyRouting?.privateNetwork}):null;a&&(e={...e,proxyServer:a.url});let c=e.region?.proxy?{proxy:e.region.proxy,country:e.region.country}:null,d=c?()=>(N=>({url:ot(c.proxy,{country:c.country,session:N,...i?{city:i}:{}}),session:N}))(xt()):null,u=t&&r?N=>xn(N,t,3e3):null,h=l(N=>u(N).then(U=>({geo:U}),U=>({error:U})),"look"),p=l(()=>(i=void 0,r=ot(c.proxy,{country:c.country,session:e.region.session}),a.setUpstream(r),a.dropProxied(),h(r)),"byCountry"),f=null,b=l(()=>{i=void 0;let N=d();return f=N.session,r=N.url,a.setUpstream(r),a.dropProxied(),h(r)},"fresh"),k=l(N=>"error"in N&&!Ne(N.error),"dead"),m=e.region?.matchOs&&c&&a?Tn(process.env,e.region.osUrl):null,y=new Map,v=l(N=>{let U=y.get(N);return U||y.set(N,U=$n(N,m)),U},"classifyOnce"),_=l(()=>{let N=xt();return{url:ot(c.proxy,{country:c.country,session:N}),session:N}},"drawByCountry"),E=m&&e.region?.matchOs?(N=>async U=>{let se={},Pe=await In({persona:N,current:U,draw:_,classify:l(async wt=>{let W=await v(wt),$e=W.os??W.why??"none";return se[$e]=(se[$e]??0)+1,W.os},"classify")});return{...Pe,pick:{...Pe.pick,seen:se}}})(e.region.matchOs):null,D=null,M=l(async N=>{if(!E||!r||"error"in N&&Ne(N.error))return N;let U=await E(r);return D=U.pick,U.session?(i=void 0,f=U.session,r=U.upstream,a.setUpstream(r),a.dropProxied(),h(r)):N},"matched");E&&r&&u&&!o&&v(r);let x=u&&r&&!o?h(r).then(N=>i&&c&&a&&!("geo"in N&&N.geo)?p():N).then(N=>d&&a&&k(N)?b():N).then(M):null,T=l(async()=>{let N=await x,U=N&&"error"in N?Ne(N.error):null;return U?new Et(U):null},"refusedLogin"),w=e.executablePath??(e.stealth?Vn():void 0)??io();if(!w)throw await a?.close().catch(()=>{}),await T()??new Error("No Chromium executable found. Set PILOTFISH_CHROME, pass executablePath, install Google Chrome, or run `npx playwright install chromium`.");let S=null;e.persona&&!e.userAgent&&(S=(await ao(w))?.split(".")[0]??null,S?e={...e,userAgent:Gn(e.persona,S)}:ba(w));let I=!e.userDataDir,$=e.userDataDir??da(he(pa(),"pilotfish-profile-")),g=ro(e,$),C=e.persona?Zn(e.persona.os):void 0;Xn($,g,C&&e.persona?eo(e.persona.os):void 0)||process.emitWarning(`pilotfish: could not set the launch prefs in ${$} (its Preferences is not valid JSON, or not writable): Chrome starts with "Preload pages" on, and may connect to a site directly before its first page is routed`,{code:"PILOTFISH_PROFILE_PREFS"});let A=null;if(e.headless===!1&&process.platform==="linux"&&!process.env.DISPLAY){let{width:N,height:U}=e.viewport??{width:1280,height:800};try{A=await En({width:N,height:U},void 0,e.screen)}catch(se){throw await a?.close().catch(()=>{}),I&&await gs($),await T()??se}}let P;try{P=la(w,g,{stdio:["ignore",e.dumpio?"inherit":"ignore",e.dumpio?"inherit":"pipe","pipe","pipe"],env:{...process.env,...e.timezoneId?{TZ:e.timezoneId}:{},...A?.env??{},...C?{FONTCONFIG_FILE:C}:{}},detached:process.platform!=="win32"})}catch(N){throw await a?.close().catch(()=>{}),A?.close(),I&&await gs($),await T()??N}let V=[];e.dumpio||P.stderr?.on("data",N=>{V.length<200&&V.push(N.toString())});let z=new Ze(P.stdio[3],P.stdio[4]),R=new Qe(z,{...e.connection,onWire:e.debugWire?(N,U)=>process.stderr.write(`${N==="send"?"\u2192":"\u2190"} ${U.slice(0,400)}
112
112
  `):e.connection?.onWire}),F=new Promise((N,U)=>{P.once("exit",(se,te)=>{U(new Error(ys(w,se,te,V.join(""))))}),P.once("error",se=>U(new Error(`Failed to spawn Chromium at ${w}: ${se.message}`)))}),K={process:P,userDataDir:$,ownsUserDataDir:I,executablePath:w},j=new Tt(R,K,e);a&&r&&(j.useForwarder(a),j._exit={upstream:r,lookup:u,looked:!o,reroll:d,pick:E,picked:!!x},e.proxyRouting&&j.setProxyRouting(e.proxyRouting)),A&&j.addCleanup(()=>A.close());try{await Promise.race([j._initialize(),F,new Promise((N,U)=>setTimeout(()=>U(new Error(`Browser did not become ready within ${e.launchTimeoutMs??3e4}ms`)),e.launchTimeoutMs??3e4).unref())])}catch(N){let U=P.exitCode!==null||P.signalCode!==null?!0:await new Promise(te=>{let Pe=setTimeout(()=>te(!1),1e3);P.once("exit",()=>{clearTimeout(Pe),te(!0)})});try{P.kill("SIGKILL")}catch{}await a?.close().catch(()=>{}),A?.close(),I&&await gs($);let se=await T();throw se||(U?new Error(ys(w,P.exitCode,P.signalCode,V.join(""))):N)}if(S){let N=/Chrome\/(\d+(?:\.\d+){1,3})/.exec((await j.version().catch(()=>null))?.product??"")?.[1];N&&N.split(".")[0]!==S&&(ka(w,N),process.emitWarning(`pilotfish: ${w} was read as Chrome ${S} before launch but runs ${N}; its own user agent named the wrong major this time`,{code:"PILOTFISH_UA_VERSION"}))}if(x){let N=await x,U=await T();if(U)throw await j.close(),U;if(!i&&j.options.region?.city){let{city:se,...te}=j.options.region;j.options.region=te,j._exit&&r&&(j._exit.upstream=r)}f&&j.options.region&&(j.options.region={...j.options.region,session:f},j._exit&&r&&(j._exit.upstream=r)),D&&j.options.region&&(j.options.region={...j.options.region,exitOs:D}),"geo"in N&&N.geo&&await j._applyExitGeo(N.geo,{geolocation:!1})}return j}l(bs,"launch");var Et=class extends Error{static{l(this,"ProxyAuthError")}status;constructor(e){super(`The residential proxy refused its login (HTTP ${e}). Check the provider account (traffic left on the plan) and its username and password.`),this.name="ProxyAuthError",this.status=e}};function ys(n,e,t,s){let o=[];return/No usable sandbox|setuid sandbox|namespace|zygote/i.test(s)&&o.push("Chromium's sandbox could not start. On Ubuntu 23.10+ (incl. GitHub ubuntu-24.04 runners) unprivileged user namespaces are blocked by AppArmor: run `sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0`, or install an AppArmor profile for Chrome. Do not disable the sandbox for untrusted pages."),/PR_SET_NO_NEW_PRIVS/i.test(s)&&/Failed to move to new namespace/i.test(s)&&o.push("If this runs under systemd, remove NoNewPrivileges=true from the unit: with no_new_privs set, AppArmor cannot apply the profile that grants Chrome user namespaces."),/error while loading shared libraries/i.test(s)&&o.push("Missing system libraries: run `npx playwright install-deps chromium` or use the provided Docker image."),/Missing X server|cannot open display/i.test(s)&&o.push("Headed mode needs a display: run under Xvfb (xvfb-run) or use headless."),[`Chromium exited during launch (code=${e} signal=${t}) \u2014 ${n}`,...o.map(i=>`Hint: ${i}`),s?`stderr:
113
113
  ${s.trim().split(`
114
114
  `).slice(-25).join(`
@@ -411,18 +411,18 @@ return __pfClassify("Accept all") === "accept" && __pfClassify("Reject all") ===
411
411
  return click.call(this);
412
412
  };
413
413
  })();
414
- ${ss(rr(cr,"autoconsent.playwright.js"),"utf8")}`,iu=3,ru=2500,au=6500,cu=6e3,lu=3e3,du=1500,uu=3e3,hu=4e3;function pu(n,e){if(n.doneAt)return"opted_out";if(n.optOut===!1)return"failed";if(n.optOut===!0)return e-n.optOutAt>lu?"opted_out":"pending";if(n.popupAt)return"pending";if(n.detectedAt)return e-n.detectedAt<au?"pending":"no_cmp";let t=n.topLifecycle!=="nothingDetected",s=l((o,i)=>e-o>=ru&&i!==void 0&&n.topAttempts>=i+iu,"quiet");return n.cmpRequestAt&&e-n.cmpRequestAt<cu&&t&&!s(n.cmpRequestAt,n.roundAtCmpRequest)||!n.topInitAt&&/^https?:/.test(n.url)&&e-n.navAt<du||n.topInitAt&&n.topAttempts<2&&e-n.topInitAt<uu||n.patternsAt&&e-n.patternsAt<hu&&t&&!s(n.patternsAt,n.roundAtPatterns)?"pending":"no_cmp"}l(pu,"outcomeOf");var ts=class{static{l(this,"ConsentEngine")}opts;emitter=new Xd;pages=new WeakMap;disposers=new Set;armed=new WeakSet;retried=new WeakSet;retryTimers=new Set;disposed=!1;config;constructor(e={}){if(this.opts=e,this.config=su(e),this.config.autoAction!=="optOut"||!["off","reject"].includes(this.config.heuristicMode))throw new Error("ConsentEngine only ever opts out")}on(e,t){return this.emitter.on(e,t),()=>this.emitter.off(e,t)}consentStatus(e){let t=this.state(e),s=Date.now(),o=pu(t,s),i=o==="opted_out"?t.doneAt??t.optOutAt:s;return{cmp:t.cmp,outcome:o,ms:Math.max(0,i-t.navAt),url:t.url,...t.cosmetic!==void 0?{cosmetic:t.cosmetic}:{},...t.selfTest!==void 0?{selfTest:t.selfTest}:{},refusedClicks:t.refused,frames:t.frames.size}}async waitForConsent(e,t=1e4){let s=Date.now()+t;for(;;){let o=this.consentStatus(e);if(o.outcome!=="pending"||Date.now()>=s||e.closed)return o;await new Promise(i=>setTimeout(i,Math.min(100,Math.max(1,s-Date.now()))))}}dispose(){this.disposed=!0;for(let e of this.retryTimers)clearTimeout(e);this.retryTimers.clear();for(let e of this.disposers)e();this.disposers.clear()}initSession=l(async e=>{let t=e.session,s=e.page;if(!e.isMain&&be(e.url)||this.armed.has(t))return;this.armed.add(t);let o=new Map,i=[()=>this.armed.delete(t)],r;try{[,r]=await Promise.all([t.send("Runtime.addBinding",{name:Ke,executionContextName:pn},{timeoutMs:5e3}),t.send("Page.addScriptToEvaluateOnNewDocument",{source:ou,worldName:pn,runImmediately:!0},{timeoutMs:5e3})])}catch(p){if(this.armed.delete(t),e.isMain&&!this.retried.has(t)&&!this.disposed){this.retried.add(t);let f=setTimeout(()=>{this.retryTimers.delete(f),this.disposed||this.initSession(e).catch(()=>{})},2e3);f.unref?.(),this.retryTimers.add(f)}throw p}let a=l(()=>{for(let p of i)p();t.send("Page.removeScriptToEvaluateOnNewDocument",{identifier:r.identifier}).catch(()=>{}),t.send("Runtime.removeBinding",{name:Ke}).catch(()=>{})},"dispose");this.disposers.add(a),i.push(()=>this.disposers.delete(a));let c=l(async(p,f,b)=>{if(!/^https?:/.test(f)||!b&&be(f))return;let{executionContextId:k}=await t.send("Page.createIsolatedWorld",{frameId:p,worldName:pn,grantUniveralAccess:!1},{timeoutMs:5e3});o.set(k,{frameId:p,mainFrame:b}),await t.send("Runtime.addBinding",{name:Ke,executionContextId:k},{timeoutMs:5e3}),await t.send("Runtime.evaluate",{contextId:k,expression:"globalThis.__pfAcFlush && __pfAcFlush()",returnByValue:!0},{timeoutMs:5e3})},"arm"),d=l((p,f,b)=>{c(p,f,b).catch(k=>{/not found|Cannot find|No frame|detached|closed|destroyed|navigated/i.test(String(k))||this.emit("error",{page:s,frameId:p,url:f,error:`arm: ${String(k)}`})})},"armSafe");i.push(t.on("Page.frameNavigated",p=>{let f=e.isMain&&!p.frame.parentId;f&&this.reset(s,p.frame.url),d(p.frame.id,p.frame.url,f)}),t.on("Runtime.bindingCalled",p=>{if(p.name!==Ke)return;let f=o.get(p.executionContextId);f&&this.onMessage(t,s,p.executionContextId,f,p.payload).catch(()=>{})}),t.on("Network.requestWillBeSent",p=>{if(!qi(p.request.url))return;let f=this.state(s);f.cmpRequestAt===void 0&&(f.cmpRequestAt=Date.now(),f.roundAtCmpRequest=f.topAttempts)}));let{frameTree:u}=await t.send("Page.getFrameTree"),h=l((p,f)=>{d(p.frame.id,p.frame.url,f);for(let b of p.childFrames??[])h(b,!1)},"walk");h(u,e.isMain)},"initSession");async onMessage(e,t,s,o,i){let r;try{r=JSON.parse(i)}catch{return}let a=this.state(t),c=typeof r.url=="string"?r.url:"",d={page:t,frameId:o.frameId,url:c},u=typeof r.cmp=="string"?r.cmp:"";switch(r.type){case"init":{a.frames.add(o.frameId),o.mainFrame&&(a.topInitAt??=Date.now());let h={autoconsent:[],compact:eu(lr,{url:c,mainFrame:o.mainFrame})};await this.reply(e,s,{type:"initResp",config:this.config,rules:h});return}case"eval":await this.reply(e,s,{type:"evalResp",id:r.id,result:await this.runSnippet(e,t,s,o,r.snippetId)});return;case"cmpDetected":a.cmp??=u,a.detectedAt??=Date.now(),this.emit("cmpDetected",{...d,cmp:u});return;case"popupFound":a.cmp=u,a.popupAt??=Date.now(),this.emit("popupFound",{...d,cmp:u});return;case"optOutResult":a.cmp=u||a.cmp,a.optOut=r.result===!0,a.optOutAt=Date.now(),r.result===!0&&r.scheduleSelfTest===!0&&(a.selfTestTarget={s:e,contextId:s}),this.emit("optOutResult",{...d,cmp:u,result:r.result===!0});return;case"autoconsentDone":{a.cmp=u||a.cmp,a.doneAt=Date.now(),a.cosmetic=r.isCosmetic===!0,this.emit("autoconsentDone",{...d,cmp:u,cosmetic:a.cosmetic,durationMs:Number(r.duration)||0,clicks:Number(r.totalClicks)||0});let h=a.selfTestTarget;a.selfTestTarget=void 0,this.opts.selfTest!==!1&&h&&await this.reply(h.s,h.contextId,{type:"selfTest"});return}case"selfTestResult":a.selfTest=r.result===!0,this.emit("selfTestResult",{...d,cmp:u,result:a.selfTest});return;case"optInResult":this.emit("error",{...d,error:"unexpected optInResult",details:r});return;case"autoconsentError":this.emit("error",{...d,error:"autoconsent",details:r.details});return;case"pfRefusedClick":a.refused++,this.emit("error",{...d,error:`refused to click an accept button: "${String(r.text)}"`});return;case"report":{let h=r.state??{};if(!o.mainFrame||r.mainFrame!==!0)return;a.topLifecycle=h.lifecycle,a.topAttempts=Math.max(a.topAttempts,Number(h.findCmpAttempts)||0),h.heuristicPatterns?.length&&a.patternsAt===void 0&&(a.patternsAt=Date.now(),a.roundAtPatterns=a.topAttempts);return}default:return}}reply(e,t,s){return e.send("Runtime.evaluate",{contextId:t,expression:`autoconsentReceiveMessage(${JSON.stringify(s)})`,returnByValue:!0}).catch(()=>{})}async runSnippet(e,t,s,o,i){let r=typeof i=="string"?ar[i]:void 0;if(!r||tu.has(i))return this.emit("error",{page:t,frameId:o.frameId,error:`refused eval snippet ${String(i)}`}),!1;let a=await e.send("Runtime.evaluate",{contextId:s,expression:"document",returnByValue:!1});if(!a.result.objectId)return!1;try{let{node:c}=await e.send("DOM.describeNode",{objectId:a.result.objectId}),{object:d}=await e.send("DOM.resolveNode",{backendNodeId:c.backendNodeId});if(!d.objectId)return!1;try{return(await e.send("Runtime.callFunctionOn",{objectId:d.objectId,functionDeclaration:`async function () { try { return !!(await (${r.toString()}).call(window)); } catch (e) { return false; } }`,returnByValue:!0,awaitPromise:!0})).result.value===!0}finally{e.send("Runtime.releaseObject",{objectId:d.objectId}).catch(()=>{})}}catch{return!1}finally{e.send("Runtime.releaseObject",{objectId:a.result.objectId}).catch(()=>{})}}state(e){let t=this.pages.get(e);return t||(t={navAt:Date.now(),url:e.url,cmp:null,refused:0,frames:new Set,topAttempts:0},this.pages.set(e,t)),t}reset(e,t){this.pages.set(e,{navAt:Date.now(),url:t,cmp:null,refused:0,frames:new Set,topAttempts:0})}emit(e,t){e==="error"&&this.emitter.listenerCount("error")===0||this.emitter.emit(e,t)}};var fu=["cmpDetected","popupFound","optOutResult","autoconsentDone","selfTestResult","error"],mu=["blocked","error"];async function mn(n,e={}){let t=e.consent===!1?null:new ts(typeof e.consent=="object"?e.consent:{}),s=e.adblock===!1?null:await es.create(typeof e.adblock=="object"?e.adblock:{}),o=[];if(e.onEvent){let r=e.onEvent;if(t)for(let a of fu)o.push(t.on(a,(c=>r({source:"consent",type:a,data:c}))));if(s)for(let a of mu)o.push(s.on(a,(c=>r({source:"adblock",type:a,data:c}))))}let i=l(async r=>{await Promise.all([t?.initSession(r),s?.initSession(r)])},"init");return o.push(n.addSessionInitializer(i)),{consent:t,adblock:s,detach(){for(let r of o)r();t?.dispose(),s?.dispose()}}}l(mn,"attachInterrupts");var gu="pilotfish's open MCP makes no model calls: the MCP client's own model reads the page and decides the next step",wu=l(()=>{let n=l(async()=>{throw new Error(gu)},"refuse");return{messages:{create:n},beta:{messages:{create:n}}}},"noModelClient"),gn=class{static{l(this,"ConfirmThrough")}inner;canAsk;constructor(e,t){this.inner=e,this.canAsk=t}async check(e){let t=await this.inner.check(e);return t.kind!=="confirm"||this.canAsk()?t:{kind:"deny",reason:`${t.reason}. This needs a person's confirmation, and this MCP client cannot ask for one (no elicitation support)`}}},Ye=class{static{l(this,"BrowserSession")}config;opts;browser=null;launching=null;interrupts=null;tabs=null;agent=null;queue=Promise.resolve();policy;asker;constructor(e,t={}){this.config=e,this.opts=t,this.policy=new Me({allowedHosts:e.allowedHosts,allowPrivateNetwork:e.allowPrivateNetwork})}get launched(){return!!this.browser&&!this.browser.closed}async page(){return await this.ensure(),this.tabs.current}get tabTable(){return this.tabs}async ensure(){return this.browser&&!this.browser.closed?this.browser:(this.launching??=this.start().finally(()=>{this.launching=null}),this.launching)}async start(){let e=await bs({...this.opts.launch,headless:this.config.headless});this.interrupts=await mn(e,{consent:!0,adblock:!0}).catch(()=>null);let t=e.allPages()[0]??await e.newPage();return this.tabs=new Re(t),this.browser=e,e.on("disconnected",()=>{this.browser===e&&this.reset()}),e}reset(){this.agent?.dispose(),this.agent=null,this.tabs?.dispose(),this.tabs=null,this.interrupts?.detach(),this.interrupts=null,this.browser=null}executor(){return this.agent??=new mt(this.tabs.current,{tabs:this.tabs,client:wu(),secrets:this.config.secrets,policy:new gn(this.policy,()=>!!this.asker),consent:this.interrupts?.consent??void 0,allowScreenshots:this.config.allowScreenshots,askUser:l(e=>this.asker?.(e)??Promise.resolve(null),"askUser"),fastSettle:!0,enableQuery:!0,compactSnapshots:!0,autoDismiss:!0,awaitLoading:!0,actionSnapshotBand:0,snapshotMoreHint:"browser_scroll for the next screen, browser_get_text for the page's text, or browser_snapshot (whole_page) for every element"}),this.agent}async act(e,t,s={}){let o=this.queue.then(async()=>(await this.ensure(),this.executor().act(e,t,"",s)));return this.queue=o.catch(()=>{}),o}async obstacle(e={}){if(!this.launched)return null;let t=this.queue.then(()=>this.executor().obstacle(e));return this.queue=t.catch(()=>{}),t.catch(()=>null)}async close(){let e=this.browser??(this.launching?await this.launching.catch(()=>null):null);return e?(this.reset(),await e.close().catch(()=>{}),!0):!1}};import{McpServer as yu}from"@modelcontextprotocol/sdk/server/mcp.js";import{ListToolsRequestSchema as bu}from"@modelcontextprotocol/sdk/types.js";import{z as q}from"zod";var ns="0.1.9";var os=Ce.click.shape.ref.describe('From the latest snapshot, e.g. "e12" or "f1e3"'),dr=q.enum(["down","up","left","right"]),ku={click:["ref"],type:["ref","text","submit"],select:["ref","values"],press:["key"],scroll:["direction"],wait:["seconds"]},vu={click:1,type:2,select:2,press:1,scroll:1,wait:1},xu=q.object({action:q.enum(["click","type","select","press","scroll","wait"]).describe("click, select, type take ref; type: text (submit: Enter after); select: values; press: key; scroll: direction; wait: seconds"),ref:os.optional(),text:q.string().max(2e4).optional(),submit:q.boolean().optional(),values:q.array(q.string()).min(1).max(50).optional(),key:q.string().min(1).max(40).optional(),direction:dr.optional(),seconds:q.number().min(.5).max(10).optional()}).strict().superRefine((n,e)=>{let t=ku[n.action];for(let s of t.slice(0,vu[n.action]))n[s]===void 0&&e.addIssue({code:"custom",path:[s],message:`${n.action} needs ${s}`});for(let s of Object.keys(n))s!=="action"&&!t.includes(s)&&e.addIssue({code:"custom",path:[s],message:`${s} is not a field of ${n.action} (it takes ${t.join(", ")})`})}),ae={browser_navigate:q.object({url:q.url({protocol:/^https?$/}).describe("Absolute http(s) URL"),snapshot:q.boolean().optional().describe("Default true. false: no page snapshot, only what happened (title, counts). Only when this same reply also reads the page with browser_query and you will not act on it; keep it for a page you have not seen or whose tabs, sorts, filters or forms you may use.")}).strict(),browser_snapshot:q.object({whole_page:q.boolean().optional()}).strict(),browser_click:q.object({ref:os}).strict(),browser_type:q.object({ref:os,text:q.string().max(2e4),submit:q.boolean().optional().describe("Press Enter afterwards")}).strict(),browser_select:q.object({ref:os,values:q.array(q.string()).min(1).max(50)}).strict(),browser_press:q.object({key:q.string().min(1).max(40).describe('e.g. "Enter", "Escape", "Tab", "ArrowDown", "Control+a"')}).strict(),browser_scroll:q.object({direction:dr}).strict(),browser_screenshot:q.object({}).strict(),browser_get_text:q.object({max_chars:q.number().int().min(500).max(6e4).optional().describe("Default 20000. Ask for less when the start of the page is enough."),selector:q.string().min(1).max(300).optional().describe(`CSS selector: only the first match's text, e.g. "main" or "#results"`)}).strict(),browser_query:q.object({expression:q.string().min(1).max(4e3).describe("e.g. [...document.querySelectorAll('.result')].slice(0,10).map(e => e.innerText)")}).strict(),browser_find:q.object({query:q.string().min(1).max(300).describe('Words naming the element(s), e.g. "Sort by" or "price max input, Go button"')}).strict(),browser_wait:q.object({seconds:q.number().min(.5).max(10)}).strict(),browser_resume:q.object({timeout_s:q.number().int().min(5).max(900).optional().describe("Default 300")}).strict(),browser_actions:q.object({actions:q.array(xu).min(1).max(10)}).strict(),browser_tabs:q.object({switch_to:q.string().min(1).max(10).optional().describe('Tab id to work in from now on, e.g. "t2"')}).strict(),browser_close:q.object({}).strict()};function Se(n,e=!1){return{content:[{type:"text",text:n}],...e?{isError:!0}:{}}}l(Se,"text");function _u(n){let e=Object.keys(n.secrets);return["Words like cheapest, latest, highest-rated or most mean the site's own sort or tab for that order (a list's default order is usually something else, e.g. 'Best' or 'Featured'): apply it, then read the top.",e.length?`Secrets are typed as placeholders and never shown: ${e.map(t=>`{{${t}}}`).join(", ")}.`:"",n.allowedHosts.length?`Navigation is limited to: ${n.allowedHosts.join(", ")}.`:"","When a result says NEEDS A PERSON (a sign-in, a one-time code or 2FA, a device approval, a CAPTCHA), tell the user in your reply what the page needs and that the pilotfish browser window is open on it; when they have done it there, call browser_resume and carry on. BLOCKED and STOP mean the site cannot be continued that way."].filter(Boolean).join(`
415
- `)}l(_u,"serverInstructions");function wn(n={}){let e=n.config??n.session?.config??kt(),t=!n.session,s=n.session??new Ye(e,n),o=new yu({name:"pilotfish",version:n.version??ns},{capabilities:{logging:{}},instructions:_u(e)}),i=Object.keys(e.secrets),r=i.length?` Secrets: ${i.map(w=>`{{${w}}}`).join(", ")}.`:"",a=l(()=>!!o.server.getClientCapabilities()?.elicitation?.form,"canElicit"),c=l(w=>a()?async S=>{let I=await o.server.elicitInput({message:S,requestedSchema:{type:"object",properties:{answer:{type:"string",title:"Answer",description:'Reply "yes" to allow an action'}},required:["answer"]}},{relatedRequestId:w.requestId,timeout:e.interventionTimeoutMs}).catch(()=>null);return I?.action==="accept"&&typeof I.content?.answer=="string"?I.content.answer:null}:void 0,"askerFor"),d=l(w=>w.replace(/in the live view/g,"in the pilotfish browser window").replace(/Please send the code, or type it/,"Please type the code"),"forWindow"),u=l(w=>w.needs==="solve_in_live_view"||w.needs==="login_in_live_view"||w.needs==="code"||w.needs==="credentials","byHand"),h=l((w,S="")=>[`NEEDS A PERSON (${w.category}${w.vendor?`, ${w.vendor}`:""}): ${d(w.message)}`,S,u(w)&&e.headless?"This browser is headless, so it cannot be done by hand here: tell the user, and that the pilotfish server needs PILOTFISH_HEADLESS=0 (a visible window) for steps like this one.":`Tell the user now, in your reply: what the page needs, and that the pilotfish browser window is open on it (${w.url}). Ask them to do it there and to tell you when it is done. Then call browser_resume: it waits for the page to move on and returns it.`,"Do not try to get past it yourself: pilotfish does not type passwords or codes it was not given, and does not solve CAPTCHAs."].filter(Boolean).join(`
414
+ ${ss(rr(cr,"autoconsent.playwright.js"),"utf8")}`,iu=3,ru=2500,au=6500,cu=6e3,lu=3e3,du=1500,uu=3e3,hu=4e3;function pu(n,e){if(n.doneAt)return"opted_out";if(n.optOut===!1)return"failed";if(n.optOut===!0)return e-n.optOutAt>lu?"opted_out":"pending";if(n.popupAt)return"pending";if(n.detectedAt)return e-n.detectedAt<au?"pending":"no_cmp";let t=n.topLifecycle!=="nothingDetected",s=l((o,i)=>e-o>=ru&&i!==void 0&&n.topAttempts>=i+iu,"quiet");return n.cmpRequestAt&&e-n.cmpRequestAt<cu&&t&&!s(n.cmpRequestAt,n.roundAtCmpRequest)||!n.topInitAt&&/^https?:/.test(n.url)&&e-n.navAt<du||n.topInitAt&&n.topAttempts<2&&e-n.topInitAt<uu||n.patternsAt&&e-n.patternsAt<hu&&t&&!s(n.patternsAt,n.roundAtPatterns)?"pending":"no_cmp"}l(pu,"outcomeOf");var ts=class{static{l(this,"ConsentEngine")}opts;emitter=new Xd;pages=new WeakMap;disposers=new Set;armed=new WeakSet;retried=new WeakSet;retryTimers=new Set;disposed=!1;config;constructor(e={}){if(this.opts=e,this.config=su(e),this.config.autoAction!=="optOut"||!["off","reject"].includes(this.config.heuristicMode))throw new Error("ConsentEngine only ever opts out")}on(e,t){return this.emitter.on(e,t),()=>this.emitter.off(e,t)}consentStatus(e){let t=this.state(e),s=Date.now(),o=pu(t,s),i=o==="opted_out"?t.doneAt??t.optOutAt:s;return{cmp:t.cmp,outcome:o,ms:Math.max(0,i-t.navAt),url:t.url,...t.cosmetic!==void 0?{cosmetic:t.cosmetic}:{},...t.selfTest!==void 0?{selfTest:t.selfTest}:{},refusedClicks:t.refused,frames:t.frames.size}}async waitForConsent(e,t=1e4){let s=Date.now()+t;for(;;){let o=this.consentStatus(e);if(o.outcome!=="pending"||Date.now()>=s||e.closed)return o;await new Promise(i=>setTimeout(i,Math.min(100,Math.max(1,s-Date.now()))))}}dispose(){this.disposed=!0;for(let e of this.retryTimers)clearTimeout(e);this.retryTimers.clear();for(let e of this.disposers)e();this.disposers.clear()}initSession=l(async e=>{let t=e.session,s=e.page;if(!e.isMain&&be(e.url)||this.armed.has(t))return;this.armed.add(t);let o=new Map,i=[()=>this.armed.delete(t)],r;try{[,r]=await Promise.all([t.send("Runtime.addBinding",{name:Ke,executionContextName:pn},{timeoutMs:5e3}),t.send("Page.addScriptToEvaluateOnNewDocument",{source:ou,worldName:pn,runImmediately:!0},{timeoutMs:5e3})])}catch(p){if(this.armed.delete(t),e.isMain&&!this.retried.has(t)&&!this.disposed){this.retried.add(t);let f=setTimeout(()=>{this.retryTimers.delete(f),this.disposed||this.initSession(e).catch(()=>{})},2e3);f.unref?.(),this.retryTimers.add(f)}throw p}let a=l(()=>{for(let p of i)p();t.send("Page.removeScriptToEvaluateOnNewDocument",{identifier:r.identifier}).catch(()=>{}),t.send("Runtime.removeBinding",{name:Ke}).catch(()=>{})},"dispose");this.disposers.add(a),i.push(()=>this.disposers.delete(a));let c=l(async(p,f,b)=>{if(!/^https?:/.test(f)||!b&&be(f))return;let{executionContextId:k}=await t.send("Page.createIsolatedWorld",{frameId:p,worldName:pn,grantUniveralAccess:!1},{timeoutMs:5e3});o.set(k,{frameId:p,mainFrame:b}),await t.send("Runtime.addBinding",{name:Ke,executionContextId:k},{timeoutMs:5e3}),await t.send("Runtime.evaluate",{contextId:k,expression:"globalThis.__pfAcFlush && __pfAcFlush()",returnByValue:!0},{timeoutMs:5e3})},"arm"),d=l((p,f,b)=>{c(p,f,b).catch(k=>{/not found|Cannot find|No frame|detached|closed|destroyed|navigated/i.test(String(k))||this.emit("error",{page:s,frameId:p,url:f,error:`arm: ${String(k)}`})})},"armSafe");i.push(t.on("Page.frameNavigated",p=>{let f=e.isMain&&!p.frame.parentId;f&&this.reset(s,p.frame.url),d(p.frame.id,p.frame.url,f)}),t.on("Runtime.bindingCalled",p=>{if(p.name!==Ke)return;let f=o.get(p.executionContextId);f&&this.onMessage(t,s,p.executionContextId,f,p.payload).catch(()=>{})}),t.on("Network.requestWillBeSent",p=>{if(!qi(p.request.url))return;let f=this.state(s);f.cmpRequestAt===void 0&&(f.cmpRequestAt=Date.now(),f.roundAtCmpRequest=f.topAttempts)}));let{frameTree:u}=await t.send("Page.getFrameTree"),h=l((p,f)=>{d(p.frame.id,p.frame.url,f);for(let b of p.childFrames??[])h(b,!1)},"walk");h(u,e.isMain)},"initSession");async onMessage(e,t,s,o,i){let r;try{r=JSON.parse(i)}catch{return}let a=this.state(t),c=typeof r.url=="string"?r.url:"",d={page:t,frameId:o.frameId,url:c},u=typeof r.cmp=="string"?r.cmp:"";switch(r.type){case"init":{a.frames.add(o.frameId),o.mainFrame&&(a.topInitAt??=Date.now());let h={autoconsent:[],compact:eu(lr,{url:c,mainFrame:o.mainFrame})};await this.reply(e,s,{type:"initResp",config:this.config,rules:h});return}case"eval":await this.reply(e,s,{type:"evalResp",id:r.id,result:await this.runSnippet(e,t,s,o,r.snippetId)});return;case"cmpDetected":a.cmp??=u,a.detectedAt??=Date.now(),this.emit("cmpDetected",{...d,cmp:u});return;case"popupFound":a.cmp=u,a.popupAt??=Date.now(),this.emit("popupFound",{...d,cmp:u});return;case"optOutResult":a.cmp=u||a.cmp,a.optOut=r.result===!0,a.optOutAt=Date.now(),r.result===!0&&r.scheduleSelfTest===!0&&(a.selfTestTarget={s:e,contextId:s}),this.emit("optOutResult",{...d,cmp:u,result:r.result===!0});return;case"autoconsentDone":{a.cmp=u||a.cmp,a.doneAt=Date.now(),a.cosmetic=r.isCosmetic===!0,this.emit("autoconsentDone",{...d,cmp:u,cosmetic:a.cosmetic,durationMs:Number(r.duration)||0,clicks:Number(r.totalClicks)||0});let h=a.selfTestTarget;a.selfTestTarget=void 0,this.opts.selfTest!==!1&&h&&await this.reply(h.s,h.contextId,{type:"selfTest"});return}case"selfTestResult":a.selfTest=r.result===!0,this.emit("selfTestResult",{...d,cmp:u,result:a.selfTest});return;case"optInResult":this.emit("error",{...d,error:"unexpected optInResult",details:r});return;case"autoconsentError":this.emit("error",{...d,error:"autoconsent",details:r.details});return;case"pfRefusedClick":a.refused++,this.emit("error",{...d,error:`refused to click an accept button: "${String(r.text)}"`});return;case"report":{let h=r.state??{};if(!o.mainFrame||r.mainFrame!==!0)return;a.topLifecycle=h.lifecycle,a.topAttempts=Math.max(a.topAttempts,Number(h.findCmpAttempts)||0),h.heuristicPatterns?.length&&a.patternsAt===void 0&&(a.patternsAt=Date.now(),a.roundAtPatterns=a.topAttempts);return}default:return}}reply(e,t,s){return e.send("Runtime.evaluate",{contextId:t,expression:`autoconsentReceiveMessage(${JSON.stringify(s)})`,returnByValue:!0}).catch(()=>{})}async runSnippet(e,t,s,o,i){let r=typeof i=="string"?ar[i]:void 0;if(!r||tu.has(i))return this.emit("error",{page:t,frameId:o.frameId,error:`refused eval snippet ${String(i)}`}),!1;let a=await e.send("Runtime.evaluate",{contextId:s,expression:"document",returnByValue:!1});if(!a.result.objectId)return!1;try{let{node:c}=await e.send("DOM.describeNode",{objectId:a.result.objectId}),{object:d}=await e.send("DOM.resolveNode",{backendNodeId:c.backendNodeId});if(!d.objectId)return!1;try{return(await e.send("Runtime.callFunctionOn",{objectId:d.objectId,functionDeclaration:`async function () { try { return !!(await (${r.toString()}).call(window)); } catch (e) { return false; } }`,returnByValue:!0,awaitPromise:!0})).result.value===!0}finally{e.send("Runtime.releaseObject",{objectId:d.objectId}).catch(()=>{})}}catch{return!1}finally{e.send("Runtime.releaseObject",{objectId:a.result.objectId}).catch(()=>{})}}state(e){let t=this.pages.get(e);return t||(t={navAt:Date.now(),url:e.url,cmp:null,refused:0,frames:new Set,topAttempts:0},this.pages.set(e,t)),t}reset(e,t){this.pages.set(e,{navAt:Date.now(),url:t,cmp:null,refused:0,frames:new Set,topAttempts:0})}emit(e,t){e==="error"&&this.emitter.listenerCount("error")===0||this.emitter.emit(e,t)}};var fu=["cmpDetected","popupFound","optOutResult","autoconsentDone","selfTestResult","error"],mu=["blocked","error"];async function mn(n,e={}){let t=e.consent===!1?null:new ts(typeof e.consent=="object"?e.consent:{}),s=e.adblock===!1?null:await es.create(typeof e.adblock=="object"?e.adblock:{}),o=[];if(e.onEvent){let r=e.onEvent;if(t)for(let a of fu)o.push(t.on(a,(c=>r({source:"consent",type:a,data:c}))));if(s)for(let a of mu)o.push(s.on(a,(c=>r({source:"adblock",type:a,data:c}))))}let i=l(async r=>{await Promise.all([t?.initSession(r),s?.initSession(r)])},"init");return o.push(n.addSessionInitializer(i)),{consent:t,adblock:s,detach(){for(let r of o)r();t?.dispose(),s?.dispose()}}}l(mn,"attachInterrupts");var gu="pilotfish's open MCP makes no model calls: the MCP client's own model reads the page and decides the next step",wu=l(()=>{let n=l(async()=>{throw new Error(gu)},"refuse");return{messages:{create:n},beta:{messages:{create:n}}}},"noModelClient"),gn=class{static{l(this,"ConfirmThrough")}inner;canAsk;constructor(e,t){this.inner=e,this.canAsk=t}async check(e){let t=await this.inner.check(e);return t.kind!=="confirm"||this.canAsk()?t:{kind:"deny",reason:`${t.reason}. This needs a person's confirmation, and this MCP client cannot ask for one (no elicitation support)`}}},Ye=class{static{l(this,"BrowserSession")}config;opts;browser=null;launching=null;interrupts=null;tabs=null;agent=null;queue=Promise.resolve();policy;asker;constructor(e,t={}){this.config=e,this.opts=t,this.policy=new Me({allowedHosts:e.allowedHosts,allowPrivateNetwork:e.allowPrivateNetwork})}get launched(){return!!this.browser&&!this.browser.closed}async page(){return await this.ensure(),this.tabs.current}get tabTable(){return this.tabs}async ensure(){return this.browser&&!this.browser.closed?this.browser:(this.launching??=this.start().finally(()=>{this.launching=null}),this.launching)}async start(){let e=await bs({...this.opts.launch,headless:this.config.headless});this.interrupts=await mn(e,{consent:!0,adblock:!0}).catch(()=>null);let t=e.allPages()[0]??await e.newPage();return this.tabs=new Re(t),this.browser=e,e.on("disconnected",()=>{this.browser===e&&this.reset()}),e}reset(){this.agent?.dispose(),this.agent=null,this.tabs?.dispose(),this.tabs=null,this.interrupts?.detach(),this.interrupts=null,this.browser=null}executor(){return this.agent??=new mt(this.tabs.current,{tabs:this.tabs,client:wu(),secrets:this.config.secrets,policy:new gn(this.policy,()=>!!this.asker),consent:this.interrupts?.consent??void 0,allowScreenshots:this.config.allowScreenshots,askUser:l(e=>this.asker?.(e)??Promise.resolve(null),"askUser"),fastSettle:!0,enableQuery:!0,compactSnapshots:!0,autoDismiss:!0,awaitLoading:!0,actionSnapshotBand:0,snapshotMoreHint:"browser_scroll for the next screen, browser_get_text for the page's text, or browser_snapshot (whole_page) for every element"}),this.agent}async act(e,t,s={}){let o=this.queue.then(async()=>(await this.ensure(),this.executor().act(e,t,"",s)));return this.queue=o.catch(()=>{}),o}async obstacle(e={}){if(!this.launched)return null;let t=this.queue.then(()=>this.executor().obstacle(e));return this.queue=t.catch(()=>{}),t.catch(()=>null)}async close(){let e=this.browser??(this.launching?await this.launching.catch(()=>null):null);return e?(this.reset(),await e.close().catch(()=>{}),!0):!1}};import{McpServer as yu}from"@modelcontextprotocol/sdk/server/mcp.js";import{ListToolsRequestSchema as bu}from"@modelcontextprotocol/sdk/types.js";import{z as q}from"zod";var ns="0.1.10";var os=Ce.click.shape.ref.describe('From the latest snapshot, e.g. "e12" or "f1e3"'),dr=q.enum(["down","up","left","right"]),ku={click:["ref"],type:["ref","text","submit"],select:["ref","values"],press:["key"],scroll:["direction"],wait:["seconds"]},vu={click:1,type:2,select:2,press:1,scroll:1,wait:1},xu=q.object({action:q.enum(["click","type","select","press","scroll","wait"]).describe("click, select, type take ref; type: text (submit: Enter after); select: values; press: key; scroll: direction; wait: seconds"),ref:os.optional(),text:q.string().max(2e4).optional(),submit:q.boolean().optional(),values:q.array(q.string()).min(1).max(50).optional(),key:q.string().min(1).max(40).optional(),direction:dr.optional(),seconds:q.number().min(.5).max(10).optional()}).strict().superRefine((n,e)=>{let t=ku[n.action];for(let s of t.slice(0,vu[n.action]))n[s]===void 0&&e.addIssue({code:"custom",path:[s],message:`${n.action} needs ${s}`});for(let s of Object.keys(n))s!=="action"&&!t.includes(s)&&e.addIssue({code:"custom",path:[s],message:`${s} is not a field of ${n.action} (it takes ${t.join(", ")})`})}),ae={browser_navigate:q.object({url:q.url({protocol:/^https?$/}).describe("Absolute http(s) URL"),snapshot:q.boolean().optional().describe("Default true. false: no page snapshot, only what happened (title, counts). Only when this same reply also reads the page with browser_query and you will not act on it; keep it for a page you have not seen or whose tabs, sorts, filters or forms you may use.")}).strict(),browser_snapshot:q.object({whole_page:q.boolean().optional()}).strict(),browser_click:q.object({ref:os}).strict(),browser_type:q.object({ref:os,text:q.string().max(2e4),submit:q.boolean().optional().describe("Press Enter afterwards")}).strict(),browser_select:q.object({ref:os,values:q.array(q.string()).min(1).max(50)}).strict(),browser_press:q.object({key:q.string().min(1).max(40).describe('e.g. "Enter", "Escape", "Tab", "ArrowDown", "Control+a"')}).strict(),browser_scroll:q.object({direction:dr}).strict(),browser_screenshot:q.object({}).strict(),browser_get_text:q.object({max_chars:q.number().int().min(500).max(6e4).optional().describe("Default 20000. Ask for less when the start of the page is enough."),selector:q.string().min(1).max(300).optional().describe(`CSS selector: only the first match's text, e.g. "main" or "#results"`)}).strict(),browser_query:q.object({expression:q.string().min(1).max(4e3).describe("e.g. [...document.querySelectorAll('.result')].slice(0,10).map(e => e.innerText)")}).strict(),browser_find:q.object({query:q.string().min(1).max(300).describe('Words naming the element(s), e.g. "Sort by" or "price max input, Go button"')}).strict(),browser_wait:q.object({seconds:q.number().min(.5).max(10)}).strict(),browser_resume:q.object({timeout_s:q.number().int().min(5).max(900).optional().describe("Default 300")}).strict(),browser_actions:q.object({actions:q.array(xu).min(1).max(10)}).strict(),browser_tabs:q.object({switch_to:q.string().min(1).max(10).optional().describe('Tab id to work in from now on, e.g. "t2"')}).strict(),browser_close:q.object({}).strict()};function Se(n,e=!1){return{content:[{type:"text",text:n}],...e?{isError:!0}:{}}}l(Se,"text");function _u(n){let e=Object.keys(n.secrets),t=[e.length?`Secrets are typed as placeholders and never shown: ${e.map(s=>`{{${s}}}`).join(", ")}.`:"",n.allowedHosts.length?`Navigation is limited to: ${n.allowedHosts.join(", ")}.`:""].filter(Boolean);return t.length?t.join(`
415
+ `):void 0}l(_u,"serverInstructions");function wn(n={}){let e=n.config??n.session?.config??kt(),t=!n.session,s=n.session??new Ye(e,n),o=new yu({name:"pilotfish",version:n.version??ns},{capabilities:{logging:{}},instructions:_u(e)}),i=Object.keys(e.secrets),r=i.length?` Secrets: ${i.map(w=>`{{${w}}}`).join(", ")}.`:"",a=l(()=>!!o.server.getClientCapabilities()?.elicitation?.form,"canElicit"),c=l(w=>a()?async S=>{let I=await o.server.elicitInput({message:S,requestedSchema:{type:"object",properties:{answer:{type:"string",title:"Answer",description:'Reply "yes" to allow an action'}},required:["answer"]}},{relatedRequestId:w.requestId,timeout:e.interventionTimeoutMs}).catch(()=>null);return I?.action==="accept"&&typeof I.content?.answer=="string"?I.content.answer:null}:void 0,"askerFor"),d=l(w=>w.replace(/in the live view/g,"in the pilotfish browser window").replace(/Please send the code, or type it/,"Please type the code"),"forWindow"),u=l(w=>w.needs==="solve_in_live_view"||w.needs==="login_in_live_view"||w.needs==="code"||w.needs==="credentials","byHand"),h=l((w,S="")=>[`NEEDS A PERSON (${w.category}${w.vendor?`, ${w.vendor}`:""}): ${d(w.message)}`,S,u(w)&&e.headless?"This browser is headless, so it cannot be done by hand here: tell the user, and that the pilotfish server needs PILOTFISH_HEADLESS=0 (a visible window) for steps like this one.":`Tell the user now, in your reply: what the page needs, and that the pilotfish browser window is open on it (${w.url}). Ask them to do it there and to tell you when it is done. Then call browser_resume: it waits for the page to move on and returns it.`,"Do not try to get past it yourself: pilotfish does not type passwords or codes it was not given, and does not solve CAPTCHAs."].filter(Boolean).join(`
416
416
  `),"needsPerson"),p=l(async()=>{let w=await s.act("read_page",{}).catch(()=>null);return w?w.text||w.summary:""},"pageNow"),f=l(async(w,S=1e3)=>{let I=Date.now()+w,$=await s.obstacle({fresh:!0});for(;$&&Date.now()<I;)await new Promise(g=>setTimeout(g,S)),$=await s.obstacle({fresh:!0});return $},"untilClear"),b=l((w,S)=>({...w,content:[...w.content,{type:"text",text:S}]}),"withNote"),k=l(async(w,S)=>{let I=await s.obstacle();if(!I)return S;if(I.autoResolvable&&!I.neverAutomate&&(I=await f(12e3),!I))return b(S,`A browser check was showing and cleared by itself. The page now:
417
417
  ${await p()}`);if(I.neverAutomate)return b(S,`STOP: ${I.host} shows a bot check, and this site is never automated past one (ticket sellers: the US BOTS Act; UIDAI: the Aadhaar Act). Do not continue on it; tell the user.`);if(I.category==="hard_block"||I.needs==="none_possible")return b(S,`BLOCKED: ${d(I.message)} A person cannot fix this from the browser window: try another way (another page or site), or tell the user.`);if(a()&&!(u(I)&&e.headless)){let $=await o.server.elicitInput({message:`${d(I.message)} (${I.url})`,requestedSchema:{type:"object",properties:{done:{type:"boolean",title:"I have done it in the pilotfish browser window"}},required:["done"]}},{relatedRequestId:w.requestId,timeout:e.interventionTimeoutMs}).catch(()=>null);if($?.action==="accept"&&$.content?.done!==!1){let g=await f(15e3);return g?b(S,h(g,"The person said it is done, but the page still shows it.")):b(S,`The person handled it. The page now:
418
418
  ${await p()}`)}return b(S,h(I,$?"The person was asked in this client and declined or dismissed it.":""))}return b(S,h(I))},"handOff"),m=8,y=0,v=null,_=l(w=>{y++;let S=w.page;if(!S||w.content===void 0)return w.text||w.summary;let I=l(()=>(v={body:S.body,at:y},w.text),"full");if(!v||y-v.at>m)return I();let $=dt(v.body,S.body,{liveRefs:S.liveRefs,truncated:S.truncated});if($&&$.ratio>.5)return I();let g=y-v.at,C=`${S.tabs}PAGE SNAPSHOT (${S.url}${S.title?` \u2014 "${S.title}"`:""}):`,A=`the page snapshot ${g} browser result${g===1?"":"s"} back`,P=$?`CHANGES ONLY since ${A}. Lines not listed are as shown there and their refs still work; "~ " marks an unchanged line shown for context. (browser_snapshot gives the whole screen again.)
419
419
  ${S.note}${$.text}`:`the same as ${A}; its refs still work.${S.note?`
420
420
  ${S.note.trimEnd()}`:""}`;return[w.content,`${C} ${P}`].filter(Boolean).join(`
421
421
 
422
- `)},"pageText"),E=l(async(w,S,I,$=!0,g={})=>{s.asker=c(w);let C=await s.act(S,I,g),A=Se(_(C),!C.ok);return $||S==="wait"||S==="read_page"?k(w,A):A},"direct"),D=new Map,M=l((w,S,I)=>{let $=o.registerTool(w,S,I);return D.set(w,$),$},"tool"),x={readOnlyHint:!0,destructiveHint:!1,openWorldHint:!0},T={readOnlyHint:!1,destructiveHint:!0,openWorldHint:!0};return M("browser_navigate",{title:"Open a URL",description:"Open an http(s) URL in the current tab. Returns evidence and a snapshot of the visible screen (elements carry refs like e12), with counts of what is above and below.",inputSchema:ae.browser_navigate,annotations:T},(w,S)=>E(S,"navigate",{url:w.url},!0,w.snapshot===!1?{snapshot:!1}:{})),M("browser_snapshot",{title:"Read the page",description:"The current page as a compact tree of headings, text and elements with refs (e12) to act on: three screens around the viewport, or everything with whole_page. Refs stay valid while their element is on the page.",inputSchema:ae.browser_snapshot,annotations:x},(w,S)=>E(S,"read_page",w.whole_page?{whole_page:!0}:{},!1)),M("browser_click",{title:"Click",description:"Click an element by ref. Refuses with a reason (obscured, stale, not visible) instead of clicking the wrong thing. Returns evidence of what changed.",inputSchema:ae.browser_click,annotations:T},(w,S)=>E(S,"click",{ref:w.ref})),M("browser_type",{title:"Type",description:`Type into an input, textarea or editable element by ref, replacing its text. Returns evidence.${r}`,inputSchema:ae.browser_type,annotations:T},(w,S)=>E(S,"type",{ref:w.ref,text:w.text,...w.submit?{submit:!0}:{}})),M("browser_select",{title:"Select options",description:"Choose option(s) of a <select> by label or value. Returns evidence.",inputSchema:ae.browser_select,annotations:T},(w,S)=>E(S,"select_option",{ref:w.ref,values:w.values})),M("browser_press",{title:"Press a key",description:"Press a key or shortcut on the focused element. Returns evidence.",inputSchema:ae.browser_press,annotations:T},(w,S)=>E(S,"press",{key:w.key})),M("browser_scroll",{title:"Scroll",description:"Scroll about one screen. Returns evidence and the newly visible elements.",inputSchema:ae.browser_scroll,annotations:{...x,readOnlyHint:!1,destructiveHint:!1}},(w,S)=>E(S,"scroll",{direction:w.direction},!1)),M("browser_screenshot",{title:"Screenshot",description:"JPEG of the current viewport. Prefer browser_snapshot (cheaper, has refs); use this when layout or images matter.",inputSchema:ae.browser_screenshot,annotations:x},async()=>{if(!e.allowScreenshots)return Se("Screenshots are off because secrets are configured (the image could show them). Set PILOTFISH_ALLOW_SCREENSHOTS=1 on the server to allow them.",!0);let w=await s.page(),S=await w.screenshot({format:"jpeg",quality:70}),I=w.browser.viewport;return{content:[{type:"image",data:S.toString("base64"),mimeType:"image/jpeg"},{type:"text",text:`Viewport ${I.width}x${I.height} CSS px. URL: ${w.url}`}]}}),M("browser_get_text",{title:"Page text",description:"Readable text of the page (PDFs too), secrets masked, without refs. To read a few values or a list, browser_query is far smaller.",inputSchema:ae.browser_get_text,annotations:x},(w,S)=>E(S,"get_text",{...w.max_chars?{max_chars:w.max_chars}:{},...w.selector?{selector:w.selector}:{}},!1)),M("browser_query",{title:"Read values with JavaScript",description:"Read just the values you need (prices, counts, titles, a results list) with a read-only JavaScript expression returning JSON (at most 4000 characters): the cheapest way to read, far smaller than browser_get_text or a snapshot. It runs in a world of its own where clicks, typing, submits and network writes are refused.",inputSchema:ae.browser_query,annotations:x},(w,S)=>E(S,"query",{expression:w.expression},!1)),M("browser_find",{title:"Find elements",description:"Refs of elements anywhere on the page, outside the visible screen too, by the words they show or are labelled with, best match first. Separate several targets by commas.",inputSchema:ae.browser_find,annotations:x},(w,S)=>E(S,"find",{query:w.query},!1)),M("browser_actions",{title:"Several actions in one call",description:"Several steps in order in one call, e.g. fill a form's fields and submit it; each gets the same checks as its own tool. Stops at the first that fails, or after one that moves the page to another address (later refs would belong to the old page). Returns a line per step and the evidence and visible screen after the last one run.",inputSchema:ae.browser_actions,annotations:T},async(w,S)=>{s.asker=c(S);let I=[],$=null,g=!1;for(let[C,A]of w.actions.entries()){let P=(await s.page()).lastDocumentAt,[V,z]=A.action==="click"?["click",{ref:A.ref}]:A.action==="type"?["type",{ref:A.ref,text:A.text,...A.submit?{submit:!0}:{}}]:A.action==="select"?["select_option",{ref:A.ref,values:A.values}]:A.action==="press"?["press",{key:A.key}]:A.action==="scroll"?["scroll",{direction:A.direction}]:["wait",{seconds:A.seconds}],R=await s.act(V,z);if(I.push(`${C+1}. ${R.ok?"":"FAILED "}${R.summary}`),$=R,!R.ok){g=!0,C<w.actions.length-1&&I.push(`Stopped: steps ${C+2}-${w.actions.length} were not run.`);break}let F=await s.page();if(F.lastDocumentAt!==P&&C<w.actions.length-1){I.push(`Stopped: the page moved to ${F.url}; steps ${C+2}-${w.actions.length} were not run (their refs belong to the previous page).`);break}}return k(S,Se(`${I.join(`
422
+ `)},"pageText"),E=l(async(w,S,I,$=!0,g={})=>{s.asker=c(w);let C=await s.act(S,I,g),A=Se(_(C),!C.ok);return $||S==="wait"||S==="read_page"?k(w,A):A},"direct"),D=new Map,M=l((w,S,I)=>{let $=o.registerTool(w,S,I);return D.set(w,$),$},"tool"),x={readOnlyHint:!0,destructiveHint:!1,openWorldHint:!0},T={readOnlyHint:!1,destructiveHint:!0,openWorldHint:!0};return M("browser_navigate",{title:"Open a URL",description:"Open an http(s) URL in the current tab. Returns evidence and a snapshot of the visible screen (elements carry refs like e12), with counts of what is above and below. Words like cheapest, latest, highest-rated or most mean the site's own sort or tab for that order (a list's default order is usually something else, e.g. 'Best' or 'Featured'): apply it, then read the top.",inputSchema:ae.browser_navigate,annotations:T},(w,S)=>E(S,"navigate",{url:w.url},!0,w.snapshot===!1?{snapshot:!1}:{})),M("browser_snapshot",{title:"Read the page",description:"The current page as a compact tree of headings, text and elements with refs (e12) to act on: three screens around the viewport, or everything with whole_page. Refs stay valid while their element is on the page.",inputSchema:ae.browser_snapshot,annotations:x},(w,S)=>E(S,"read_page",w.whole_page?{whole_page:!0}:{},!1)),M("browser_click",{title:"Click",description:"Click an element by ref. Refuses with a reason (obscured, stale, not visible) instead of clicking the wrong thing. Returns evidence of what changed.",inputSchema:ae.browser_click,annotations:T},(w,S)=>E(S,"click",{ref:w.ref})),M("browser_type",{title:"Type",description:`Type into an input, textarea or editable element by ref, replacing its text. Returns evidence.${r}`,inputSchema:ae.browser_type,annotations:T},(w,S)=>E(S,"type",{ref:w.ref,text:w.text,...w.submit?{submit:!0}:{}})),M("browser_select",{title:"Select options",description:"Choose option(s) of a <select> by label or value. Returns evidence.",inputSchema:ae.browser_select,annotations:T},(w,S)=>E(S,"select_option",{ref:w.ref,values:w.values})),M("browser_press",{title:"Press a key",description:"Press a key or shortcut on the focused element. Returns evidence.",inputSchema:ae.browser_press,annotations:T},(w,S)=>E(S,"press",{key:w.key})),M("browser_scroll",{title:"Scroll",description:"Scroll about one screen. Returns evidence and the newly visible elements.",inputSchema:ae.browser_scroll,annotations:{...x,readOnlyHint:!1,destructiveHint:!1}},(w,S)=>E(S,"scroll",{direction:w.direction},!1)),M("browser_screenshot",{title:"Screenshot",description:"JPEG of the current viewport. Prefer browser_snapshot (cheaper, has refs); use this when layout or images matter.",inputSchema:ae.browser_screenshot,annotations:x},async()=>{if(!e.allowScreenshots)return Se("Screenshots are off because secrets are configured (the image could show them). Set PILOTFISH_ALLOW_SCREENSHOTS=1 on the server to allow them.",!0);let w=await s.page(),S=await w.screenshot({format:"jpeg",quality:70}),I=w.browser.viewport;return{content:[{type:"image",data:S.toString("base64"),mimeType:"image/jpeg"},{type:"text",text:`Viewport ${I.width}x${I.height} CSS px. URL: ${w.url}`}]}}),M("browser_get_text",{title:"Page text",description:"Readable text of the page (PDFs too), secrets masked, without refs. To read a few values or a list, browser_query is far smaller.",inputSchema:ae.browser_get_text,annotations:x},(w,S)=>E(S,"get_text",{...w.max_chars?{max_chars:w.max_chars}:{},...w.selector?{selector:w.selector}:{}},!1)),M("browser_query",{title:"Read values with JavaScript",description:"Read just the values you need (prices, counts, titles, a results list) with a read-only JavaScript expression returning JSON (at most 4000 characters): the cheapest way to read, far smaller than browser_get_text or a snapshot. It runs in a world of its own where clicks, typing, submits and network writes are refused.",inputSchema:ae.browser_query,annotations:x},(w,S)=>E(S,"query",{expression:w.expression},!1)),M("browser_find",{title:"Find elements",description:"Refs of elements anywhere on the page, outside the visible screen too, by the words they show or are labelled with, best match first. Separate several targets by commas.",inputSchema:ae.browser_find,annotations:x},(w,S)=>E(S,"find",{query:w.query},!1)),M("browser_actions",{title:"Several actions in one call",description:"Several steps in order in one call, e.g. fill a form's fields and submit it; each gets the same checks as its own tool. Stops at the first that fails, or after one that moves the page to another address (later refs would belong to the old page). Returns a line per step and the evidence and visible screen after the last one run.",inputSchema:ae.browser_actions,annotations:T},async(w,S)=>{s.asker=c(S);let I=[],$=null,g=!1;for(let[C,A]of w.actions.entries()){let P=(await s.page()).lastDocumentAt,[V,z]=A.action==="click"?["click",{ref:A.ref}]:A.action==="type"?["type",{ref:A.ref,text:A.text,...A.submit?{submit:!0}:{}}]:A.action==="select"?["select_option",{ref:A.ref,values:A.values}]:A.action==="press"?["press",{key:A.key}]:A.action==="scroll"?["scroll",{direction:A.direction}]:["wait",{seconds:A.seconds}],R=await s.act(V,z);if(I.push(`${C+1}. ${R.ok?"":"FAILED "}${R.summary}`),$=R,!R.ok){g=!0,C<w.actions.length-1&&I.push(`Stopped: steps ${C+2}-${w.actions.length} were not run.`);break}let F=await s.page();if(F.lastDocumentAt!==P&&C<w.actions.length-1){I.push(`Stopped: the page moved to ${F.url}; steps ${C+2}-${w.actions.length} were not run (their refs belong to the previous page).`);break}}return k(S,Se(`${I.join(`
423
423
  `)}
424
424
 
425
- ${$?_($):""}`,g))}),M("browser_resume",{title:"Continue after the person",description:"After NEEDS A PERSON, once the user was asked to do it in the pilotfish browser window (when they say it is done, or right after asking): waits until the page has moved on, then returns it.",inputSchema:ae.browser_resume,annotations:x},async w=>{if(!s.launched)return Se("No browser is open.",!0);let S=w.timeout_s??300,I=await f(S*1e3,1500);return Se(I?h(I,`Still waiting after ${S} s.`):`The page needs nobody now. The page:
425
+ ${$?_($):""}`,g))}),M("browser_resume",{title:"Continue after the person",description:"When a result says NEEDS A PERSON (a sign-in, a one-time code or 2FA, a device approval, a CAPTCHA), tell the user in your reply what the page needs and that the pilotfish browser window is open on it; when they have done it there (or right after asking), call this and carry on: it waits until the page has moved on, then returns it. BLOCKED and STOP mean the site cannot be continued that way.",inputSchema:ae.browser_resume,annotations:x},async w=>{if(!s.launched)return Se("No browser is open.",!0);let S=w.timeout_s??300,I=await f(S*1e3,1500);return Se(I?h(I,`Still waiting after ${S} s.`):`The page needs nobody now. The page:
426
426
  ${await p()}`)}),M("browser_wait",{title:"Wait",description:"Wait 0.5-10 seconds, then return what changed. Only for a page that is visibly still loading: every action already waits for the requests it started.",inputSchema:ae.browser_wait,annotations:x},(w,S)=>E(S,"wait",{seconds:w.seconds},!1)),M("browser_tabs",{title:"Tabs",description:"List the open tabs (the current one is marked). switch_to makes another tab current and returns its snapshot.",inputSchema:ae.browser_tabs,annotations:{...x,readOnlyHint:!1,destructiveHint:!1}},async(w,S)=>{if(w.switch_to)return E(S,"switch_tab",{tab:w.switch_to});await s.page();let I=s.tabTable?.describe()??"";return Se(I?I.split(" | ").join(`
427
427
  `):"No open tabs.")}),M("browser_close",{title:"Close the browser",description:"Only to start over: closes the browser, and the next call starts a fresh one (cookies and logins are gone). It closes by itself when the client disconnects.",inputSchema:ae.browser_close,annotations:{readOnlyHint:!1,destructiveHint:!0,openWorldHint:!1}},async()=>Se(await s.close()?"Closed the browser.":"No browser was open.")),o.server.setRequestHandler(bu,()=>({tools:[...D].filter(([,w])=>w.enabled).map(([w,S])=>{let{$schema:I,...$}=q.toJSONSchema(S.inputSchema,{target:"draft-07",io:"input"});return{name:w,title:S.title,description:S.description,inputSchema:$,annotations:S.annotations,execution:S.execution,_meta:S._meta}})})),{server:o,session:s,async close(){await o.close().catch(()=>{}),t&&await s.close()}}}l(wn,"createMcpServer");var Au=`pilotfish-mcp \u2014 pilotfish browser automation as an open MCP server
428
428
  Your MCP client's own model drives the browser: no API key, no model calls here.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gaiventures/pilotfish-mcp",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "A real Chromium on your machine for any MCP agent: ref-based tools, evidence per action, no API key.",
5
5
  "license": "MIT",
6
6
  "type": "module",