@buoy-gg/mcp 7.0.78 → 7.0.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/SyncClient-gqMRmB79.d.mts +374 -0
- package/dist/SyncClient-gqMRmB79.d.ts +374 -0
- package/dist/{chunk-LHGIYW7N.mjs → chunk-3FTJ74HL.mjs} +1 -1
- package/dist/{chunk-XA74T4JN.mjs → chunk-5XATNNKY.mjs} +2 -2
- package/dist/{chunk-ODGXMS2O.mjs → chunk-FYJWWTAC.mjs} +5 -4
- package/dist/chunk-HZEASQD3.mjs +980 -0
- package/dist/{chunk-2IWYZICF.mjs → chunk-MR4NVULE.mjs} +1 -1
- package/dist/{chunk-3STZVJQG.mjs → chunk-SGVMY5TU.mjs} +1 -1
- package/dist/chunk-TMN3LXKB.mjs +42 -0
- package/dist/cli.js +285 -222
- package/dist/cli.mjs +12 -12
- package/dist/flowRun-4VU3KXYX.mjs +2 -0
- package/dist/index.d.mts +9 -370
- package/dist/index.d.ts +9 -370
- package/dist/index.js +231 -168
- package/dist/index.mjs +1 -1
- package/dist/{init-VCQMVZXB.mjs → init-6ANI6DIF.mjs} +1 -1
- package/dist/{main-6OX5577P.mjs → main-XP2I2BCD.mjs} +2 -2
- package/dist/nativeProfiler.d.mts +184 -0
- package/dist/nativeProfiler.d.ts +184 -0
- package/dist/nativeProfiler.js +157 -0
- package/dist/nativeProfiler.mjs +1 -0
- package/dist/{simFarm-R4P6QXRK.mjs → simFarm-AWVHVU6A.mjs} +1 -1
- package/dist/{src-R7ICMP6Q.mjs → src-HRUK7GEE.mjs} +1 -1
- package/package.json +16 -5
- package/dist/chunk-HEPFVYZG.mjs +0 -959
- package/dist/flowRun-XEK65IL7.mjs +0 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import{createRequire as q}from"module";const fe=q(import.meta.url);import I from"fs";import R from"os";import y from"path";var G='// Buoy Web helper. Chrome starts it for the Buoy Web extension (native messaging) and\n// ends it when Chrome closes. It passes messages between the extension and Buoy MCP\n// sessions, which connect over a Unix socket only this user can open.\n//\n// extension <-- stdin/stdout (native messaging) --> helper <-- ~/.buoy/web/chrome-<pid>.sock --> MCP sessions\nimport fs from "node:fs";\nimport net from "node:net";\nimport os from "node:os";\nimport path from "node:path";\n\nconst dir = process.env.BUOY_WEB_DIR ?? path.join(os.homedir(), ".buoy", "web");\nfs.mkdirSync(dir, { recursive: true, mode: 0o700 });\nfs.chmodSync(dir, 0o700);\nconst sock = path.join(dir, `chrome-${process.pid}.sock`);\nconst info = path.join(dir, `chrome-${process.pid}.json`);\n\n// ---- native messaging: 4-byte little-endian length, then JSON -----------------\nlet inbuf = Buffer.alloc(0);\nprocess.stdin.on("data", (chunk) => {\n inbuf = Buffer.concat([inbuf, chunk]);\n while (inbuf.length >= 4) {\n const n = inbuf.readUInt32LE(0);\n if (inbuf.length < 4 + n) break;\n const msg = JSON.parse(inbuf.subarray(4, 4 + n).toString("utf8"));\n inbuf = inbuf.subarray(4 + n);\n fromExtension(msg);\n }\n});\nprocess.stdin.on("end", quit); // Chrome closed or the extension let go\nprocess.stdout.on("error", quit);\nfunction toExtension(msg) {\n const body = Buffer.from(JSON.stringify(msg), "utf8");\n if (body.length > 1024 * 1024) throw new Error("Message too big for Chrome (over 1 MB).");\n const head = Buffer.alloc(4);\n head.writeUInt32LE(body.length, 0);\n process.stdout.write(Buffer.concat([head, body]));\n}\n\n// ---- MCP sessions: one JSON message per line ------------------------------------\nconst clients = new Map(); // id -> socket\nlet nextClient = 1;\nfunction fromExtension(msg) {\n const s = clients.get(msg.c);\n if (!s) return;\n const { c, ...rest } = msg;\n s.write(JSON.stringify(rest) + "\\n");\n}\n\nconst server = net.createServer((s) => {\n const c = nextClient++;\n clients.set(c, s);\n let buf = "";\n s.setEncoding("utf8");\n s.on("data", (d) => {\n buf += d;\n let i;\n while ((i = buf.indexOf("\\n")) >= 0) {\n const line = buf.slice(0, i);\n buf = buf.slice(i + 1);\n if (!line) continue;\n let msg;\n try {\n msg = JSON.parse(line);\n toExtension({ ...msg, c });\n } catch (e) {\n if (msg?.id != null) s.write(JSON.stringify({ id: msg.id, error: String(e.message ?? e) }) + "\\n");\n }\n }\n });\n const gone = () => {\n if (!clients.delete(c)) return;\n try {\n toExtension({ c, method: "client.closed" });\n } catch {}\n };\n s.on("close", gone);\n s.on("error", gone);\n});\n\n// Clear out sockets left by helpers that died without cleaning up.\nfor (const f of fs.readdirSync(dir)) {\n const m = /^chrome-(\\d+)\\.(sock|json)$/.exec(f);\n if (!m || Number(m[1]) === process.pid) continue;\n try {\n process.kill(Number(m[1]), 0);\n } catch {\n fs.rmSync(path.join(dir, f), { force: true });\n }\n}\n\nconst old = process.umask(0o177); // the socket file is created 0600\nserver.listen(sock, () => {\n process.umask(old);\n fs.writeFileSync(info, JSON.stringify({ pid: process.pid, sock, started: Date.now() }), { mode: 0o600 });\n // Tells Buoy the extension is set up, so a closed Chrome isn\'t mistaken for "no extension".\n fs.writeFileSync(path.join(dir, "seen"), new Date().toISOString(), { mode: 0o600 });\n toExtension({ ready: true }); // the extension shows "connected" only after this\n});\n\nfunction quit() {\n for (const f of [sock, info]) fs.rmSync(f, { force: true });\n process.exit(0);\n}\nprocess.on("SIGTERM", quit);\nprocess.on("SIGINT", quit);\n';import V from"crypto";import N from"fs";import X from"http";import K from"net";import z from"os";import U from"path";import{WebSocketServer as Q}from"ws";var Z=process.env.BUOY_WEB_RELAYLOG==="1";var M=(...n)=>Z&&process.stderr.write(`[buoy-web relay] ${n.join(" ")}
|
|
2
2
|
`);var x=()=>process.env.BUOY_WEB_DIR??U.join(z.homedir(),".buoy","web");function ee(n=x()){let e=[];try{e=N.readdirSync(n)}catch{return[]}const i=[];for(const o of e){const a=/^chrome-(\d+)\.json$/.exec(o);if(!a)continue;try{process.kill(Number(a[1]),0);const b=JSON.parse(N.readFileSync(U.join(n,o),"utf8"));if(N.existsSync(b.sock))i.push(b)}catch{}}return i.sort((o,a)=>a.started-o.started)}function te(n){return new Promise((e,i)=>{const o=K.createConnection(n);const a=new Map;let b=1;let l="";let d=false;let v=false;const w={onEvent:()=>{},onClose:()=>{},call(m,S){if(v)return Promise.reject(new Error("Chrome's Buoy helper closed."));const c=b++;o.write(JSON.stringify({id:c,method:m,params:S})+"\n");return new Promise((O,B)=>a.set(c,{res:O,rej:B}))},close:()=>o.end()};o.setEncoding("utf8");o.on("connect",()=>{d=true;e(w)});o.on("error",m=>{if(!d)i(m)});o.on("data",m=>{l+=m;let S;while((S=l.indexOf("\n"))>=0){const c=JSON.parse(l.slice(0,S));l=l.slice(S+1);if(c.id!=null&&a.has(c.id)){const{res:O,rej:B}=a.get(c.id);a.delete(c.id);if(c.error)B(new Error(c.error));else O(c.result)}else if(c.event)w.onEvent(c.event,c.params)}});o.on("close",()=>{v=true;for(const{rej:m}of a.values())m(new Error("Chrome's Buoy helper closed."));a.clear();w.onClose()})})}async function ne({sock:n}){const e=await te(n);const i=await e.call("hello");const o=new Map;let a=1;let b=false;let l=null;const d=new Set;const v=t=>{if(l?.readyState===1)l.send(JSON.stringify(t))};const w=t=>[...o.values()].find(t);const m={url:"",hello:i,onDetach:()=>{},onClose:()=>{},idle:()=>Promise.all([...d]),close:()=>{}};async function S(t,{settle:r=false}={}){const s=o.get(t);if(s)return s;await e.call("debugger.attach",{tabId:t});if(r){const E=Date.now();while(Date.now()-E<5e3){const T=await e.call("debugger.send",{tabId:t,method:"Runtime.evaluate",params:{expression:"[location.href, document.readyState]",returnByValue:true}}).catch(()=>null);const[D,L]=T?.result?.value??[];if(D&&D!=="about:blank"&&L!=="loading")break;await new Promise(H=>setTimeout(H,50))}}const{targetInfo:u}=await e.call("debugger.send",{tabId:t,method:"Target.getTargetInfo"});const{openerId:g,openerFrameId:f,...p}=u;p.canAccessOpener=false;const h={tabId:t,sessionId:`buoy-tab-${a++}`,targetInfo:p,children:new Set,framesSent:false,held:[]};o.set(t,h);v({method:"Target.attachedToTarget",params:{sessionId:h.sessionId,targetInfo:{...p,attached:true},waitingForDebugger:false}});return h}function c(t){const r=o.get(t);if(!r)return;o.delete(t);v({method:"Target.detachedFromTarget",params:{sessionId:r.sessionId,targetId:r.targetInfo?.targetId}})}e.onEvent=(t,r)=>{if(t==="debugger.event"){const s=o.get(r.tabId);if(!s)return;const u=r.params?.sessionId;if(r.method==="Target.attachedToTarget"&&u)s.children.add(u);if(r.method==="Target.detachedFromTarget"&&u)s.children.delete(u);const g={sessionId:r.sessionId||s.sessionId,method:r.method,params:r.params};if(!s.framesSent)s.held.push(g);else v(g)}else if(t==="debugger.detached"){c(r.tabId);m.onDetach(r)}else if(t==="tabs.created"){if(b){const s=S(r.id,{settle:true}).catch(()=>{});d.add(s);void s.finally(()=>d.delete(s))}}else if(t==="tabs.removed")c(r.tabId)};e.onClose=()=>{for(const t of[...o.keys()])c(t);l?.close();m.onClose()};async function O(t,r,s){if(!s){switch(t){case"Browser.getVersion":return{protocolVersion:"1.3",product:"Chrome/BuoyWeb",userAgent:i.userAgent,jsVersion:""};case"Browser.setDownloadBehavior":case"Target.setDiscoverTargets":case"Browser.close":return{};case"Target.setAutoAttach":b=true;return{};case"Target.getTargetInfo":return{targetInfo:{targetId:"buoy-browser",type:"browser",title:"",url:"",attached:true,canAccessOpener:false}};case"Target.getBrowserContexts":return{browserContextIds:[]};case"Target.createTarget":{const p=await e.call("tabs.create",{url:r?.url});const h=await S(p.id);return{targetId:h.targetInfo.targetId}}case"Target.closeTarget":{const p=w(h=>h.targetInfo?.targetId===r?.targetId);if(!p)return{success:false};await e.call("tabs.remove",{tabId:p.tabId});return{success:true}}}const f=o.values().next().value;if(!f)throw new Error(`No Buoy tab to run ${t} on.`);return e.call("debugger.send",{tabId:f.tabId,method:t,params:r})}if(t==="Target.getTargetInfo"){const f=w(p=>p.sessionId===s);if(f)return{targetInfo:f.targetInfo}}let u=w(f=>f.sessionId===s);let g;if(!u){u=w(f=>f.children.has(s));g=s}if(!u)throw new Error(`No Buoy tab for session ${s}.`);return e.call("debugger.send",{tabId:u.tabId,sessionId:g,method:t,params:r})}const B=V.randomUUID();const _=X.createServer((t,r)=>r.writeHead(404).end());const C=new Q({server:_});C.on("connection",(t,r)=>{if(r.url!==`/${B}`||l)return t.close(4004,"No.");l=t;t.on("message",async s=>{const{id:u,sessionId:g,method:f,params:p}=JSON.parse(s.toString());try{const h=await O(f,p,g);M("ok ",g??"-",f);t.send(JSON.stringify({id:u,sessionId:g,result:h??{}}));if(f==="Page.getFrameTree"){const E=w(T=>T.sessionId===g);if(E&&!E.framesSent){E.framesSent=true;for(const T of E.held.splice(0))v(T)}}}catch(h){M("ERR",g??"-",f,h.message);t.send(JSON.stringify({id:u,sessionId:g,error:{message:String(h.message??h)}}))}});t.on("close",()=>{l=null;e.close()})});await new Promise(t=>_.listen(0,"127.0.0.1",t));m.url=`ws://127.0.0.1:${_.address().port}/${B}`;m.close=()=>{e.close();C.close();_.close()};return m}var J="gg.buoy.web";var $=["cfolfiajhlenfifknbomdogooagnlamf","kjbgbjimhjiocnfifnemmenkondjeifp",...(process.env.BUOY_WEB_EXTENSION_IDS??"").split(",").filter(Boolean)];var A=["Google/Chrome","Google/Chrome Beta","Google/Chrome Dev","Google/Chrome Canary","Chromium","BraveSoftware/Brave-Browser","Microsoft Edge","Vivaldi","Arc/User Data"];function P(){if(process.env.BUOY_WEB_NMH_DIRS)return process.env.BUOY_WEB_NMH_DIRS.split(y.delimiter).filter(Boolean);if(process.platform!=="darwin")return[];const n=y.join(R.homedir(),"Library","Application Support");return A.map(e=>y.join(n,e)).filter(e=>I.existsSync(e)).map(e=>y.join(e,"NativeMessagingHosts"))}var j=(n,e,i)=>{try{if(I.readFileSync(n,"utf8")===e)return}catch{}I.writeFileSync(n,e,{mode:i});I.chmodSync(n,i)};function oe(){try{const n=y.join(x(),"helper");I.mkdirSync(n,{recursive:true,mode:448});const e=y.join(n,"helper.mjs");const i=y.join(n,"helper.sh");j(e,G,384);const o=d=>`'${d.replace(/'/g,`'\\''`)}'`;const a=process.env.BUOY_WEB_DIR?`BUOY_WEB_DIR=${o(process.env.BUOY_WEB_DIR)} `:"";j(i,`#!/bin/sh
|
|
3
3
|
${a}exec ${o(process.execPath)} ${o(e)} "$@"
|
|
4
|
-
`,448);const b=JSON.stringify({name:J,description:"Buoy Web helper: lets the Buoy MCP use Buoy's own tabs through the Buoy Web extension.",path:i,type:"stdio",allowed_origins:$.map(d=>`chrome-extension://${d}/`)},null,2);const l=[];for(const d of P()){try{I.mkdirSync(d,{recursive:true});j(y.join(d,`${J}.json`),b+"\n",420);l.push(d)}catch{}}return l}catch{return[]}}function re(){if(process.env.BUOY_WEB_NMH_DIRS)return false;if(process.platform!=="darwin")return false;const n=y.join(R.homedir(),"Library","Application Support");for(const e of A){let i=[];try{i=I.readdirSync(y.join(n,e))}catch{continue}for(const o of i){if(o!=="Default"&&!/^Profile \d+$/.test(o))continue;if($.some(a=>I.existsSync(y.join(n,e,o,"Extensions",a))))return true}}return false}import se from"crypto";import k from"fs";import W from"path";var ie=process.env.BUOY_TELEMETRY_URL??"https://buoy.gg/api/t";var ae=24*60*60*1e3;function Y(){const n=(process.env.BUOY_TELEMETRY??"").toLowerCase();return n==="0"||n==="false"||n==="off"||process.env.DO_NOT_TRACK==="1"}var F=false;function ce(){if(F||Y())return;F=true;try{const n=W.join(x(),"install.json");let e={};try{e=JSON.parse(k.readFileSync(n,"utf8"))}catch{}if(typeof e.installId!=="string")e={installId:se.randomUUID(),lastPingAt:0};if(Date.now()-(e.lastPingAt??0)<ae)return;e.lastPingAt=Date.now();k.mkdirSync(W.dirname(n),{recursive:true,mode:448});k.writeFileSync(n,JSON.stringify(e),{mode:384});const i=JSON.stringify({installId:e.installId,version:"7.0.
|
|
4
|
+
`,448);const b=JSON.stringify({name:J,description:"Buoy Web helper: lets the Buoy MCP use Buoy's own tabs through the Buoy Web extension.",path:i,type:"stdio",allowed_origins:$.map(d=>`chrome-extension://${d}/`)},null,2);const l=[];for(const d of P()){try{I.mkdirSync(d,{recursive:true});j(y.join(d,`${J}.json`),b+"\n",420);l.push(d)}catch{}}return l}catch{return[]}}function re(){if(process.env.BUOY_WEB_NMH_DIRS)return false;if(process.platform!=="darwin")return false;const n=y.join(R.homedir(),"Library","Application Support");for(const e of A){let i=[];try{i=I.readdirSync(y.join(n,e))}catch{continue}for(const o of i){if(o!=="Default"&&!/^Profile \d+$/.test(o))continue;if($.some(a=>I.existsSync(y.join(n,e,o,"Extensions",a))))return true}}return false}import se from"crypto";import k from"fs";import W from"path";var ie=process.env.BUOY_TELEMETRY_URL??"https://buoy.gg/api/t";var ae=24*60*60*1e3;function Y(){const n=(process.env.BUOY_TELEMETRY??"").toLowerCase();return n==="0"||n==="false"||n==="off"||process.env.DO_NOT_TRACK==="1"}var F=false;function ce(){if(F||Y())return;F=true;try{const n=W.join(x(),"install.json");let e={};try{e=JSON.parse(k.readFileSync(n,"utf8"))}catch{}if(typeof e.installId!=="string")e={installId:se.randomUUID(),lastPingAt:0};if(Date.now()-(e.lastPingAt??0)<ae)return;e.lastPingAt=Date.now();k.mkdirSync(W.dirname(n),{recursive:true,mode:448});k.writeFileSync(n,JSON.stringify(e),{mode:384});const i=JSON.stringify({installId:e.installId,version:"7.0.79",platform:process.platform==="darwin"?"macos":process.platform==="win32"?"windows":"linux",tier:"locked",surface:"web-mcp"});void fetch(ie,{method:"POST",headers:{"content-type":"application/json"},body:i,signal:AbortSignal.timeout(5e3)}).catch(()=>{})}catch{}}export{x as webDir,ee as findHelpers,ne as startRelay,P as helperManifestDirs,oe as ensureHelper,re as extensionInstalled,Y as telemetryOff,ce as noteUse};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createRequire as e}from"module";const a=e(import.meta.url);var r="7.0.
|
|
1
|
+
import{createRequire as e}from"module";const a=e(import.meta.url);var r="7.0.79"?"7.0.79":"0.0.0-dev";var o="@buoy-gg/mcp";export{r as VERSION,o as PACKAGE_NAME};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import{createRequire as _i}from"module";const Qs=_i(import.meta.url);import{bootEnv as Ei,farmAdbTarget as rt,farmAdbTargets as ki,simLaunchEnv as Si}from"./chunk-FYJWWTAC.mjs";import{currentAccess as se,hasProAccess as J,isFreeAppCall as Ti,meetsAccess as ve,outsideCalls as Ai,proRequiredMessage as de}from"./chunk-D5K6WO6B.mjs";import{__commonJS as $,__require as x,__toESM as nt}from"./chunk-MTTWR4T7.mjs";var st=$({"../../node_modules/pngjs/lib/chunkstream.js"(e,t){"use strict";var i=x("util");var n=x("stream");var o=t.exports=function(){n.call(this);this._buffers=[];this._buffered=0;this._reads=[];this._paused=false;this._encoding="utf8";this.writable=true};i.inherits(o,n);o.prototype.read=function(r,a){this._reads.push({length:Math.abs(r),allowLess:r<0,func:a});process.nextTick(function(){this._process();if(this._paused&&this._reads&&this._reads.length>0){this._paused=false;this.emit("drain")}}.bind(this))};o.prototype.write=function(r,a){if(!this.writable){this.emit("error",new Error("Stream not writable"));return false}let s;if(Buffer.isBuffer(r)){s=r}else{s=Buffer.from(r,a||this._encoding)}this._buffers.push(s);this._buffered+=s.length;this._process();if(this._reads&&this._reads.length===0){this._paused=true}return this.writable&&!this._paused};o.prototype.end=function(r,a){if(r){this.write(r,a)}this.writable=false;if(!this._buffers){return}if(this._buffers.length===0){this._end()}else{this._buffers.push(null);this._process()}};o.prototype.destroySoon=o.prototype.end;o.prototype._end=function(){if(this._reads.length>0){this.emit("error",new Error("Unexpected end of input"))}this.destroy()};o.prototype.destroy=function(){if(!this._buffers){return}this.writable=false;this._reads=null;this._buffers=null;this.emit("close")};o.prototype._processReadAllowingLess=function(r){this._reads.shift();let a=this._buffers[0];if(a.length>r.length){this._buffered-=r.length;this._buffers[0]=a.slice(r.length);r.func.call(this,a.slice(0,r.length))}else{this._buffered-=a.length;this._buffers.shift();r.func.call(this,a)}};o.prototype._processRead=function(r){this._reads.shift();let a=0;let s=0;let c=Buffer.alloc(r.length);while(a<r.length){let l=this._buffers[s++];let h=Math.min(l.length,r.length-a);l.copy(c,a,0,h);a+=h;if(h!==l.length){this._buffers[--s]=l.slice(h)}}if(s>0){this._buffers.splice(0,s)}this._buffered-=r.length;r.func.call(this,c)};o.prototype._process=function(){try{while(this._buffered>0&&this._reads&&this._reads.length>0){let r=this._reads[0];if(r.allowLess){this._processReadAllowingLess(r)}else if(this._buffered>=r.length){this._processRead(r)}else{break}}if(this._buffers&&!this.writable){this._end()}}catch(r){this.emit("error",r)}}}});var ot=$({"../../node_modules/pngjs/lib/interlace.js"(e){"use strict";var t=[{x:[0],y:[0]},{x:[4],y:[0]},{x:[0,4],y:[4]},{x:[2,6],y:[0,4]},{x:[0,2,4,6],y:[2,6]},{x:[1,3,5,7],y:[0,2,4,6]},{x:[0,1,2,3,4,5,6,7],y:[1,3,5,7]}];e.getImagePasses=function(i,n){let o=[];let r=i%8;let a=n%8;let s=(i-r)/8;let c=(n-a)/8;for(let l=0;l<t.length;l++){let h=t[l];let u=s*h.x.length;let d=c*h.y.length;for(let f=0;f<h.x.length;f++){if(h.x[f]<r){u++}else{break}}for(let f=0;f<h.y.length;f++){if(h.y[f]<a){d++}else{break}}if(u>0&&d>0){o.push({width:u,height:d,index:l})}}return o};e.getInterlaceIterator=function(i){return function(n,o,r){let a=n%t[r].x.length;let s=(n-a)/t[r].x.length*8+t[r].x[a];let c=o%t[r].y.length;let l=(o-c)/t[r].y.length*8+t[r].y[c];return s*4+l*i*4}}}});var at=$({"../../node_modules/pngjs/lib/paeth-predictor.js"(e,t){"use strict";t.exports=function i(n,o,r){let a=n+o-r;let s=Math.abs(a-n);let c=Math.abs(a-o);let l=Math.abs(a-r);if(s<=c&&s<=l){return n}if(c<=l){return o}return r}}});var ct=$({"../../node_modules/pngjs/lib/filter-parse.js"(e,t){"use strict";var i=ot();var n=at();function o(a,s,c){let l=a*s;if(c!==8){l=Math.ceil(l/(8/c))}return l}var r=t.exports=function(a,s){let c=a.width;let l=a.height;let h=a.interlace;let u=a.bpp;let d=a.depth;this.read=s.read;this.write=s.write;this.complete=s.complete;this._imageIndex=0;this._images=[];if(h){let f=i.getImagePasses(c,l);for(let m=0;m<f.length;m++){this._images.push({byteWidth:o(f[m].width,u,d),height:f[m].height,lineIndex:0})}}else{this._images.push({byteWidth:o(c,u,d),height:l,lineIndex:0})}if(d===8){this._xComparison=u}else if(d===16){this._xComparison=u*2}else{this._xComparison=1}};r.prototype.start=function(){this.read(this._images[this._imageIndex].byteWidth+1,this._reverseFilterLine.bind(this))};r.prototype._unFilterType1=function(a,s,c){let l=this._xComparison;let h=l-1;for(let u=0;u<c;u++){let d=a[1+u];let f=u>h?s[u-l]:0;s[u]=d+f}};r.prototype._unFilterType2=function(a,s,c){let l=this._lastLine;for(let h=0;h<c;h++){let u=a[1+h];let d=l?l[h]:0;s[h]=u+d}};r.prototype._unFilterType3=function(a,s,c){let l=this._xComparison;let h=l-1;let u=this._lastLine;for(let d=0;d<c;d++){let f=a[1+d];let m=u?u[d]:0;let p=d>h?s[d-l]:0;let w=Math.floor((p+m)/2);s[d]=f+w}};r.prototype._unFilterType4=function(a,s,c){let l=this._xComparison;let h=l-1;let u=this._lastLine;for(let d=0;d<c;d++){let f=a[1+d];let m=u?u[d]:0;let p=d>h?s[d-l]:0;let w=d>h&&u?u[d-l]:0;let y=n(p,m,w);s[d]=f+y}};r.prototype._reverseFilterLine=function(a){let s=a[0];let c;let l=this._images[this._imageIndex];let h=l.byteWidth;if(s===0){c=a.slice(1,h+1)}else{c=Buffer.alloc(h);switch(s){case 1:this._unFilterType1(a,c,h);break;case 2:this._unFilterType2(a,c,h);break;case 3:this._unFilterType3(a,c,h);break;case 4:this._unFilterType4(a,c,h);break;default:throw new Error("Unrecognised filter type - "+s)}}this.write(c);l.lineIndex++;if(l.lineIndex>=l.height){this._lastLine=null;this._imageIndex++;l=this._images[this._imageIndex]}else{this._lastLine=c}if(l){this.read(l.byteWidth+1,this._reverseFilterLine.bind(this))}else{this._lastLine=null;this.complete()}}}});var Oi=$({"../../node_modules/pngjs/lib/filter-parse-async.js"(e,t){"use strict";var i=x("util");var n=st();var o=ct();var r=t.exports=function(a){n.call(this);let s=[];let c=this;this._filter=new o(a,{read:this.read.bind(this),write:function(l){s.push(l)},complete:function(){c.emit("complete",Buffer.concat(s))}});this._filter.start()};i.inherits(r,n)}});var oe=$({"../../node_modules/pngjs/lib/constants.js"(e,t){"use strict";t.exports={PNG_SIGNATURE:[137,80,78,71,13,10,26,10],TYPE_IHDR:1229472850,TYPE_IEND:1229278788,TYPE_IDAT:1229209940,TYPE_PLTE:1347179589,TYPE_tRNS:1951551059,TYPE_gAMA:1732332865,COLORTYPE_GRAYSCALE:0,COLORTYPE_PALETTE:1,COLORTYPE_COLOR:2,COLORTYPE_ALPHA:4,COLORTYPE_PALETTE_COLOR:3,COLORTYPE_COLOR_ALPHA:6,COLORTYPE_TO_BPP_MAP:{0:1,2:3,3:1,4:2,6:4},GAMMA_DIVISION:1e5}}});var lt=$({"../../node_modules/pngjs/lib/crc.js"(e,t){"use strict";var i=[];(function(){for(let o=0;o<256;o++){let r=o;for(let a=0;a<8;a++){if(r&1){r=3988292384^r>>>1}else{r=r>>>1}}i[o]=r}})();var n=t.exports=function(){this._crc=-1};n.prototype.write=function(o){for(let r=0;r<o.length;r++){this._crc=i[(this._crc^o[r])&255]^this._crc>>>8}return true};n.prototype.crc32=function(){return this._crc^-1};n.crc32=function(o){let r=-1;for(let a=0;a<o.length;a++){r=i[(r^o[a])&255]^r>>>8}return r^-1}}});var ht=$({"../../node_modules/pngjs/lib/parser.js"(e,t){"use strict";var i=oe();var n=lt();var o=t.exports=function(r,a){this._options=r;r.checkCRC=r.checkCRC!==false;this._hasIHDR=false;this._hasIEND=false;this._emittedHeadersFinished=false;this._palette=[];this._colorType=0;this._chunks={};this._chunks[i.TYPE_IHDR]=this._handleIHDR.bind(this);this._chunks[i.TYPE_IEND]=this._handleIEND.bind(this);this._chunks[i.TYPE_IDAT]=this._handleIDAT.bind(this);this._chunks[i.TYPE_PLTE]=this._handlePLTE.bind(this);this._chunks[i.TYPE_tRNS]=this._handleTRNS.bind(this);this._chunks[i.TYPE_gAMA]=this._handleGAMA.bind(this);this.read=a.read;this.error=a.error;this.metadata=a.metadata;this.gamma=a.gamma;this.transColor=a.transColor;this.palette=a.palette;this.parsed=a.parsed;this.inflateData=a.inflateData;this.finished=a.finished;this.simpleTransparency=a.simpleTransparency;this.headersFinished=a.headersFinished||function(){}};o.prototype.start=function(){this.read(i.PNG_SIGNATURE.length,this._parseSignature.bind(this))};o.prototype._parseSignature=function(r){let a=i.PNG_SIGNATURE;for(let s=0;s<a.length;s++){if(r[s]!==a[s]){this.error(new Error("Invalid file signature"));return}}this.read(8,this._parseChunkBegin.bind(this))};o.prototype._parseChunkBegin=function(r){let a=r.readUInt32BE(0);let s=r.readUInt32BE(4);let c="";for(let h=4;h<8;h++){c+=String.fromCharCode(r[h])}let l=Boolean(r[4]&32);if(!this._hasIHDR&&s!==i.TYPE_IHDR){this.error(new Error("Expected IHDR on beggining"));return}this._crc=new n;this._crc.write(Buffer.from(c));if(this._chunks[s]){return this._chunks[s](a)}if(!l){this.error(new Error("Unsupported critical chunk type "+c));return}this.read(a+4,this._skipChunk.bind(this))};o.prototype._skipChunk=function(){this.read(8,this._parseChunkBegin.bind(this))};o.prototype._handleChunkEnd=function(){this.read(4,this._parseChunkEnd.bind(this))};o.prototype._parseChunkEnd=function(r){let a=r.readInt32BE(0);let s=this._crc.crc32();if(this._options.checkCRC&&s!==a){this.error(new Error("Crc error - "+a+" - "+s));return}if(!this._hasIEND){this.read(8,this._parseChunkBegin.bind(this))}};o.prototype._handleIHDR=function(r){this.read(r,this._parseIHDR.bind(this))};o.prototype._parseIHDR=function(r){this._crc.write(r);let a=r.readUInt32BE(0);let s=r.readUInt32BE(4);let c=r[8];let l=r[9];let h=r[10];let u=r[11];let d=r[12];if(c!==8&&c!==4&&c!==2&&c!==1&&c!==16){this.error(new Error("Unsupported bit depth "+c));return}if(!(l in i.COLORTYPE_TO_BPP_MAP)){this.error(new Error("Unsupported color type"));return}if(h!==0){this.error(new Error("Unsupported compression method"));return}if(u!==0){this.error(new Error("Unsupported filter method"));return}if(d!==0&&d!==1){this.error(new Error("Unsupported interlace method"));return}this._colorType=l;let f=i.COLORTYPE_TO_BPP_MAP[this._colorType];this._hasIHDR=true;this.metadata({width:a,height:s,depth:c,interlace:Boolean(d),palette:Boolean(l&i.COLORTYPE_PALETTE),color:Boolean(l&i.COLORTYPE_COLOR),alpha:Boolean(l&i.COLORTYPE_ALPHA),bpp:f,colorType:l});this._handleChunkEnd()};o.prototype._handlePLTE=function(r){this.read(r,this._parsePLTE.bind(this))};o.prototype._parsePLTE=function(r){this._crc.write(r);let a=Math.floor(r.length/3);for(let s=0;s<a;s++){this._palette.push([r[s*3],r[s*3+1],r[s*3+2],255])}this.palette(this._palette);this._handleChunkEnd()};o.prototype._handleTRNS=function(r){this.simpleTransparency();this.read(r,this._parseTRNS.bind(this))};o.prototype._parseTRNS=function(r){this._crc.write(r);if(this._colorType===i.COLORTYPE_PALETTE_COLOR){if(this._palette.length===0){this.error(new Error("Transparency chunk must be after palette"));return}if(r.length>this._palette.length){this.error(new Error("More transparent colors than palette size"));return}for(let a=0;a<r.length;a++){this._palette[a][3]=r[a]}this.palette(this._palette)}if(this._colorType===i.COLORTYPE_GRAYSCALE){this.transColor([r.readUInt16BE(0)])}if(this._colorType===i.COLORTYPE_COLOR){this.transColor([r.readUInt16BE(0),r.readUInt16BE(2),r.readUInt16BE(4)])}this._handleChunkEnd()};o.prototype._handleGAMA=function(r){this.read(r,this._parseGAMA.bind(this))};o.prototype._parseGAMA=function(r){this._crc.write(r);this.gamma(r.readUInt32BE(0)/i.GAMMA_DIVISION);this._handleChunkEnd()};o.prototype._handleIDAT=function(r){if(!this._emittedHeadersFinished){this._emittedHeadersFinished=true;this.headersFinished()}this.read(-r,this._parseIDAT.bind(this,r))};o.prototype._parseIDAT=function(r,a){this._crc.write(a);if(this._colorType===i.COLORTYPE_PALETTE_COLOR&&this._palette.length===0){throw new Error("Expected palette not found")}this.inflateData(a);let s=r-a.length;if(s>0){this._handleIDAT(s)}else{this._handleChunkEnd()}};o.prototype._handleIEND=function(r){this.read(r,this._parseIEND.bind(this))};o.prototype._parseIEND=function(r){this._crc.write(r);this._hasIEND=true;this._handleChunkEnd();if(this.finished){this.finished()}}}});var ut=$({"../../node_modules/pngjs/lib/bitmapper.js"(e){"use strict";var t=ot();var i=[function(){},function(s,c,l,h){if(h===c.length){throw new Error("Ran out of data")}let u=c[h];s[l]=u;s[l+1]=u;s[l+2]=u;s[l+3]=255},function(s,c,l,h){if(h+1>=c.length){throw new Error("Ran out of data")}let u=c[h];s[l]=u;s[l+1]=u;s[l+2]=u;s[l+3]=c[h+1]},function(s,c,l,h){if(h+2>=c.length){throw new Error("Ran out of data")}s[l]=c[h];s[l+1]=c[h+1];s[l+2]=c[h+2];s[l+3]=255},function(s,c,l,h){if(h+3>=c.length){throw new Error("Ran out of data")}s[l]=c[h];s[l+1]=c[h+1];s[l+2]=c[h+2];s[l+3]=c[h+3]}];var n=[function(){},function(s,c,l,h){let u=c[0];s[l]=u;s[l+1]=u;s[l+2]=u;s[l+3]=h},function(s,c,l){let h=c[0];s[l]=h;s[l+1]=h;s[l+2]=h;s[l+3]=c[1]},function(s,c,l,h){s[l]=c[0];s[l+1]=c[1];s[l+2]=c[2];s[l+3]=h},function(s,c,l){s[l]=c[0];s[l+1]=c[1];s[l+2]=c[2];s[l+3]=c[3]}];function o(s,c){let l=[];let h=0;function u(){if(h===s.length){throw new Error("Ran out of data")}let d=s[h];h++;let f,m,p,w,y,b,v,g;switch(c){default:throw new Error("unrecognised depth");case 16:v=s[h];h++;l.push((d<<8)+v);break;case 4:v=d&15;g=d>>4;l.push(g,v);break;case 2:y=d&3;b=d>>2&3;v=d>>4&3;g=d>>6&3;l.push(g,v,b,y);break;case 1:f=d&1;m=d>>1&1;p=d>>2&1;w=d>>3&1;y=d>>4&1;b=d>>5&1;v=d>>6&1;g=d>>7&1;l.push(g,v,b,y,w,p,m,f);break}}return{get:function(d){while(l.length<d){u()}let f=l.slice(0,d);l=l.slice(d);return f},resetAfterLine:function(){l.length=0},end:function(){if(h!==s.length){throw new Error("extra data found")}}}}function r(s,c,l,h,u,d){let f=s.width;let m=s.height;let p=s.index;for(let w=0;w<m;w++){for(let y=0;y<f;y++){let b=l(y,w,p);i[h](c,u,b,d);d+=h}}return d}function a(s,c,l,h,u,d){let f=s.width;let m=s.height;let p=s.index;for(let w=0;w<m;w++){for(let y=0;y<f;y++){let b=u.get(h);let v=l(y,w,p);n[h](c,b,v,d)}u.resetAfterLine()}}e.dataToBitMap=function(s,c){let l=c.width;let h=c.height;let u=c.depth;let d=c.bpp;let f=c.interlace;let m;if(u!==8){m=o(s,u)}let p;if(u<=8){p=Buffer.alloc(l*h*4)}else{p=new Uint16Array(l*h*4)}let w=Math.pow(2,u)-1;let y=0;let b;let v;if(f){b=t.getImagePasses(l,h);v=t.getInterlaceIterator(l,h)}else{let g=0;v=function(){let _=g;g+=4;return _};b=[{width:l,height:h}]}for(let g=0;g<b.length;g++){if(u===8){y=r(b[g],p,v,d,s,y)}else{a(b[g],p,v,d,m,w)}}if(u===8){if(y!==s.length){throw new Error("extra data found")}}else{m.end()}return p}}});var dt=$({"../../node_modules/pngjs/lib/format-normaliser.js"(e,t){"use strict";function i(r,a,s,c,l){let h=0;for(let u=0;u<c;u++){for(let d=0;d<s;d++){let f=l[r[h]];if(!f){throw new Error("index "+r[h]+" not in palette")}for(let m=0;m<4;m++){a[h+m]=f[m]}h+=4}}}function n(r,a,s,c,l){let h=0;for(let u=0;u<c;u++){for(let d=0;d<s;d++){let f=false;if(l.length===1){if(l[0]===r[h]){f=true}}else if(l[0]===r[h]&&l[1]===r[h+1]&&l[2]===r[h+2]){f=true}if(f){for(let m=0;m<4;m++){a[h+m]=0}}h+=4}}}function o(r,a,s,c,l){let h=255;let u=Math.pow(2,l)-1;let d=0;for(let f=0;f<c;f++){for(let m=0;m<s;m++){for(let p=0;p<4;p++){a[d+p]=Math.floor(r[d+p]*h/u+.5)}d+=4}}}t.exports=function(r,a,s=false){let c=a.depth;let l=a.width;let h=a.height;let u=a.colorType;let d=a.transColor;let f=a.palette;let m=r;if(u===3){i(r,m,l,h,f)}else{if(d){n(r,m,l,h,d)}if(c!==8&&!s){if(c===16){m=Buffer.alloc(l*h*4)}o(r,m,l,h,c)}}return m}}});var Mi=$({"../../node_modules/pngjs/lib/parser-async.js"(e,t){"use strict";var i=x("util");var n=x("zlib");var o=st();var r=Oi();var a=ht();var s=ut();var c=dt();var l=t.exports=function(h){o.call(this);this._parser=new a(h,{read:this.read.bind(this),error:this._handleError.bind(this),metadata:this._handleMetaData.bind(this),gamma:this.emit.bind(this,"gamma"),palette:this._handlePalette.bind(this),transColor:this._handleTransColor.bind(this),finished:this._finished.bind(this),inflateData:this._inflateData.bind(this),simpleTransparency:this._simpleTransparency.bind(this),headersFinished:this._headersFinished.bind(this)});this._options=h;this.writable=true;this._parser.start()};i.inherits(l,o);l.prototype._handleError=function(h){this.emit("error",h);this.writable=false;this.destroy();if(this._inflate&&this._inflate.destroy){this._inflate.destroy()}if(this._filter){this._filter.destroy();this._filter.on("error",function(){})}this.errord=true};l.prototype._inflateData=function(h){if(!this._inflate){if(this._bitmapInfo.interlace){this._inflate=n.createInflate();this._inflate.on("error",this.emit.bind(this,"error"));this._filter.on("complete",this._complete.bind(this));this._inflate.pipe(this._filter)}else{let u=(this._bitmapInfo.width*this._bitmapInfo.bpp*this._bitmapInfo.depth+7>>3)+1;let d=u*this._bitmapInfo.height;let f=Math.max(d,n.Z_MIN_CHUNK);this._inflate=n.createInflate({chunkSize:f});let m=d;let p=this.emit.bind(this,"error");this._inflate.on("error",function(y){if(!m){return}p(y)});this._filter.on("complete",this._complete.bind(this));let w=this._filter.write.bind(this._filter);this._inflate.on("data",function(y){if(!m){return}if(y.length>m){y=y.slice(0,m)}m-=y.length;w(y)});this._inflate.on("end",this._filter.end.bind(this._filter))}}this._inflate.write(h)};l.prototype._handleMetaData=function(h){this._metaData=h;this._bitmapInfo=Object.create(h);this._filter=new r(this._bitmapInfo)};l.prototype._handleTransColor=function(h){this._bitmapInfo.transColor=h};l.prototype._handlePalette=function(h){this._bitmapInfo.palette=h};l.prototype._simpleTransparency=function(){this._metaData.alpha=true};l.prototype._headersFinished=function(){this.emit("metadata",this._metaData)};l.prototype._finished=function(){if(this.errord){return}if(!this._inflate){this.emit("error","No Inflate block")}else{this._inflate.end()}};l.prototype._complete=function(h){if(this.errord){return}let u;try{let d=s.dataToBitMap(h,this._bitmapInfo);u=c(d,this._bitmapInfo,this._options.skipRescale);d=null}catch(d){this._handleError(d);return}this.emit("parsed",u)}}});var Ii=$({"../../node_modules/pngjs/lib/bitpacker.js"(e,t){"use strict";var i=oe();t.exports=function(n,o,r,a){let s=[i.COLORTYPE_COLOR_ALPHA,i.COLORTYPE_ALPHA].indexOf(a.colorType)!==-1;if(a.colorType===a.inputColorType){let y=(function(){let b=new ArrayBuffer(2);new DataView(b).setInt16(0,256,true);return new Int16Array(b)[0]!==256})();if(a.bitDepth===8||a.bitDepth===16&&y){return n}}let c=a.bitDepth!==16?n:new Uint16Array(n.buffer);let l=255;let h=i.COLORTYPE_TO_BPP_MAP[a.inputColorType];if(h===4&&!a.inputHasAlpha){h=3}let u=i.COLORTYPE_TO_BPP_MAP[a.colorType];if(a.bitDepth===16){l=65535;u*=2}let d=Buffer.alloc(o*r*u);let f=0;let m=0;let p=a.bgColor||{};if(p.red===void 0){p.red=l}if(p.green===void 0){p.green=l}if(p.blue===void 0){p.blue=l}function w(){let y;let b;let v;let g=l;switch(a.inputColorType){case i.COLORTYPE_COLOR_ALPHA:g=c[f+3];y=c[f];b=c[f+1];v=c[f+2];break;case i.COLORTYPE_COLOR:y=c[f];b=c[f+1];v=c[f+2];break;case i.COLORTYPE_ALPHA:g=c[f+1];y=c[f];b=y;v=y;break;case i.COLORTYPE_GRAYSCALE:y=c[f];b=y;v=y;break;default:throw new Error("input color type:"+a.inputColorType+" is not supported at present")}if(a.inputHasAlpha){if(!s){g/=l;y=Math.min(Math.max(Math.round((1-g)*p.red+g*y),0),l);b=Math.min(Math.max(Math.round((1-g)*p.green+g*b),0),l);v=Math.min(Math.max(Math.round((1-g)*p.blue+g*v),0),l)}}return{red:y,green:b,blue:v,alpha:g}}for(let y=0;y<r;y++){for(let b=0;b<o;b++){let v=w(c,f);switch(a.colorType){case i.COLORTYPE_COLOR_ALPHA:case i.COLORTYPE_COLOR:if(a.bitDepth===8){d[m]=v.red;d[m+1]=v.green;d[m+2]=v.blue;if(s){d[m+3]=v.alpha}}else{d.writeUInt16BE(v.red,m);d.writeUInt16BE(v.green,m+2);d.writeUInt16BE(v.blue,m+4);if(s){d.writeUInt16BE(v.alpha,m+6)}}break;case i.COLORTYPE_ALPHA:case i.COLORTYPE_GRAYSCALE:{let g=(v.red+v.green+v.blue)/3;if(a.bitDepth===8){d[m]=g;if(s){d[m+1]=v.alpha}}else{d.writeUInt16BE(g,m);if(s){d.writeUInt16BE(v.alpha,m+2)}}break}default:throw new Error("unrecognised color Type "+a.colorType)}f+=h;m+=u}}return d}}});var Ci=$({"../../node_modules/pngjs/lib/filter-pack.js"(e,t){"use strict";var i=at();function n(p,w,y,b,v){for(let g=0;g<y;g++){b[v+g]=p[w+g]}}function o(p,w,y){let b=0;let v=w+y;for(let g=w;g<v;g++){b+=Math.abs(p[g])}return b}function r(p,w,y,b,v,g){for(let _=0;_<y;_++){let E=_>=g?p[w+_-g]:0;let S=p[w+_]-E;b[v+_]=S}}function a(p,w,y,b){let v=0;for(let g=0;g<y;g++){let _=g>=b?p[w+g-b]:0;let E=p[w+g]-_;v+=Math.abs(E)}return v}function s(p,w,y,b,v){for(let g=0;g<y;g++){let _=w>0?p[w+g-y]:0;let E=p[w+g]-_;b[v+g]=E}}function c(p,w,y){let b=0;let v=w+y;for(let g=w;g<v;g++){let _=w>0?p[g-y]:0;let E=p[g]-_;b+=Math.abs(E)}return b}function l(p,w,y,b,v,g){for(let _=0;_<y;_++){let E=_>=g?p[w+_-g]:0;let S=w>0?p[w+_-y]:0;let I=p[w+_]-(E+S>>1);b[v+_]=I}}function h(p,w,y,b){let v=0;for(let g=0;g<y;g++){let _=g>=b?p[w+g-b]:0;let E=w>0?p[w+g-y]:0;let S=p[w+g]-(_+E>>1);v+=Math.abs(S)}return v}function u(p,w,y,b,v,g){for(let _=0;_<y;_++){let E=_>=g?p[w+_-g]:0;let S=w>0?p[w+_-y]:0;let I=w>0&&_>=g?p[w+_-(y+g)]:0;let A=p[w+_]-i(E,S,I);b[v+_]=A}}function d(p,w,y,b){let v=0;for(let g=0;g<y;g++){let _=g>=b?p[w+g-b]:0;let E=w>0?p[w+g-y]:0;let S=w>0&&g>=b?p[w+g-(y+b)]:0;let I=p[w+g]-i(_,E,S);v+=Math.abs(I)}return v}var f={0:n,1:r,2:s,3:l,4:u};var m={0:o,1:a,2:c,3:h,4:d};t.exports=function(p,w,y,b,v){let g;if(!("filterType"in b)||b.filterType===-1){g=[0,1,2,3,4]}else if(typeof b.filterType==="number"){g=[b.filterType]}else{throw new Error("unrecognised filter types")}if(b.bitDepth===16){v*=2}let _=w*v;let E=0;let S=0;let I=Buffer.alloc((_+1)*y);let A=g[0];for(let C=0;C<y;C++){if(g.length>1){let O=Infinity;for(let M=0;M<g.length;M++){let F=m[g[M]](p,S,_,v);if(F<O){A=g[M];O=F}}}I[E]=A;E++;f[A](p,S,_,I,E,v);E+=_;S+=_}return I}}});var ft=$({"../../node_modules/pngjs/lib/packer.js"(e,t){"use strict";var i=oe();var n=lt();var o=Ii();var r=Ci();var a=x("zlib");var s=t.exports=function(c){this._options=c;c.deflateChunkSize=c.deflateChunkSize||32*1024;c.deflateLevel=c.deflateLevel!=null?c.deflateLevel:9;c.deflateStrategy=c.deflateStrategy!=null?c.deflateStrategy:3;c.inputHasAlpha=c.inputHasAlpha!=null?c.inputHasAlpha:true;c.deflateFactory=c.deflateFactory||a.createDeflate;c.bitDepth=c.bitDepth||8;c.colorType=typeof c.colorType==="number"?c.colorType:i.COLORTYPE_COLOR_ALPHA;c.inputColorType=typeof c.inputColorType==="number"?c.inputColorType:i.COLORTYPE_COLOR_ALPHA;if([i.COLORTYPE_GRAYSCALE,i.COLORTYPE_COLOR,i.COLORTYPE_COLOR_ALPHA,i.COLORTYPE_ALPHA].indexOf(c.colorType)===-1){throw new Error("option color type:"+c.colorType+" is not supported at present")}if([i.COLORTYPE_GRAYSCALE,i.COLORTYPE_COLOR,i.COLORTYPE_COLOR_ALPHA,i.COLORTYPE_ALPHA].indexOf(c.inputColorType)===-1){throw new Error("option input color type:"+c.inputColorType+" is not supported at present")}if(c.bitDepth!==8&&c.bitDepth!==16){throw new Error("option bit depth:"+c.bitDepth+" is not supported at present")}};s.prototype.getDeflateOptions=function(){return{chunkSize:this._options.deflateChunkSize,level:this._options.deflateLevel,strategy:this._options.deflateStrategy}};s.prototype.createDeflate=function(){return this._options.deflateFactory(this.getDeflateOptions())};s.prototype.filterData=function(c,l,h){let u=o(c,l,h,this._options);let d=i.COLORTYPE_TO_BPP_MAP[this._options.colorType];let f=r(u,l,h,this._options,d);return f};s.prototype._packChunk=function(c,l){let h=l?l.length:0;let u=Buffer.alloc(h+12);u.writeUInt32BE(h,0);u.writeUInt32BE(c,4);if(l){l.copy(u,8)}u.writeInt32BE(n.crc32(u.slice(4,u.length-4)),u.length-4);return u};s.prototype.packGAMA=function(c){let l=Buffer.alloc(4);l.writeUInt32BE(Math.floor(c*i.GAMMA_DIVISION),0);return this._packChunk(i.TYPE_gAMA,l)};s.prototype.packIHDR=function(c,l){let h=Buffer.alloc(13);h.writeUInt32BE(c,0);h.writeUInt32BE(l,4);h[8]=this._options.bitDepth;h[9]=this._options.colorType;h[10]=0;h[11]=0;h[12]=0;return this._packChunk(i.TYPE_IHDR,h)};s.prototype.packIDAT=function(c){return this._packChunk(i.TYPE_IDAT,c)};s.prototype.packIEND=function(){return this._packChunk(i.TYPE_IEND,null)}}});var $i=$({"../../node_modules/pngjs/lib/packer-async.js"(e,t){"use strict";var i=x("util");var n=x("stream");var o=oe();var r=ft();var a=t.exports=function(s){n.call(this);let c=s||{};this._packer=new r(c);this._deflate=this._packer.createDeflate();this.readable=true};i.inherits(a,n);a.prototype.pack=function(s,c,l,h){this.emit("data",Buffer.from(o.PNG_SIGNATURE));this.emit("data",this._packer.packIHDR(c,l));if(h){this.emit("data",this._packer.packGAMA(h))}let u=this._packer.filterData(s,c,l);this._deflate.on("error",this.emit.bind(this,"error"));this._deflate.on("data",function(d){this.emit("data",this._packer.packIDAT(d))}.bind(this));this._deflate.on("end",function(){this.emit("data",this._packer.packIEND());this.emit("end")}.bind(this));this._deflate.end(u)}}});var Pi=$({"../../node_modules/pngjs/lib/sync-inflate.js"(e,t){"use strict";var i=x("assert").ok;var n=x("zlib");var o=x("util");var r=x("buffer").kMaxLength;function a(u){if(!(this instanceof a)){return new a(u)}if(u&&u.chunkSize<n.Z_MIN_CHUNK){u.chunkSize=n.Z_MIN_CHUNK}n.Inflate.call(this,u);this._offset=this._offset===void 0?this._outOffset:this._offset;this._buffer=this._buffer||this._outBuffer;if(u&&u.maxLength!=null){this._maxLength=u.maxLength}}function s(u){return new a(u)}function c(u,d){if(d){process.nextTick(d)}if(!u._handle){return}u._handle.close();u._handle=null}a.prototype._processChunk=function(u,d,f){if(typeof f==="function"){return n.Inflate._processChunk.call(this,u,d,f)}let m=this;let p=u&&u.length;let w=this._chunkSize-this._offset;let y=this._maxLength;let b=0;let v=[];let g=0;let _;this.on("error",function(A){_=A});function E(A,C){if(m._hadError){return}let O=w-C;i(O>=0,"have should not go down");if(O>0){let M=m._buffer.slice(m._offset,m._offset+O);m._offset+=O;if(M.length>y){M=M.slice(0,y)}v.push(M);g+=M.length;y-=M.length;if(y===0){return false}}if(C===0||m._offset>=m._chunkSize){w=m._chunkSize;m._offset=0;m._buffer=Buffer.allocUnsafe(m._chunkSize)}if(C===0){b+=p-A;p=A;return true}return false}i(this._handle,"zlib binding closed");let S;do{S=this._handle.writeSync(d,u,b,p,this._buffer,this._offset,w);S=S||this._writeState}while(!this._hadError&&E(S[0],S[1]));if(this._hadError){throw _}if(g>=r){c(this);throw new RangeError("Cannot create final Buffer. It would be larger than 0x"+r.toString(16)+" bytes")}let I=Buffer.concat(v,g);c(this);return I};o.inherits(a,n.Inflate);function l(u,d){if(typeof d==="string"){d=Buffer.from(d)}if(!(d instanceof Buffer)){throw new TypeError("Not a string or buffer")}let f=u._finishFlushFlag;if(f==null){f=n.Z_FINISH}return u._processChunk(d,f)}function h(u,d){return l(new a(d),u)}t.exports=e=h;e.Inflate=a;e.createInflate=s;e.inflateSync=h}});var pt=$({"../../node_modules/pngjs/lib/sync-reader.js"(e,t){"use strict";var i=t.exports=function(n){this._buffer=n;this._reads=[]};i.prototype.read=function(n,o){this._reads.push({length:Math.abs(n),allowLess:n<0,func:o})};i.prototype.process=function(){while(this._reads.length>0&&this._buffer.length){let n=this._reads[0];if(this._buffer.length&&(this._buffer.length>=n.length||n.allowLess)){this._reads.shift();let o=this._buffer;this._buffer=o.slice(n.length);n.func.call(this,o.slice(0,n.length))}else{break}}if(this._reads.length>0){throw new Error("There are some read requests waitng on finished stream")}if(this._buffer.length>0){throw new Error("unrecognised content at end of stream")}}}});var Ri=$({"../../node_modules/pngjs/lib/filter-parse-sync.js"(e){"use strict";var t=pt();var i=ct();e.process=function(n,o){let r=[];let a=new t(n);let s=new i(o,{read:a.read.bind(a),write:function(c){r.push(c)},complete:function(){}});s.start();a.process();return Buffer.concat(r)}}});var Di=$({"../../node_modules/pngjs/lib/parser-sync.js"(e,t){"use strict";var i=true;var n=x("zlib");var o=Pi();if(!n.deflateSync){i=false}var r=pt();var a=Ri();var s=ht();var c=ut();var l=dt();t.exports=function(h,u){if(!i){throw new Error("To use the sync capability of this library in old node versions, please pin pngjs to v2.3.0")}let d;function f(R){d=R}let m;function p(R){m=R}function w(R){m.transColor=R}function y(R){m.palette=R}function b(){m.alpha=true}let v;function g(R){v=R}let _=[];function E(R){_.push(R)}let S=new r(h);let I=new s(u,{read:S.read.bind(S),error:f,metadata:p,gamma:g,palette:y,transColor:w,inflateData:E,simpleTransparency:b});I.start();S.process();if(d){throw d}let A=Buffer.concat(_);_.length=0;let C;if(m.interlace){C=n.inflateSync(A)}else{let R=(m.width*m.bpp*m.depth+7>>3)+1;let V=R*m.height;C=o(A,{chunkSize:V,maxLength:V})}A=null;if(!C||!C.length){throw new Error("bad png - invalid inflate data response")}let O=a.process(C,m);A=null;let M=c.dataToBitMap(O,m);O=null;let F=l(M,m,u.skipRescale);m.data=F;m.gamma=v||0;return m}}});var xi=$({"../../node_modules/pngjs/lib/packer-sync.js"(e,t){"use strict";var i=true;var n=x("zlib");if(!n.deflateSync){i=false}var o=oe();var r=ft();t.exports=function(a,s){if(!i){throw new Error("To use the sync capability of this library in old node versions, please pin pngjs to v2.3.0")}let c=s||{};let l=new r(c);let h=[];h.push(Buffer.from(o.PNG_SIGNATURE));h.push(l.packIHDR(a.width,a.height));if(a.gamma){h.push(l.packGAMA(a.gamma))}let u=l.filterData(a.data,a.width,a.height);let d=n.deflateSync(u,l.getDeflateOptions());u=null;if(!d||!d.length){throw new Error("bad png - invalid compressed data response")}h.push(l.packIDAT(d));h.push(l.packIEND());return Buffer.concat(h)}}});var Ni=$({"../../node_modules/pngjs/lib/png-sync.js"(e){"use strict";var t=Di();var i=xi();e.read=function(n,o){return t(n,o||{})};e.write=function(n,o){return i(n,o)}}});var _e=$({"../../node_modules/pngjs/lib/png.js"(e){"use strict";var t=x("util");var i=x("stream");var n=Mi();var o=$i();var r=Ni();var a=e.PNG=function(s){i.call(this);s=s||{};this.width=s.width|0;this.height=s.height|0;this.data=this.width>0&&this.height>0?Buffer.alloc(4*this.width*this.height):null;if(s.fill&&this.data){this.data.fill(0)}this.gamma=0;this.readable=this.writable=true;this._parser=new n(s);this._parser.on("error",this.emit.bind(this,"error"));this._parser.on("close",this._handleClose.bind(this));this._parser.on("metadata",this._metadata.bind(this));this._parser.on("gamma",this._gamma.bind(this));this._parser.on("parsed",function(c){this.data=c;this.emit("parsed",c)}.bind(this));this._packer=new o(s);this._packer.on("data",this.emit.bind(this,"data"));this._packer.on("end",this.emit.bind(this,"end"));this._parser.on("close",this._handleClose.bind(this));this._packer.on("error",this.emit.bind(this,"error"))};t.inherits(a,i);a.sync=r;a.prototype.pack=function(){if(!this.data||!this.data.length){this.emit("error","No data provided");return this}process.nextTick(function(){this._packer.pack(this.data,this.width,this.height,this.gamma)}.bind(this));return this};a.prototype.parse=function(s,c){if(c){let l,h;l=function(u){this.removeListener("error",h);this.data=u;c(null,this)}.bind(this);h=function(u){this.removeListener("parsed",l);c(u,null)}.bind(this);this.once("parsed",l);this.once("error",h)}this.end(s);return this};a.prototype.write=function(s){this._parser.write(s);return true};a.prototype.end=function(s){this._parser.end(s)};a.prototype._metadata=function(s){this.width=s.width;this.height=s.height;this.emit("metadata",s)};a.prototype._gamma=function(s){this.gamma=s};a.prototype._handleClose=function(){if(!this._parser.writable&&!this._packer.readable){this.emit("close")}};a.bitblt=function(s,c,l,h,u,d,f,m){l|=0;h|=0;u|=0;d|=0;f|=0;m|=0;if(l>s.width||h>s.height||l+u>s.width||h+d>s.height){throw new Error("bitblt reading outside image")}if(f>c.width||m>c.height||f+u>c.width||m+d>c.height){throw new Error("bitblt writing outside image")}for(let p=0;p<d;p++){s.data.copy(c.data,(m+p)*c.width+f<<2,(h+p)*s.width+l<<2,(h+p)*s.width+l+u<<2)}};a.prototype.bitblt=function(s,c,l,h,u,d,f){a.bitblt(this,s,c,l,h,u,d,f);return this};a.adjustGamma=function(s){if(s.gamma){for(let c=0;c<s.height;c++){for(let l=0;l<s.width;l++){let h=s.width*c+l<<2;for(let u=0;u<3;u++){let d=s.data[h+u]/255;d=Math.pow(d,1/2.2/s.gamma);s.data[h+u]=Math.round(d*255)}}}s.gamma=0}};a.prototype.adjustGamma=function(){a.adjustGamma(this)}}});import{DEFAULT_BROKER_PORT as Li}from"@buoy-gg/sync-broker";var Ee=Li;var mt=`http://localhost:${Ee}`;var wt=15e3;var ke=2500;var Bi=1200;var gt=2e4;var ji=3e4;var yt=5e3;var Fi=15*6e4;var Se="Dashboard";var B="All";import{readFileSync as bt,writeFileSync as Ui,mkdirSync as Hi,renameSync as Yi,unlinkSync as Wi}from"fs";import{dirname as qi,join as vt,resolve as zi}from"path";import{homedir as Ki}from"os";import{createHash as Gi}from"crypto";import{validateLicense as Vi,isAcceptedValidation as Ji,isTransientLicenseError as Xi,resolveTier as Zi,isRecognizedAccount as Qi,isBuoyToken as er,verifyFreeToken as tr,resolveAccountAccess as ir,signCache as fe,verifyCacheSignature as rr,LICENSE_LAUNCH_VALIDATION_TTL as _t,LICENSE_REVALIDATION_INTERVAL as X,LICENSE_TRANSIENT_RETRY_DELAYS as Et,KEYGEN_CONFIG as nr,createNodeSession as sr,CLI_AUDIENCE as or,exchangeServiceKey as ar,isServiceKey as cr,normalizeLicenseKey as kt,checkDevToken as lr,isDevToken as hr}from"@buoy-gg/license/headless";var St=["BUOY_LICENSE_KEY","BUOY_KEY","EXPO_PUBLIC_BUOY_KEY","VITE_BUOY_KEY","NEXT_PUBLIC_BUOY_KEY","REACT_APP_BUOY_KEY"];function pe(e,t=process.env){for(const i of St)if(t[i]!==void 0)return kt(t[i])||null;for(const i of[".env.development.local",".env.local",".env"]){let n;try{n=bt(vt(e,i),"utf8")}catch{continue}const o=new Map;for(const r of n.split(/\r?\n/)){const a=r.match(/^\s*(?:export\s+)?([A-Z_][A-Z_0-9]*)\s*=\s*(.*)$/);if(!a)continue;const s=a[2].trim();const c=s.match(/^(['"])(.*?)\1\s*(?:#.*)?$/);o.set(a[1],c?c[2]:s.split("#")[0].trim())}for(const r of St)if(o.has(r))return kt(o.get(r))||null}return null}var ur=class{constructor(e,t={}){this.changed=e;this.key=null;this.cache=null;this.timer=null;this.unsubscribeSession=null;this.generation=0;this.busy=false;this.nextCheck=0;this.retry=0;this.lastAdmission=false;this.hasAccess=()=>this.hasKeyAccess()||this.session?.getStatus().state==="active";this.lastSessionSync=0;this.projectDir=zi(t.projectDir??process.env.BUOY_PROJECT_DIR??process.cwd());const i=Gi("sha256").update(this.projectDir).digest("hex").slice(0,20);this.cacheFile=t.cacheFile??vt(Ki(),".buoy",`account-${i}.json`);this.session=t.session!==void 0?t.session:sr({audience:or,openBrowser:()=>{throw new Error("The MCP does not open a browser. Run `npx --package=@buoy-gg/core buoy login`.")}})}hasKeyAccess(){return ir({initialized:true,validating:false,key:this.key,tierSource:this.cache?.tier?"key":"none",verifiedAt:this.cache?.cachedAt??null}).status==="admitted"}start(){if(this.timer)return;if(this.session){this.unsubscribeSession=this.session.subscribe(()=>this.notify());void this.session.load().then(()=>this.notify(),()=>{})}try{const e=JSON.parse(bt(this.cacheFile,"utf8"));if(e.valid&&rr(e))this.cache=e}catch{}void this.tick();this.timer=setInterval(()=>{void this.tick()},1e3);this.timer.unref()}stop(){++this.generation;this.unsubscribeSession?.();this.unsubscribeSession=null;if(this.timer)clearInterval(this.timer);this.timer=null;this.key=null;this.cache=null;this.notify()}notify(){const e=this.hasAccess();if(e===this.lastAdmission)return;this.lastAdmission=e;this.changed()}async tick(){if(this.session&&Date.now()-this.lastSessionSync>15e3){this.lastSessionSync=Date.now();void this.session.sync().catch(()=>{})}const e=pe(this.projectDir);if(e!==this.key){++this.generation;this.key=e;if(this.cache?.licenseKey!==e)this.cache=null;this.nextCheck=0;this.retry=0;if(!e)this.removeCache()}this.notify();if(!e||this.busy||Date.now()<this.nextCheck)return;if(this.cache&&this.hasKeyAccess()&&Date.now()-this.cache.lastValidatedAt<_t&&this.nextCheck===0){this.nextCheck=this.cache.lastValidatedAt+_t;return}this.busy=true;const t=this.generation;try{if(hr(e)){const s=lr(e);if(!s.ok){this.cache=null;this.removeCache()}else{const c=Math.min(Date.now(),s.claims.iat*1e3);const l=s.claims.tier==="free"?"free":"pro";this.cache=fe({licenseKey:e,licenseId:`dev:${s.claims.sid}`,valid:true,tier:l,isPro:l==="pro",expiresAt:new Date(s.claims.exp*1e3).toISOString(),cachedAt:c,lastValidatedAt:Date.now()});this.persistCache()}this.nextCheck=Date.now()+X;return}if(cr(e)){const s=await ar(e,{origin:process.env.BUOY_SITE_URL||"https://buoy.gg"});if(t!==this.generation||pe(this.projectDir)!==e)return;if(!s.ok){if(s.reason==="rejected"){this.cache=null;this.removeCache();this.nextCheck=Date.now()+X}else{this.nextCheck=Date.now()+(Et[this.retry++]??X)}return}const c=Date.now();const l=s.claims.exp*1e3;this.cache=fe({licenseKey:e,licenseId:`service:${s.claims.sid}`,valid:true,tier:"pro",isPro:true,expiresAt:new Date(l).toISOString(),cachedAt:c,lastValidatedAt:c});this.persistCache();this.retry=0;this.nextCheck=Math.max(c+6e4,l-60*60*1e3);return}if(er(e)){const s=tr(e);if(!s){this.cache=null;this.removeCache()}else{const c=Date.now();this.cache=fe({licenseKey:e,licenseId:`token:${s.accountHash}`,valid:true,tier:"free",isPro:false,expiresAt:null,cachedAt:c,lastValidatedAt:c});this.persistCache()}this.nextCheck=Date.now()+X;return}const i=await Vi(e);if(t!==this.generation||pe(this.projectDir)!==e)return;const n=i.data?.relationships?.product?.data?.id;const o=i.meta.code==="EXPIRED"&&(Qi(i.data)||this.cache?.licenseKey===e);if(n&&n!==nr.productId||!o&&!Ji(i)){this.cache=null;this.removeCache();this.nextCheck=Date.now()+X;return}const r=o?"free":Zi(i.data);const a=Date.now();this.cache=fe({licenseKey:e,licenseId:i.data?.id??"",valid:true,tier:r,isPro:r==="pro",expiresAt:o?null:i.data.attributes.expiry,cachedAt:a,lastValidatedAt:a});this.persistCache();this.retry=0;this.nextCheck=a+X}catch(i){if(t!==this.generation)return;const n=Xi(i)?Et[this.retry++]:void 0;this.nextCheck=Date.now()+(n??X)}finally{this.busy=false;this.notify()}}persistCache(){try{Hi(qi(this.cacheFile),{recursive:true,mode:448});Ui(`${this.cacheFile}.tmp`,JSON.stringify(this.cache),{mode:384});Yi(`${this.cacheFile}.tmp`,this.cacheFile)}catch{}}removeCache(){try{Wi(this.cacheFile)}catch{}}};import dr from"http";import{randomUUID as fr}from"crypto";import{io as pr}from"socket.io-client";import{createBroker as mr,brokerTokenPath as Te,readBrokerToken as wr,writeBrokerToken as gr,DASHBOARD_TOKEN_REQUIRED as yr}from"@buoy-gg/sync-broker";import{execFile as br}from"child_process";import{promisify as vr}from"util";import z from"path";import _r from"os";var Tt=vr(br);async function Er(e){let t;try{t=new URL(e)}catch{return null}if(!kr(t.hostname))return null;const i=t.port||(t.protocol==="https:"?"443":"80");try{const{stdout:n}=await Tt("lsof",["-nP",`-iTCP:${i}`,"-sTCP:LISTEN","-t"],{timeout:3e3});const o=n.split("\n").map(s=>s.trim()).find(Boolean);if(!o)return null;const{stdout:r}=await Tt("lsof",["-a","-p",o,"-d","cwd","-Fn"],{timeout:3e3});const a=r.split("\n").find(s=>s.startsWith("n"));return a?z.resolve(a.slice(1)):null}catch{return null}}function kr(e,t=_r.networkInterfaces()){const i=e.replace(/^\[|\]$/g,"").toLowerCase();if(i==="localhost"||i==="127.0.0.1"||i==="::1"||i==="10.0.2.2"||i==="10.0.3.2")return true;return Object.values(t).flat().some(n=>n?.address?.toLowerCase()===i)}function Ae(e,t){const i=z.resolve(t);const n=e.map(a=>({deviceId:a.deviceId,score:a.root?Sr(z.resolve(a.root),i):-1})).filter(a=>a.score>=0);if(n.length===0)return null;const o=Math.min(...n.map(a=>a.score));const r=n.filter(a=>a.score===o);return r.length===1?r[0].deviceId:null}function Sr(e,t){if(e===t)return 0;const i=z.relative(e,t);const n=z.relative(t,e);const o=r=>r&&!r.startsWith("..")&&!z.isAbsolute(r);if(o(i))return i.split(z.sep).length;if(o(n))return n.split(z.sep).length;return-1}function Tr(e,t){return e==="app"&&(t==="ping"||t==="reloadApp")}function At(e){return e>=2}function Ar(e){if(Tr(e.toolId,e.action)){return{timeoutMs:e.fullMs,usedFullBudget:false}}if(!At(e.consecutiveTimeouts)){return{timeoutMs:e.fullMs,usedFullBudget:false}}if(e.now-e.lastFullBudgetAt>e.recoveryProbeMs){return{timeoutMs:e.fullMs,usedFullBudget:true}}return{timeoutMs:e.shortMs,usedFullBudget:false}}function Or(e){if(e.consecutiveTimeouts>=2)return true;if(e.lastOkAt>0&&e.now-e.lastOkAt>e.unresponsiveAfterMs){return true}return false}var Mr=3e4;var Ir=5e3;function Cr(e,t,i){if(e!=="highlight-updates"||t!=="waitFor")return 0;const n=Number(i?.timeoutMs);if(!Number.isFinite(n)||n<=0)return 0;return Math.min(n,Mr)+Ir}var ee="Not ready:";var $r=class{constructor(e={}){this.socket=null;this.admitted=false;this.createdAt=Date.now();this.lockedDevices=new Set;this.hostAccount=null;this.broker=null;this.capabilities=new Map;this.devices=[];this.sessionDevice=null;this.snapshots=new Map;this.watching=new Map;this.pendingActions=new Map;this.snapshotWaiters=new Map;this.snapshotAt=new Map;this.health=new Map;this.devServerOrigins=new Map;this.crashSeen=new Map;this.rootLookups=new Set;this.devServerRoots=new Map;this.originProbed=new Set;this.lastSeen=new Map;this.transportLost=false;this.deviceConnections=new Map;if(e.dashboardToken!==void 0&&!e.dashboardToken.trim())throw new Error("The host token must not be empty.");this.dashboardToken=e.dashboardToken;this.port=e.port??Ee;this.url=e.url??(e.port?`http://localhost:${e.port}`:mt);this.actionTimeoutMs=e.actionTimeoutMs??wt;this.log=e.log??(t=>process.stderr.write(`[buoy-mcp] ${t}
|
|
2
|
+
`));this.autoSpawn=e.dashboardToken?false:e.autoSpawnBroker??true}async start(){const e=await Oe(this.url)||await Oe(this.url,8e3);let t="connected";if(!e){if(!this.autoSpawn){throw new Error(`No Buoy broker reachable at ${this.url} and autoSpawnBroker is off. Start the Buoy desktop app, or enable autoSpawnBroker.`)}this.log(`no broker at ${this.url} \u2014 spawning one in-process on :${this.port}`);this.hostAccount=new ur(()=>this.broker?.refreshAccess());this.hostAccount.start();let i=false;this.broker=mr({canUseDesktop:this.hostAccount.hasAccess,port:this.port,onError:n=>{if(n.code==="EADDRINUSE")i=true;this.log(`broker error: ${n.code??n.message}`)}});await Pr(this.url).catch(n=>{if(!i)throw n});if(!this.broker.httpServer.listening)i=true;if(i){this.log(`another broker holds :${this.port} \u2014 connecting to it as a dashboard`);await this.broker.close().catch(()=>void 0);this.broker=null;this.hostAccount.stop();this.hostAccount=null;await this.connectSocket();return"connected"}try{gr(this.broker.dashboardToken,Te(this.port))}catch(n){this.log(`could not write the broker token file: ${n.message}`)}t="spawned"}else{this.log(`found a running broker at ${this.url} \u2014 connecting as a dashboard`)}await this.connectSocket();return t}connectSocket(){return Ai(()=>new Promise((e,t)=>{const i=pr(this.url,{transports:["websocket"],query:{deviceName:Se},auth:s=>{const c=this.dashboardToken??this.broker?.dashboardToken??wr(Te(this.port));s(c?{dashboardToken:c}:{})},reconnection:true,reconnectionDelay:500,timeout:4e3});this.socket=i;let n=false;const o=()=>{if(n)return;n=true;this.log("connected to broker");e()};const r=setTimeout(()=>{if(n)return;n=true;t(new Error("Broker account is locked or the broker needs an update. Sign in to Buoy Desktop or run npx --package=@buoy-gg/core buoy login for standalone MCP."))},5e3);i.on("buoy:access",s=>{const c=!!s&&typeof s==="object"&&s.protocol===1&&s.ready===true;this.setAdmission(c);if(c){clearTimeout(r);this.rearmWatches();o()}});i.on("connect",()=>{i.emit("buoy:access:request")});i.on("disconnect",()=>this.setAdmission(false,true));const a=s=>s.message===yr;i.once("connect_error",s=>{if(n)return;n=true;clearTimeout(r);t(new Error(a(s)?`The broker at ${this.url} refused this MCP as a dashboard: the token in ${Te(this.port)} is missing or stale. Update Buoy Desktop, or restart it so it writes the file.`:`failed to connect to broker at ${this.url}: ${s.message}`))});i.on("connect_error",s=>{if(!a(s)||i.active)return;setTimeout(()=>{if(this.socket===i&&!i.connected)i.connect()},1e3).unref?.()});i.on("devtool-capabilities",s=>{if(!this.admitted||this.lockedDevices.has(s.persistentDeviceId))return;this.capabilities.set(s.persistentDeviceId,s);this.trackRootLookup(this.learnDevServerOrigin(s.persistentDeviceId));if(s.tools?.some(c=>c.toolId==="console"))this.ensureWatching("console",s.persistentDeviceId)});i.on("all-devices-update",s=>{if(!this.admitted)return;if(!Array.isArray(s))return;let c=false;for(const h of s){if(h.deviceId&&(h.tier==="locked"||h.tier==="checking"))this.lockDevice(String(h.deviceId),h.tier==="checking");else if(h.deviceId&&(h.tier==="free"||h.tier==="pro")){c=this.lockedDevices.delete(String(h.deviceId))||c}}this.devices=s;const l=Date.now();for(const h of s)if(h.deviceId)this.lastSeen.set(h.deviceId,l);this.pruneStaleDevices();if(c)this.rearmWatches();else this.rearmReconnectedDevices(s)});i.on("devtool-sync",s=>{if(!this.admitted||this.lockedDevices.has(s.persistentDeviceId))return;const c=W(s.persistentDeviceId,s.toolId);this.snapshots.set(c,s.payload);this.snapshotAt.set(c,Date.now());if(s.toolId==="console")this.noteCrashReport(s.persistentDeviceId,s.payload);const l=this.snapshotWaiters.get(c);if(l&&l.length){this.snapshotWaiters.set(c,[]);for(const h of l)h(s.payload)}});i.on("devtool-action-result",s=>{if(!this.admitted)return;const c=this.pendingActions.get(s.requestId);if(!c)return;this.pendingActions.delete(s.requestId);clearTimeout(c.timer);c.resolve(s)})}))}isAdmitted(){return this.admitted}requireAdmission(){if(this.transportLost)throw new Error("The broker link was lost. Wait for it to reconnect.");if(!this.admitted)throw new Error("A verified Buoy account and an updated broker are required. Sign in to Buoy Desktop, or run npx --package=@buoy-gg/core buoy login for standalone MCP.")}setAdmission(e,t=false){this.transportLost=!e&&t;this.admitted=e;if(e)return;this.devices=[];this.lockedDevices.clear();this.capabilities.clear();this.snapshots.clear();this.snapshotAt.clear();this.devServerOrigins.clear();this.devServerRoots.clear();this.crashSeen.clear();this.originProbed.clear();this.health.clear();this.lastSeen.clear();if(!t)this.watching.clear();for(const i of this.pendingActions.values()){clearTimeout(i.timer);i.reject(new Error(t?"The broker link was lost during the call. Its result is unknown.":"Buoy account access ended while the action was pending."))}this.pendingActions.clear();for(const i of this.snapshotWaiters.values())for(const n of i)n(void 0);this.snapshotWaiters.clear();if(this.socket)this.socket.sendBuffer=[]}requireDeviceAccount(e){if(this.devices.some(t=>t.deviceId===e&&t.tier==="checking"))throw new Error(`${ee} The new app link is still checking access.`);if(this.lockedDevices.has(e))throw new Error("The device's Buoy account access ended. Sign in and verify its key.")}lockDevice(e,t=false){this.lockedDevices.add(e);this.capabilities.delete(e);this.health.delete(e);this.devServerOrigins.delete(e);this.devServerRoots.delete(e);this.originProbed.delete(e);const i=`${e}::`;for(const n of this.snapshots.keys())if(n.startsWith(i)){this.snapshots.delete(n);this.snapshotAt.delete(n)}for(const[n,o]of this.snapshotWaiters)if(n.startsWith(i)){this.snapshotWaiters.delete(n);for(const r of o)r(void 0)}for(const[n,o]of this.pendingActions)if(o.target===e||o.target===B){clearTimeout(o.timer);this.pendingActions.delete(n);o.reject(new Error(t?"The app link changed during the call. Its result is unknown.":"Device account access ended while the action was pending."))}}pruneStaleDevices(){const e=Date.now()-Fi;for(const[t,i]of this.lastSeen){if(i>e)continue;if(this.devices.some(o=>o.deviceId===t))continue;this.lastSeen.delete(t);this.capabilities.delete(t);this.health.delete(t);this.devServerOrigins.delete(t);this.devServerRoots.delete(t);this.originProbed.delete(t);const n=`${t}::`;for(const o of[...this.snapshots.keys()]){if(o.startsWith(n)){this.snapshots.delete(o);this.snapshotAt.delete(o)}}for(const o of[...this.watching.keys()]){if(o.startsWith(n))this.watching.delete(o)}}}async stop(){this.setAdmission(false);this.hostAccount?.stop();this.hostAccount=null;for(const[,e]of this.pendingActions){clearTimeout(e.timer);e.reject(new Error("SyncClient stopped"))}this.pendingActions.clear();for(const[,e]of this.snapshotWaiters)for(const t of e)t(void 0);this.snapshotWaiters.clear();this.socket?.close();this.socket=null;if(this.broker){await this.broker.close();this.broker=null}}listDevices(){if(!this.admitted)return[];return this.devices.filter(e=>e.deviceName!==Se&&e.deviceId).map(e=>{const t=this.capabilities.get(e.deviceId);return{deviceId:e.deviceId,deviceName:e.deviceName,platform:e.platform,isConnected:e.isConnected!==false,isPro:e.isPro===true,tier:e.tier==="free"||e.tier==="pro"||e.tier==="checking"?e.tier:e.tier===void 0&&e.isPro===true?"pro":"locked",collision:e.collision===true,tools:t?.tools??[]}})}connectedDevices(){return this.listDevices().filter(e=>e.isConnected)}toolActions(e){return this.listDevices().map(t=>{const i=t.tools.find(n=>n.toolId===e);return i?{deviceId:t.deviceId,deviceName:t.deviceName,actions:i.actions}:null}).filter(t=>t!==null)}peekProjectDevice(){if(!this.admitted)return null;if(this.sessionDevice)return this.sessionDevice;const e=this.connectedDevices().filter(i=>!this.lockedDevices.has(i.deviceId)&&i.tools.length>0);const t=process.env.BUOY_PROJECT_ROOT||process.cwd();return Ae(e.map(i=>({deviceId:i.deviceId,root:this.devServerRoots.get(i.deviceId)})),t)??null}resolveDevice(e){this.requireAdmission();if(e){this.requireDeviceAccount(e);return e}const t=(l,h)=>new Error(`${ee} "${l}" (${h}) is not connected right now. If it is restarting, it usually takes a few seconds; to use another device, pass deviceId (run list_devices to see them).`);if(this.sessionDevice){const l=this.devices.find(h=>h.deviceId===this.sessionDevice);if(l&&l.isConnected!==false)return this.sessionDevice;throw t(this.sessionDevice,"the device this session was using")}const i=this.connectedDevices();const n=i.filter(l=>!this.lockedDevices.has(l.deviceId));const o=n.filter(l=>l.tools.length>0);const r=process.env.BUOY_PROJECT_ROOT||process.cwd();const a=Ae(o.map(l=>({deviceId:l.deviceId,root:this.devServerRoots.get(l.deviceId)})),r);if(a)return this.sessionDevice=a;const s=Ae(this.devices.filter(l=>l.deviceId&&l.isConnected===false&&!this.lockedDevices.has(l.deviceId)).map(l=>({deviceId:l.deviceId,root:this.devServerRoots.get(l.deviceId)})),r);if(s)throw t(s,"this project's app");if(n.length===0){if(i.length>0){throw new Error(`Devices are connected (${i.map(l=>l.deviceName).join(", ")}), but the broker has not admitted their accounts. Check app account validation and the account announcement after reconnect; this does not prove the user is signed out.`)}throw new Error("No devices are connected to the broker. Open the app with Buoy devtools running.")}if(n.length===1)return this.sessionDevice=n[0].deviceId;if(o.length===1)return this.sessionDevice=o[0].deviceId;if(o.length===0){const l=n.map(h=>h.deviceName).join(", ");throw new Error(`Connected devices (${l}) are not exposing any Buoy tools yet. Make sure the app is in the foreground with devtools mounted, then retry.`)}const c=o.map(l=>`${l.deviceName} (${l.deviceId})`).join(", ");throw new Error(`Multiple devices connected \u2014 specify deviceId. Connected: ${c}`)}requireProDevice(e){this.requireAdmission();const t=se();if(e&&e!==B)this.sessionDevice=e;const i=a=>{if(!a)return"";return a.tier==="locked"?` The broker has not confirmed account access for "${a.deviceName}"; a locked status does not establish whether a key is configured.`:` The connected app "${a.deviceName}" is on the ${a.tier} tier.`};const n=a=>t.level==="pro"?de(t,i(a)):`The Buoy MCP needs an app on a Buoy account.${i(a)} Run \`npx --package=@buoy-gg/core buoy login\` in the project (free), restart the dev server with its cache cleared, then run \`list_devices\` to check.`;if(e===B){const a=this.connectedDevices().filter(c=>c.isPro||c.tier==="free");const s=a.find(c=>!ve(c,t.level));if(a.length===0)throw new Error(n());if(s)throw new Error(n(s));return B}const o=this.resolveDevice(e);const r=this.connectedDevices().find(a=>a.deviceId===o);if(!ve(r,t.level)){const a=this.accessState(o,t.level);if(a==="offline")throw new Error(`${ee} "${o}" is not connected right now. If the app is starting or reloading, it usually takes a few seconds. Run list_devices to check.`);if(a==="pending")throw new Error(`${ee} account access for "${o}" is still being confirmed (the app just connected). Try again in a moment.`);throw new Error(n(r))}return o}checkAppCall(e,t,i){const n=se();if(!n.tool)return;const o=i===B?this.connectedDevices().find(r=>r.tier==="free"&&!J(r)):this.listDevices().find(r=>r.deviceId===i&&!J(r));if(!o)return;if(n.level==="free"&&Ti(e,t))return;throw new Error(de({...n,level:"pro"},` The app "${o.deviceName}" is on the ${o.tier} tier${n.level==="free"?`, and this part of \`${n.tool}\` reads its ${e} data, which is Pro`:""}.`))}refuseBuoyPanelTap(e,t,i,n){if(e!=="highlight-updates"||t!=="tapElement")return null;const o=se();if(!o.tool||o.level!=="free")return null;const r=i??{};const a=typeof r.nativeTag==="number"?r.nativeTag:null;const s=typeof r.testID==="string"&&r.testID?r.testID.toLowerCase():null;const c=a!=null||s!=null||!!r.includeBuoy;if(n===B){if(!c||!this.connectedDevices().some(u=>u.tier==="free"&&!J(u)))return null;return Promise.reject(new Error(de({...o,level:"pro"},' On a free app, tap one app at a time (not "All") when tapping by testID or nativeTag.')))}const l=this.listDevices().find(u=>u.deviceId===n);if(!l||J(l)||!c)return null;const h=u=>new Error(de({...o,level:"pro"},u));if(r.includeBuoy)return Promise.reject(h(" That reaches Buoy's own panel, not the app."));return(async()=>{let u;try{u=await this.callAction("highlight-updates","describeScreen",{includeBuoy:false,includeHidden:true},n,{timeoutMs:4e3,silent:true})}catch(f){throw new Error(`Could not check that target before tapping (${f instanceof Error?f.message:String(f)}). On a free app, tap by query, or retry once the screen settles.`)}const d=(u?.elements??[]).some(f=>a!=null?f.nativeTag===a:f.testID?.toLowerCase()===s);if(!d&&a==null&&typeof r.query==="string"){const{testID:f,...m}=r;return m}if(!d){throw new Error(`"${a??String(r.testID)}" is not on the app's screen right now. On a free app, a tap by testID or nativeTag must hit an element describe_screen lists, and Buoy's own panels are Pro. Swipe it into view first, or tap by query.`)}})()}fullResultsFor(e){if(e===B){const t=this.connectedDevices();return t.length>0&&t.every(i=>J(i))}return J(this.listDevices().find(t=>t.deviceId===e))}deviceWasAllowed(e){return ve(this.listDevices().find(t=>t.deviceId===e),se().level)}accessState(e,t=se().level){const i=this.devices.find(n=>n.deviceId===e);if(!i||i.isConnected===false)return"offline";if(J(i)||t==="free"&&i.tier==="free")return"ready";return i.tier===void 0||i.tier==="checking"?"pending":"denied"}healthOf(e){let t=this.health.get(e);if(!t){t={consecutiveTimeouts:0,lastOkAt:0,lastFullBudgetAt:0};this.health.set(e,t)}return t}noteOk(e){const t=this.healthOf(e);t.consecutiveTimeouts=0;t.lastOkAt=Date.now()}noteTimeout(e){this.healthOf(e).consecutiveTimeouts+=1}isUnresponsive(e){return At(this.healthOf(e).consecutiveTimeouts)}timeoutFor(e,t,i){const n=this.healthOf(e);const{timeoutMs:o,usedFullBudget:r}=Ar({consecutiveTimeouts:n.consecutiveTimeouts,lastFullBudgetAt:n.lastFullBudgetAt,now:Date.now(),fullMs:this.actionTimeoutMs,shortMs:ke,recoveryProbeMs:gt,toolId:t,action:i});if(r)n.lastFullBudgetAt=Date.now();return o}unresponsiveNote(e){const t=this.healthOf(e);const i=t.lastOkAt?Math.round((Date.now()-t.lastOkAt)/1e3):null;const n=Or({consecutiveTimeouts:t.consecutiveTimeouts,lastOkAt:t.lastOkAt,now:Date.now(),unresponsiveAfterMs:ji});const o=[`The app on "${e}" is not responding (${t.consecutiveTimeouts} call${t.consecutiveTimeouts===1?"":"s"} unanswered${i!=null?`, last answered ${i}s ago`:""}).`,n?"It is most likely CRASHED \u2014 a fatal render error unmounts the React tree, and the dev tools live in that tree, so nothing on-device can answer until the app is reloaded.":"It may be busy, mid-reload, or crashing."];const r=this.lastFatalFor(e);if(r)o.push(`Last captured error: ${r}`);o.push(r?"Read the crash state with get_console / get_storage (served from the last sync), then reload_app to recover.":"Try get_console (served from the last sync) for the app's last words, then reload_app to recover.");return o.join(" ")}noteCrashReport(e,t){const i=t?.entries;if(!Array.isArray(i)||i.length===0)return;const n=Math.max(...i.map(a=>typeof a?.timestamp==="number"?a.timestamp:-Infinity));const o=this.crashSeen.get(e)??new Set;let r=false;for(const a of i){const s=a?.fatal||/^\[(RENDER ERROR|FATAL)/.test(a?.message??"");if(!s||typeof a.timestamp!=="number")continue;const c=`${a.id??""}|${a.timestamp}|${(a.message??"").slice(0,120)}`;if(o.has(c))continue;o.add(c);if(n-a.timestamp<3e4)r=true}if(o.size>200)for(const a of[...o].slice(0,o.size-200))o.delete(a);this.crashSeen.set(e,o);if(!r)return;if(![...this.pendingActions.values()].some(a=>a.target===e&&a.expire&&!a.cut))return;void this.callAction("app","ping",{},e,{timeoutMs:1500,silent:true}).then(()=>void 0,()=>{const a=Date.now();for(const s of this.pendingActions.values()){if(s.target!==e||!s.expire||s.cut)continue;s.cut=true;if(s.deadline!==void 0&&s.deadline-a<=ke)continue;clearTimeout(s.timer);s.timer=setTimeout(s.expire,ke)}})}lastFatalFor(e){const t=this.snapshots.get(W(e,"console"));const i=t?.entries;if(!Array.isArray(i))return null;for(let n=i.length-1;n>=0;n--){const o=i[n];if(o?.fatal||o?.level==="error"){const r=(o.message??"").split("\n")[0];if(r)return r.length>300?`${r.slice(0,300)}\u2026`:r}}return null}snapshotAgeMs(e,t){this.requireAdmission();const i=this.snapshotAt.get(W(t,e));return i?Date.now()-i:null}noteDevServerOrigin(e,t){if(typeof t==="string"&&/^https?:\/\//.test(t)){const i=this.devServerOrigins.get(e)!==t;this.devServerOrigins.set(e,t);if(i)this.devServerRoots.delete(e);if(i||!this.devServerRoots.has(e)){const n=Er(t).then(o=>{if(o&&this.devServerOrigins.get(e)===t)this.devServerRoots.set(e,o)});this.trackRootLookup(n)}}}async learnDevServerOrigin(e){if(this.devServerOrigins.has(e)||this.originProbed.has(e))return;this.originProbed.add(e);const t=this.capabilities.get(e)?.tools?.some(i=>i.toolId==="app");if(!t)return;try{const{payload:i}=await this.getSnapshot("app",e,{timeoutMs:4e3,silent:true});this.noteDevServerOrigin(e,i?.devServerOrigin)}catch{}}trackRootLookup(e){const t=e.catch(()=>void 0).finally(()=>this.rootLookups.delete(t));this.rootLookups.add(t)}async awaitBroker(){if(Date.now()-this.createdAt>15e3)return;for(let e=0;!this.admitted&&e<4e3;e+=100)await new Promise(t=>setTimeout(t,100));for(let e=0;this.admitted&&this.devices.length===0&&e<1500;e+=100)await new Promise(t=>setTimeout(t,100))}async settleDeviceChoice(e=1500){await this.awaitBroker();if(this.sessionDevice||this.rootLookups.size===0||this.connectedDevices().length<2)return;await Promise.race([Promise.all([...this.rootLookups]),new Promise(t=>setTimeout(t,e))]);if(this.rootLookups.size)await Promise.race([Promise.all([...this.rootLookups]),new Promise(t=>setTimeout(t,Math.min(800,e)))])}devServerOrigin(e){this.requireAdmission();const t=this.devServerOrigins.get(e);if(t)return t;const i=this.snapshots.get(W(e,"app"));if(typeof i?.devServerOrigin==="string")return i.devServerOrigin;return null}ensureWatching(e,t){const i=W(t,e);if(this.watching.has(i))return;this.watching.set(i,{toolId:e,targetDeviceId:t});this.sendWatch(e,t)}sendWatch(e,t){if(!this.admitted||!this.socket?.connected||this.lockedDevices.has(t))return;this.socket?.emit("devtool-watch",{toolId:e,targetDeviceId:t,watching:true,stream:false})}rearmWatches(){for(const{toolId:e,targetDeviceId:t}of this.watching.values()){this.sendWatch(e,t)}}rearmReconnectedDevices(e){for(const t of e){if(!t.deviceId||t.isConnected===false)continue;const i=String(t.deviceId);const n=`${t.id}|${t.connectionNonce??""}`;const o=this.deviceConnections.get(i);this.deviceConnections.set(i,n);if(o===void 0||o===n)continue;for(const{toolId:r,targetDeviceId:a}of this.watching.values()){if(a===i)this.sendWatch(r,a)}}}unwatch(e,t){const i=W(t,e);if(!this.watching.delete(i))return;if(this.admitted&&this.socket?.connected)this.socket.emit("devtool-watch",{toolId:e,targetDeviceId:t,watching:false})}cachedSnapshot(e,t){this.requireAdmission();this.requireDeviceAccount(t);this.checkAppCall(e,"@snapshot",t);return this.snapshots.get(W(t,e))}async getSnapshot(e,t,i={}){const n=this.resolveDevice(t);this.checkAppCall(e,"@snapshot",n);const o=this.connectedDevices().find(h=>h.deviceId===n)?.tools??[];if(!i.silent&&o.length>0&&!o.some(h=>h.toolId===e)){throw new Error(`This app doesn't have the "${e}" tool installed, so there is no state to read. The app itself is fine.`)}const r=W(n,e);this.ensureWatching(e,n);let a=i.timeoutMs??yt;if(i.timeoutMs===void 0&&this.isUnresponsive(n)){const h=this.healthOf(n);if(Date.now()-h.lastFullBudgetAt>gt){h.lastFullBudgetAt=Date.now()}else{a=Bi}}const s=this.waitForSnapshot(r,a);this.socket?.emit("request-devtool-state",{toolId:e,targetDeviceId:n});const c=await s;this.requireAdmission();this.requireDeviceAccount(n);if(c!==void 0){if(!i.silent)this.noteOk(n);return{deviceId:n,payload:c}}if(!i.silent)this.noteTimeout(n);const l=this.snapshots.get(r);if(l!==void 0){return{deviceId:n,payload:l,stale:this.staleNote(e,n)}}throw new Error(`Timed out waiting for "${e}" state from ${n} (is the tool active?). ${this.unresponsiveNote(n)}`)}lastSync(e,t){this.checkAppCall(t,"@snapshot",e);const i=this.snapshots.get(W(e,t));return i===void 0?void 0:{payload:i,ageMs:this.snapshotAgeMs(t,e)}}staleNote(e,t){const i=this.snapshotAgeMs(e,t);const n=i==null?"an earlier sync":`${Math.round(i/1e3)}s ago`;return`\u26A0\uFE0F Served from the last sync (${n}) \u2014 ${this.unresponsiveNote(t)}`}waitForSnapshot(e,t){return new Promise(i=>{const n=this.snapshotWaiters.get(e)??[];const o=setTimeout(()=>{const a=(this.snapshotWaiters.get(e)??[]).filter(s=>s!==r);this.snapshotWaiters.set(e,a);i(void 0)},t);const r=a=>{clearTimeout(o);i(a)};n.push(r);this.snapshotWaiters.set(e,n)})}async callAction(e,t,i,n,o={}){this.requireAdmission();const r=n===B?B:this.resolveDevice(n);this.checkAppCall(e,t,r);const a=this.refuseBuoyPanelTap(e,t,i,r);if(a)i=await a??i;if(!this.socket)throw new Error("SyncClient is not connected");const s=fr();const c=o.timeoutMs??Math.max(this.timeoutFor(r,e,t),Cr(e,t,i));const l=await new Promise((h,u)=>{const d=Date.now();const f=()=>{this.pendingActions.delete(s);if(r!==B&&!o.silent)this.noteTimeout(r);u(new Error(`Action "${e}.${t}" got no answer in ${Date.now()-d}ms. `+(r===B?"No device responded.":this.unresponsiveNote(r))))};const m=setTimeout(f,c);this.pendingActions.set(s,{resolve:h,reject:u,timer:m,target:r,expire:f,deadline:d+c});this.socket.emit("devtool-action",{requestId:s,toolId:e,targetDeviceId:r,action:t,params:i??{}})});if(r!==B&&!o.silent)this.noteOk(r);if(!l.ok){throw new Error(l.error||`Action "${e}.${t}" failed on device`)}return l.result}};var W=(e,t)=>`${e}::${t}`;function Oe(e,t=800){return new Promise(i=>{const n=dr.get(e,o=>{o.resume();i((o.statusCode??0)>0)});n.setTimeout(t,()=>{n.destroy();i(false)});n.on("error",()=>i(false))})}async function Pr(e,t=20){for(let i=0;i<t;i++){if(await Oe(e,300))return;await Rr(50)}throw new Error(`Spawned broker never became reachable at ${e}`)}var Rr=e=>new Promise(t=>setTimeout(t,e));import{execFile as Dr,spawn as xr}from"child_process";import{promisify as Nr}from"util";import{promises as Me}from"fs";import Ie from"path";import{SaxesParser as Lr}from"saxes";var Br=32*1024*1024;function Ot(e){if(Buffer.byteLength(e)>Br)throw new Error("Trace XML is too large. Use a shorter run.");const t={tag:"root",attrs:{},text:"",children:[]};const i=[t];const n=new Map;let o=0;const r=new Lr;r.on("doctype",()=>{throw new Error("Trace XML must not use a DTD.")});r.on("opentag",h=>{if(++o>5e5||i.length>64)throw new Error("Trace XML is too deep or too large.");const u={tag:h.name,attrs:h.attributes,text:"",children:[]};i[i.length-1].children.push(u);i.push(u);if(u.attrs.id){if(n.has(u.attrs.id))throw new Error("Trace XML has a repeated ID.");n.set(u.attrs.id,u)}});r.on("text",h=>{i[i.length-1].text+=h});r.on("closetag",()=>{i.pop()});r.write(e).close();function a(h){const u=new Set;while(h.attrs.ref){if(u.has(h))throw new Error("Trace XML has a ref loop.");u.add(h);const d=n.get(h.attrs.ref);if(!d)throw new Error("Trace XML has a missing ref.");h=d}return h}function s(h,u,d=0){if(d>64)throw new Error("Trace XML has a ref loop.");h=a(h);return[...h.tag===u?[h]:[],...h.children.flatMap(f=>s(f,u,d+1))]}const c=h=>{if(!h)throw new Error("Trace XML lacks a number.");const u=Number(a(h).text);if(!Number.isFinite(u)||!a(h).text.trim())throw new Error("Trace XML has a bad number.");return u};const l=s(t,"node");return{nodes:l,resolve:a,descendants:s,num:c}}function jr(e,t){const{nodes:i,resolve:n,descendants:o,num:r}=Ot(e);const a=[];for(const s of i){const c=s.children.find(h=>h.tag==="schema");if(c?.attrs.name!=="time-profile")continue;const l=c.children.map(h=>h.children.find(u=>u.tag==="mnemonic")?.text);for(const h of s.children.filter(u=>u.tag==="row")){const u=b=>h.children[l.indexOf(b)];const d=u("thread");const f=u("process");if(!d||!f||r(o(f,"pid")[0])!==t)continue;const m=r(o(d,"tid")[0]);const p=u("stack")?o(u("stack"),"frame").map(b=>({symbol:(b.attrs.name||b.attrs.addr||"Unknown").slice(0,180),image:(o(b,"binary")[0]?.attrs.name||"Unknown").slice(0,100)})):[];const w=r(u("time"))/1e6;const y=r(u("weight"))/1e6;if(w<0||y<=0||y>1e3)throw new Error("Trace sample timing is invalid.");a.push({timeMs:w,weightMs:y,tid:m,main:/Main Thread/i.test(n(d).attrs.fmt||""),running:n(u("thread-state")).text==="Running",frames:p})}}return a.sort((s,c)=>s.timeMs-c.timeMs)}function Fr(e,t,i){const{nodes:n,descendants:o,num:r}=Ot(e);const a=[];for(const s of n){const c=s.children.find(h=>h.tag==="schema");if(c?.attrs.name!=="potential-hangs")continue;const l=c.children.map(h=>h.children.find(u=>u.tag==="mnemonic")?.text);for(const h of s.children.filter(u=>u.tag==="row")){const u=f=>h.children[l.indexOf(f)];if(!u("process")||r(o(u("process"),"pid")[0])!==t)continue;const d=r(u("duration"))/1e6;if(d>=i)a.push({startMs:r(u("start"))/1e6,durationMs:d,method:"os-hang",topSymbols:[],sampleCount:0})}}return a}import{execFile as Ur}from"child_process";function Mt(e,t){const i=e.filter(n=>Number.isFinite(n)&&n>0);if(!i.length)return null;return{source:t,samples:i.length,startBytes:i[0],peakBytes:Math.max(...i),endBytes:i[i.length-1],growthBytes:i[i.length-1]-i[0]}}function Hr(e){const t=[];let i;let n=false;let o;const r=new Promise(c=>{o=c});const a=()=>{if(i||n)return;i=new Promise(c=>{Ur("ps",["-o","rss=","-p",String(e)],{timeout:1e3,maxBuffer:1024},(l,h)=>{const u=Number(h.trim());if(!l&&Number.isFinite(u)&&u>0)t.push(u*1024);c()})}).finally(()=>{i=void 0})};a();const s=setInterval(a,250);return{result:r,stop(){if(n)return;n=true;clearInterval(s);void Promise.resolve(i).then(()=>o(Mt(t,"host-ps-rss")))}}}function Yr(e){const t=e.udid;const i=e.appId;return new Error(`xctrace did not attach. Both tries hit the 45 second cap. This is an Xcode/CoreSimulator attach issue. First, close and open this app. Then try the trace again:
|
|
3
|
+
xcrun simctl terminate ${t} ${i}
|
|
4
|
+
xcrun simctl launch ${t} ${i}
|
|
5
|
+
If it still fails, reboot just this sim. Then open the app and try again:
|
|
6
|
+
xcrun simctl shutdown ${t}
|
|
7
|
+
xcrun simctl boot ${t}
|
|
8
|
+
xcrun simctl bootstatus ${t} -b
|
|
9
|
+
xcrun simctl launch ${t} ${i}`)}var Wr=class extends Error{constructor(){super(...arguments);this.name="AttachTimeout"}};function qr(e,t,i){let n=e();let o=0;let r=false;let a;let s;const c=new Promise((h,u)=>{a=h;s=u});void c.catch(()=>{});const l=(async()=>{while(true){try{await n.ready}catch(h){await n.result.catch(()=>{});if(!r&&o===0&&h?.name==="AttachTimeout"){await t();if(r)throw h;o++;n=e();continue}if(!r&&o===1&&h?.name==="AttachTimeout"&&i)throw i();throw h}a();return await n.result}})().catch(h=>{s(h);throw h});void l.catch(()=>{});return{get pid(){return n.pid},get attachRetries(){return o},ready:c,result:l,stop(){r=true;n.stop()},cancel(){r=true;n.cancel()}}}var Ce=Nr(Dr);async function It(e,t){if(process.platform!=="darwin")throw new Error("Native Profiler needs macOS with Xcode.");if(!/^[A-Fa-f0-9-]{36}$/.test(e)||!/^[A-Za-z0-9_.-]+$/.test(t))throw new Error("Bad app or sim ID.");const{stdout:i}=await Ce("xcrun",["simctl","spawn",e,"launchctl","list"],{timeout:1e4});const n=i.split("\n").filter(o=>o.includes(`UIKitApplication:${t}[`));if(n.length!==1||!/^\d+\s/.test(n[0]))throw new Error("Open the app on the chosen sim first.");return Number(n[0].split(/\s/)[0])}async function Ct(e){let t=0;for(const i of await Me.readdir(e,{withFileTypes:true})){const n=Ie.join(e,i.name);if(i.isDirectory())t+=await Ct(n);else if(i.isFile())t+=(await Me.stat(n)).size}return t}var zr=(e,t,i,n,o)=>{const r=Ie.join(t,"capture.trace");const a=new AbortController;const s=xr("xcrun",["xctrace","record","--device",e.udid,"--attach",String(e.pid),"--instrument","Time Profiler","--time-limit",`${i}ms`,"--no-prompt","--output",r],{stdio:["ignore","pipe","pipe"]});let c;let l;let h;let u;const d=new Promise((O,M)=>{h=O;u=M});void d.catch(()=>{});let f="";let m=false;let p=false;let w;let y;const b=()=>{if(p)return;c?.stop();s.kill("SIGINT");if(!y)y=setTimeout(()=>{w??=new Error("Xcode did not save the trace within 45 seconds of stop.");s.kill("SIGKILL")},45e3)};const v=()=>{a.abort();if(p)return;w??=new Error("Trace was stopped before it could finish.");s.kill("SIGKILL")};const g=setTimeout(()=>{w??=new Wr("Xcode did not start the trace within 45 seconds.");b()},45e3);const _=setTimeout(()=>{w??=new Error("Xcode took too long to finish the trace.");b()},i+6e4);let E=false;const S=setInterval(()=>{if(E)return;E=true;void Ct(t).then(O=>{if(O>256*1024*1024){w??=new Error("Trace reached the 256 MiB cap.");b()}},()=>{}).finally(()=>{E=false})},2e3);const I=O=>{f=(f+O.toString()).slice(-16e3);if(!m&&!w&&f.includes("Ctrl-C to stop the recording")){m=true;clearTimeout(g);c=Hr(e.pid);l=setTimeout(()=>c?.stop(),i);h()}};s.stdout.on("data",I);s.stderr.on("data",I);const A=new Promise((O,M)=>{const F=R=>{w=R};s.once("error",F);s.once("close",R=>{p=true;c?.stop();clearTimeout(l);clearTimeout(g);clearTimeout(_);clearTimeout(y);clearInterval(S);if(w||!m||R!==0&&!f.includes("Output file saved")){const V=w?Object.assign(new Error(`${w.message} ${f.slice(-1e3)}`),{name:w.name}):new Error(`Xcode could not save the trace. ${f.slice(-1e3)}`);u(V);M(V)}else O()})});const C=A.then(async()=>{const O=async bi=>{const{stdout:vi}=await Ce("xcrun",["xctrace","export","--input",r,"--xpath",`/trace-toc/run[@number="1"]/data/table[@schema="${bi}"]`],{signal:a.signal,timeout:45e3,maxBuffer:32*1024*1024});return vi};const{stdout:M}=await Ce("xcrun",["xctrace","export","--input",r,"--toc"],{signal:a.signal,timeout:3e4,maxBuffer:1024*1024});const F=Number(/<duration>([\d.]+)<\/duration>/.exec(M)?.[1])*1e3;if(!Number.isFinite(F)||F<=0)throw new Error("Xcode did not return a valid trace span.");if(!M.includes('schema="time-profile"'))throw new Error("This Xcode trace has no CPU sample table.");const R=await O("time-profile");const V=jr(R,e.pid);const ne=["Busy spans from samples are estimates. They do not find sleeping or blocked hangs.","Frame data is not captured on iOS."];let tt=[];if(!M.includes('schema="potential-hangs"'))ne.push("No OS hang rows were captured. Busy spans use CPU samples.");if(M.includes('schema="potential-hangs"')){try{tt=Fr(await O("potential-hangs"),e.pid,n)}catch{ne.push("OS hang rows could not be read. Used CPU samples.")}}if(f.includes("[Warning]"))ne.push("Xcode warned of a trace source gap. CPU rows were checked on their own.");const it=await c?.result??null;if(!it)ne.push("Host RSS could not be read. Memory use is unknown.");return{samples:V,hangs:tt,durationMs:F,warnings:ne,memory:it}});void C.catch(()=>{});try{o?.(s.pid)}catch(O){w=O;v()}return{pid:s.pid,ready:d,result:C,stop:b,cancel:v}};var Kr=(e,t,i,n,o)=>qr(()=>zr(e,t,i,n,o),()=>Me.rm(Ie.join(t,"capture.trace"),{recursive:true,force:true}),()=>Yr(e));import{promises as N,writeFileSync as Gr}from"fs";import Z from"path";import Vr from"os";import{createHash as Jr,randomUUID as $t}from"crypto";import{promises as Pt}from"fs";import ae from"path";import{execFile as Xr}from"child_process";import{promisify as Zr}from"util";import{execFile as Qr}from"child_process";import{promisify as en}from"util";var $e=/\{\{\s*secret:([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g;var te=6;var Pe=new Map;var Rt=()=>false;function tn(e){Rt=e}function rn(e){$e.lastIndex=0;return $e.test(e)}function nn(e,t=process.env){if(!rn(e))return e;if(Rt()){throw new Error("A screen recording is running, and a video can't hide a typed secret. Stop the recording first (screen_recording stop), then type it.")}const i=[];const n=[];const o=e.replace($e,(r,a)=>{const s=t[`BUOY_SECRET_${a}`]??t[a];if(s===void 0||s===""){i.push(a);return""}if(s.length<te){n.push(a);return""}Pe.set(a,s);return s});if(i.length){throw new Error(`No value for ${i.map(r=>`{{secret:${r}}}`).join(", ")}. Set ${i.map(r=>`BUOY_SECRET_${r}`).join(", ")} in the environment the MCP server runs in (for example in your editor's MCP config "env").`)}if(n.length){throw new Error(`${n.map(r=>`{{secret:${r}}}`).join(", ")} is shorter than ${te} characters, so Buoy can't hide it reliably in what it sends back. Use a longer value, or type it as plain text if it isn't sensitive.`)}return o}function Dt(e=process.env){const t=new Map(Pe);for(const[i,n]of Object.entries(e))if(i.startsWith("BUOY_SECRET_")&&n)t.set(i.slice("BUOY_SECRET_".length),n);return t}var sn=e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");function xt(e){const t=new Map;for(const[o,r]of Dt()){if(r.length<te)continue;for(const a of[r,JSON.stringify(r).slice(1,-1),encodeURIComponent(r)])t.set(a,o);for(let a=r.length-1;a>=te;a--){t.set(`${r.slice(0,a)}\u2026`,o);t.set(`${r.slice(0,a)}...`,o);if(a>=8)t.set(r.slice(0,a),o)}}if(t.size===0)return e;const i=[...t.keys()].sort((o,r)=>r.length-o.length);const n=new RegExp(i.map(sn).join("|"),"g");return e.replace(n,(o,r,a)=>{if(/^\d+$/.test(o)){const s=a.slice(Math.max(0,r-8),r);const c=a.slice(r+o.length,r+o.length+8);if(/(\d|\d\.)$/.test(s)||/^(\d|\.\d|[eE][+-]?\d)/.test(c))return o;if(/[:[,]\s*$/.test(s)&&/^\s*([,\]}]|$)/.test(c)&&!on(a,r))return`"{{secret:${t.get(o)}}}"`}return`{{secret:${t.get(o)}}}`})}function on(e,t){let i=false;for(let n=0;n<t;n++){if(e[n]==="\\")n++;else if(e[n]==='"')i=!i}return i}function an(){return[...Pe.values()]}function cn(){return[...Dt().values()].filter(e=>e.length>=te)}var Nt=en(Qr);var Re="Android control needs adb on this computer (Android SDK platform-tools). Install it, or add it to PATH.";function D(e){return`'${e.replace(/'/g,`'\\''`)}'`}function me(e){if(!/^[A-Za-z][\w]*(\.[A-Za-z_][\w]*)+$/.test(e))throw new Error(`"${e}" is not a valid Android package name.`);return e}function De(e,t){const i=e?rt(e):null;if(i)return{bin:i.adb,args:["-P",String(i.port),"-s",e,...t]};return{bin:"adb",args:e?["-s",e,...t]:t}}async function K(e,t,i=15e3){try{const n=De(e,t);const{stdout:o}=await Nt(n.bin,n.args,{timeout:i,maxBuffer:32*1024*1024});return o}catch(n){const o=n;if(o.code==="ENOENT")throw new Error(Re);const r=t[0]==="shell"?`shell ${(t[1]??"").split(" ")[0]}`:t.slice(0,2).join(" ");const a=t[0]==="shell"?o.stderr||(o.killed?"timed out":"no error text"):o.stderr||o.message||"";const s=xt(a.replace(/'[^']*'/g,"'\u2026'")).trim().slice(0,300);throw new Error(`adb ${r} failed: ${s}`)}}function k(e,t,i=15e3){return K(e,["shell",t],i)}async function Lt(e,t,i=15e3){try{const n=De(e,t);const{stdout:o}=await Nt(n.bin,n.args,{timeout:i,encoding:"buffer",maxBuffer:64*1024*1024});return o}catch(n){const o=n;if(o.code==="ENOENT")throw new Error(Re);throw new Error(`adb ${t.slice(0,2).join(" ")} failed: ${o.message.slice(0,300)}`)}}async function Bt(){const e=[...ki().keys()];try{const t=ln(await K(null,["devices"]));return[...t,...e.filter(i=>!t.includes(i))]}catch(t){if(e.length&&t.message===Re)return e;throw t}}function ln(e){return e.split("\n").slice(1).map(t=>t.trim().split(/\s+/)).filter(t=>t[1]==="device").map(t=>t[0])}async function hn(e){const t=un(await k(e,"wm size"));const i=dn(await k(e,"dumpsys input").catch(()=>""));return i%2===1?{width:t.height,height:t.width}:t}function un(e){const t=/Override size:\s*(\d+)x(\d+)/.exec(e);const i=/Physical size:\s*(\d+)x(\d+)/.exec(e);const n=t??i;if(!n)throw new Error(`Could not read the Android screen size (adb said: ${e.trim().slice(0,120)}).`);return{width:Number(n[1]),height:Number(n[2])}}function dn(e){const t=/SurfaceOrientation:\s*(\d)/.exec(e);return t?Number(t[1]):0}async function fn(e,t){const i=await k(e,"dumpsys window windows").catch(()=>"");return pn(i,t)}function pn(e,t){const i=e.split(/\n(?=\s*Window #\d+)/);for(const n of i){if(!n.includes(`${t}/`))continue;const o=/mFrame=\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]/.exec(n)??/\bframe=\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]/.exec(n);if(o){const[r,a,s,c]=o.slice(1).map(Number);if(s>r&&c>a)return{x:r,y:a,width:s-r,height:c-a}}}return null}async function mn(e){return wn(await k(e,"dumpsys activity activities"))}function wn(e){const t=/(?:topResumedActivity|mResumedActivity|ResumedActivity)[^\n]*?\s([a-zA-Z][\w.]*)\/[\w.$]+/.exec(e);return t?t[1]:null}async function gn(e){const t=await k(e,"pm list packages -3");return t.split("\n").map(i=>i.trim().replace(/^package:/,"")).filter(i=>/^[A-Za-z][\w.]+$/.test(i))}async function jt(e,t){me(t);const i=await k(e,`run-as ${D(t)} cat files/react-buoy/@react_buoy_sync_device_id.json 2>/dev/null`,5e3).catch(()=>"");const n=i.trim().replace(/^"|"$/g,"");if(/^[\w-]{4,64}$/.test(n))return n;const o=await Lt(e,["exec-out",`run-as ${D(t)} sh -c 'cat app_webview/Default/"Local Storage"/leveldb/*.log app_webview/Default/"Local Storage"/leveldb/*.ldb 2>/dev/null'`],5e3).catch(()=>null);return o?yn(o):null}function yn(e){const t=e.toString("latin1");const i=/@react_buoy_sync_device_id([\s\S])\x01([\w-]{4,64})/g;let n=null;for(let o=i.exec(t);o;o=i.exec(t)){const r=o[1].charCodeAt(0)-1;if(r>=4&&r<=o[2].length)n=o[2].slice(0,r)}return n}function bn(e){if(/[^\x20-\x7e]/.test(e))throw new Error("adb can only type plain ASCII text. Use tap_element's text for other characters.");return D(e.replace(/ /g,"%s"))}async function vn(e,t,i,n){const[o,r]=[Math.round(t),Math.round(i)];if(n)await k(e,`input swipe ${o} ${r} ${o} ${r} ${Math.round(n)}`);else await k(e,`input tap ${o} ${r}`)}async function _n(e,t,i,n){const o=[...t,...i].map(r=>Math.round(r)).join(" ");await k(e,`input swipe ${o} ${Math.round(n)}`)}async function En(e,t){const i=t.replace(/ /g,"\0");for(let n=0;n<i.length;n+=40){const o=i.slice(n,n+40).replace(/\u0000/g," ");await k(e,`input text ${bn(o)}`)}}async function kn(e){const t=await Lt(e,["exec-out","screencap","-p"]);if(t.length<8||t.readUInt32BE(0)!==2303741511)throw new Error("The Android screenshot was not a PNG.");return t}async function Ft(e,t){if(t){await k(e,"settings put global sysui_demo_allowed 1");for(const i of["-e command enter","-e command clock -e hhmm 0941","-e command battery -e level 100 -e plugged false","-e command network -e wifi show -e level 4","-e command notifications -e visible false"]){await k(e,`am broadcast -a com.android.systemui.demo ${i}`).catch(()=>void 0)}}else{await k(e,"am broadcast -a com.android.systemui.demo -e command exit").catch(()=>void 0)}}async function Sn(e){const t=await K(e,["exec-out","uiautomator","dump","/dev/tty"],2e4);return Tn(t)}function Tn(e){const t=[];const i=(n,o)=>{const r=new RegExp(`\\s${o}="([^"]*)"`).exec(n);return r?An(r[1]):""};for(const n of e.matchAll(/<node\b[^>]*>/g)){const o=n[0];const r=/\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]/.exec(i(o,"bounds"));if(!r)continue;const[a,s,c,l]=r.slice(1).map(Number);t.push({text:i(o,"text"),desc:i(o,"content-desc"),id:i(o,"resource-id"),cls:i(o,"class"),clickable:i(o,"clickable")==="true",bounds:{x:a,y:s,width:c-a,height:l-s}})}return t}function An(e){return e.replace(/"/g,'"').replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">").replace(/&#(\d+);/g,(t,i)=>String.fromCharCode(Number(i))).replace(/&/g,"&")}function On(e,t){const i=t.trim().toLowerCase();const n=c=>{let l=0;for(const h of[c.desc,c.text,c.id.split("/").pop()??""]){const u=h.trim().toLowerCase();if(!u)continue;if(u===i)l=Math.max(l,3);else if(u.startsWith(i))l=Math.max(l,2);else if(u.includes(i))l=Math.max(l,1)}return l};const o=e.filter(c=>c.bounds.width>0&&c.bounds.height>0&&n(c)>0);const r=Math.max(0,...o.map(n));const a=o.filter(c=>n(c)===r);let s=a.filter((c,l)=>a.findIndex(h=>JSON.stringify(h.bounds)===JSON.stringify(c.bounds))===l);if(s.length>1){const c=s.filter(l=>[l.desc,l.text,l.id.split("/").pop()??""].some(h=>h.trim()===t.trim()));if(c.length)s=c}if(s.length>1){const c=s.filter(l=>l.clickable||/EditText|Button/.test(l.cls));if(c.length)s=c}return s}import{promises as ie}from"fs";import Ut from"path";import{createHash as Mn,randomUUID as In}from"crypto";import{execFile as Cn}from"child_process";import{promisify as $n}from"util";var Pn=$n(Cn);var Ht="v52.0";var Rn={"darwin-arm64":["mac-arm64","5d7d89fb2472c2ef40413141d5e8f3603d4a500a645d35aa57cb023b9d635b31"],"darwin-x64":["mac-amd64","0ce16fc3e2add79cd66c6596a04ec61eb3b2b8d5507496c5d325f57cb89dc5e8"],"linux-x64":["linux-amd64","411402424f34f2ac1fafa819493c13827ae5a10e335d9387a483c4b696b7f6e1"],"linux-arm64":["linux-arm64","e52888ab9b7d4f3d2620a6f803402e60a11230536d048d0ece4f33b78dd7c368"]};async function Dn(e,t){const i=Rn[`${process.platform}-${process.arch}`];if(!i)throw new Error("Perfetto needs a Mac or Linux host for now.");const[n,o]=i;const r=Ut.join(e,Ht,n);const a=Ut.join(r,"trace_processor_shell");const s=w=>Mn("sha256").update(w).digest("hex")===o;const c=await ie.stat(a).catch(()=>null);const l=c&&c.size<=40*1024*1024?await ie.readFile(a).catch(()=>null):null;if(l&&s(l))return a;const h=await fetch(`https://commondatastorage.googleapis.com/perfetto-luci-artifacts/${Ht}/${n}/trace_processor_shell`,{signal:t?AbortSignal.any([t,AbortSignal.timeout(6e4)]):AbortSignal.timeout(6e4)});if(!h.ok||Number(h.headers.get("content-length"))>40*1024*1024)throw new Error("The pinned Perfetto tool could not be fetched.");if(!h.body)throw new Error("The Perfetto download was empty.");const u=h.body.getReader();const d=[];let f=0;try{while(true){const w=await u.read();if(w.done)break;f+=w.value.length;if(f>40*1024*1024)throw new Error("The Perfetto download is too large.");d.push(Buffer.from(w.value))}}finally{await u.cancel()}const m=Buffer.concat(d);if(!s(m))throw new Error("The Perfetto checksum did not match.");await ie.mkdir(r,{recursive:true,mode:448});const p=`${a}.${In()}.tmp`;try{await ie.writeFile(p,m,{mode:448});await ie.rename(p,a)}finally{await ie.rm(p,{force:true})}return a}function xn(e){const t=[];let i=[];let n="";let o=false;for(let a=0;a<e.length;a++){const s=e[a];if(s==='"'){if(o&&e[a+1]==='"'){n+='"';a++}else o=!o}else if(!o&&(s===","||s==="\n")){i.push(n);n="";if(s==="\n"){if(i.some(Boolean))t.push(i);i=[]}}else if(s!=="\r"||o)n+=s}if(o)throw new Error("Perfetto returned a broken data row.");if(n||i.length){i.push(n);t.push(i)}const r=t.shift()??[];return t.map(a=>{if(a.length!==r.length)throw new Error("Perfetto returned a short data row.");return Object.fromEntries(r.map((s,c)=>[s,a[c]]))})}async function Nn(e,t,i,n){const{stdout:o}=await Pn(e,["-Q",i,t],{signal:n,timeout:45e3,maxBuffer:32*1024*1024});return xn(o)}async function Ln(e,t,i,n,o){if(!Number.isSafeInteger(i)||i<1||!/^[\w.]+$/.test(n))throw new Error("Bad trace app ID.");const r=w=>Nn(e,t,w,o);const a=await r(`SELECT upid FROM process WHERE pid=${i} AND (name='${n}' OR cmdline='${n}')`);if(a.length!==1||!/^\d+$/.test(a[0].upid))throw new Error("The trace does not name one matching app process.");const s=`SELECT ${a[0].upid}`;const c=await r("SELECT start_ts, end_ts FROM trace_bounds");const l=Number(c[0]?.start_ts);const h=(Number(c[0]?.end_ts)-l)/1e6;if(!Number.isFinite(h)||h<=0)throw new Error("Perfetto has no valid trace span.");const u=await r(`SELECT (s.ts-${l})/1e6 AS timeMs, s.dur/1e6 AS durationMs, t.tid, t.name, t.is_main_thread AS main
|
|
10
|
+
FROM sched s JOIN thread t USING(utid) WHERE t.upid IN (${s}) AND s.dur>0 ORDER BY s.ts LIMIT 200001`);if(u.length>2e5)throw new Error("The trace has too many CPU rows. Try a shorter run.");const d=await r(`SELECT (s.ts-${l})/1e6 AS timeMs, t.tid,
|
|
11
|
+
COALESCE(NULLIF(f.deobfuscated_name,''), NULLIF(f.name,''), '[unknown]') AS symbol, m.name AS image
|
|
12
|
+
FROM perf_sample s JOIN thread t USING(utid) JOIN stack_profile_callsite c ON c.id=s.callsite_id
|
|
13
|
+
JOIN stack_profile_frame f ON f.id=c.frame_id LEFT JOIN stack_profile_mapping m ON m.id=f.mapping
|
|
14
|
+
WHERE t.upid IN (${s}) ORDER BY s.ts LIMIT 100001`);if(d.length>1e5)throw new Error("The trace has too many stack rows. Try a shorter run.");const f=new Map;for(const w of d){const y=`${w.symbol}\0${w.image}`;const b=f.get(y)??{symbol:w.symbol.slice(0,180),image:w.image.slice(0,100),samples:0,selfMs:0};b.samples++;b.selfMs+=10;f.set(y,b)}const m=await r(`WITH tracks AS (
|
|
15
|
+
SELECT id FROM process_counter_track WHERE upid IN (${s}) AND name IN ('mem.rss.anon','mem.rss.file','mem.rss.shmem')
|
|
16
|
+
), times AS (SELECT DISTINCT ts FROM counter WHERE track_id IN (SELECT id FROM tracks))
|
|
17
|
+
SELECT ts, (SELECT SUM((SELECT value FROM counter c WHERE c.track_id=t.id AND c.ts<=times.ts ORDER BY c.ts DESC LIMIT 1)) FROM tracks t) AS bytes
|
|
18
|
+
FROM times ORDER BY ts`);const p=await r(`WITH frames AS (
|
|
19
|
+
SELECT surface_frame_token,
|
|
20
|
+
MAX(CASE WHEN jank_type NOT IN ('None','Unknown') THEN 1 ELSE 0 END) AS janky,
|
|
21
|
+
MAX(CASE WHEN present_type='Dropped Frame' THEN 1 ELSE 0 END) AS dropped
|
|
22
|
+
FROM actual_frame_timeline_slice WHERE upid IN (${s}) AND dur>=0 GROUP BY surface_frame_token
|
|
23
|
+
) SELECT COUNT(*) AS total, SUM(janky) AS janky, SUM(dropped) AS dropped FROM frames`);return{durationMs:h,sched:u.map(w=>({timeMs:Number(w.timeMs),durationMs:Number(w.durationMs),tid:Number(w.tid),name:w.name,main:w.main==="1"})),functions:[...f.values()].sort((w,y)=>y.selfMs-w.selfMs),symbols:d.map(w=>({timeMs:Number(w.timeMs),tid:Number(w.tid),symbol:w.symbol.slice(0,180)})),rss:m.map(w=>Number(w.bytes)),frames:Number(p[0]?.total)>0?{source:"frame-timeline",total:Number(p[0].total),janky:Number(p[0].janky),dropped:Number(p[0].dropped)}:null}}function Bn(e,t){const i=new Map;const n=e.sched.filter(u=>u.durationMs>0&&u.timeMs>=0&&u.timeMs<e.durationMs).map(u=>({...u,durationMs:Math.min(u.durationMs,e.durationMs-u.timeMs)}));for(const u of n){const d=i.get(u.tid)??{tid:u.tid,name:u.name.slice(0,100),cpuMs:0,main:u.main};d.cpuMs+=u.durationMs;i.set(u.tid,d)}const o=n.filter(u=>u.main).sort((u,d)=>u.timeMs-d.timeMs);const r=[];let a=[];let s=-1;const c=()=>{if(!a.length)return;const u=a[0].timeMs;const d=s-u;const f=a.reduce((m,p)=>m+p.durationMs,0);if(d>=t&&f/d>=.6){const m=new Map;for(const p of e.symbols??[])if(p.tid===a[0].tid&&p.timeMs>=u&&p.timeMs<s){m.set(p.symbol,(m.get(p.symbol)??0)+1)}r.push({startMs:u,durationMs:d,method:"sched-busy",sampleCount:a.length,topSymbols:[...m].sort((p,w)=>w[1]-p[1]).slice(0,3).map(p=>p[0])})}a=[]};for(const u of o){if(s>=0&&u.timeMs-s>10)c();a.push(u);s=u.timeMs+u.durationMs}c();r.sort((u,d)=>d.durationMs-u.durationMs);const l=Mt(e.rss,"perfetto-process-stats");const h=!e.frames?null:e.frames.total<30?{source:"frame-timeline",total:e.frames.total,minimum:30,status:"too-few-frames",janky:null,dropped:null}:{...e.frames,minimum:30,status:"captured"};return{sampleCount:n.length,mainThreadBusyPct:n.length?Math.min(100,o.reduce((u,d)=>u+d.durationMs,0)/e.durationMs*100):null,hotFunctions:e.functions.slice(0,10),hotThreads:[...i.values()].sort((u,d)=>d.cpuMs-u.cpuMs).slice(0,10),stalls:r.slice(0,10),totals:{hotFunctions:e.functions.length,stalls:r.length},memory:l,frames:h,coverage:{cpu:e.functions.length?"perf-samples":n.length?"sched":"unavailable",stalls:n.length?"sched-busy":"unavailable",memory:l?.source??"unavailable",frames:e.frames?"frame-timeline":"unavailable"}}}var jn=Zr(Xr);var Fn=e=>new Promise(t=>setTimeout(t,e));async function Un(e,t){const i=(await k(e,`pidof ${D(me(t))}`)).trim();if(!/^\d+$/.test(i))throw new Error("Open the chosen Android app first.");return Number(i)}function Yt(e,t){me(e);return`duration_ms: ${t}
|
|
24
|
+
buffers { size_kb: 32768 fill_policy: DISCARD }
|
|
25
|
+
write_into_file: true
|
|
26
|
+
file_write_period_ms: 250
|
|
27
|
+
max_file_size_bytes: 268435456
|
|
28
|
+
data_sources { config { name: "linux.ftrace" ftrace_config {
|
|
29
|
+
ftrace_events: "sched/sched_switch" ftrace_events: "sched/sched_waking"
|
|
30
|
+
ftrace_events: "power/cpu_frequency" ftrace_events: "power/cpu_idle"
|
|
31
|
+
atrace_categories: "gfx" atrace_categories: "view" atrace_categories: "am" atrace_categories: "dalvik"
|
|
32
|
+
atrace_apps: "${e}"
|
|
33
|
+
} } }
|
|
34
|
+
data_sources { config { name: "linux.process_stats" process_stats_config {
|
|
35
|
+
scan_all_processes_on_start: true proc_stats_poll_ms: 250
|
|
36
|
+
} } }
|
|
37
|
+
data_sources { config { name: "android.surfaceflinger.frametimeline" } }
|
|
38
|
+
data_sources { config { name: "linux.perf" perf_event_config {
|
|
39
|
+
timebase { counter: SW_CPU_CLOCK frequency: 100 timestamp_clock: PERF_CLOCK_MONOTONIC }
|
|
40
|
+
callstack_sampling { scope { target_cmdline: "${e}" } }
|
|
41
|
+
} } }
|
|
42
|
+
`}var Hn=(e,t,i,n)=>{const o=e.serial;const r=ae.basename(t);if(!/^[a-f0-9-]{36}$/.test(r))throw new Error("Bad trace folder ID.");const a=`/data/misc/perfetto-traces/buoy-${r}.perfetto-trace`;const s=ae.join(t,"capture.perfetto-trace");let c;let l;const h=new Promise((b,v)=>{c=b;l=v});void h.catch(()=>{});let u;let d=false;let f=false;let m=false;const p=new AbortController;const w=async()=>{if(u&&!m)await k(o,`kill -TERM ${u}`).catch(()=>{})};const y=(async()=>{const b=await Dn(process.env.BUOY_TRACE_PROCESSOR_CACHE||ae.join(ae.dirname(t),".tools"),p.signal);if(f)throw new Error("Trace was stopped before it could start.");const v=ae.join(t,"capture.pbtxt");await Pt.writeFile(v,Yt(e.appId,i),{mode:384});try{try{const A=jn("adb",["-s",o,"shell","perfetto","--background-wait","--txt","-c","-","-o",a],{timeout:35e3,maxBuffer:65536});A.child.stdin.on("error",()=>{});A.child.stdin.end(Yt(e.appId,i));const{stdout:C}=await A;u=Number(/^\s*(\d+)\s*$/m.exec(C)?.[1])}catch(A){u=Number(/^\s*(\d+)\s*$/m.exec(A.stdout??"")?.[1])||void 0;throw new Error(`Perfetto could not start: ${String(A.stderr??"start timed out after 35 seconds").slice(-1e3)}`)}if(!u||!Number.isSafeInteger(u))throw new Error("Perfetto did not return its process ID.");if(d||f)await w();c();const g=Date.now()+i+15e3;while(true){const A=await k(o,`ps -p ${u} -o NAME 2>&1; echo BUOY_PS_EXIT:$?`);const C=A.trim().split(/\r?\n/).map(F=>F.trim());const O=C.pop();const M=C.shift();if(M!=="NAME"||O!=="BUOY_PS_EXIT:0"&&!(O==="BUOY_PS_EXIT:1"&&!C.length)){throw new Error("Android could not read the trace process.")}if(!C.includes("perfetto"))break;if(Date.now()>g)throw new Error("Perfetto took too long to stop.");await Fn(250)}m=true;if(f)throw new Error("Trace was stopped before it could finish.");const _=Number((await k(o,`stat -c %s ${D(a)}`)).trim());if(!Number.isFinite(_)||_<=0||_>256*1024*1024)throw new Error("Perfetto trace is empty or too large.");await K(o,["pull",a,s],3e4);await Pt.chmod(s,384);const E=await Ln(b,s,e.pid,e.appId,p.signal);const S=Bn(E,n);const I=["UI busy spans use scheduler CPU time. Sleeping or blocked hangs are not counted."];if(!E.functions.length)I.push("No call stacks were captured. Hot threads show scheduler CPU time.");else I.push("Function self time uses 100 Hz samples. Some symbols may be missing.");if(!S.memory)I.push("No RSS rows were found. Memory use is unknown.");if(S.frames?.status==="too-few-frames")I.push(`Too few frames to judge: ${S.frames.total}. Need at least 30 app frame rows.`);if(!E.frames)I.push("No app frame rows were found. Frame counts are unknown.");return{samples:[],hangs:[],durationMs:E.durationMs,warnings:I,memory:S.memory,summary:S}}finally{await w();await k(o,`rm -f ${D(a)}`).catch(()=>{})}})().catch(b=>{l(b);throw b});void y.catch(()=>{});return{ready:h,result:y,stop(){d=true;void w()},cancel(){f=true;p.abort();void w()}}};function Yn(e,t,i,n=[]){const o=e.filter(d=>d.running&&d.timeMs>=0&&d.timeMs<t);const r=new Map;for(const d of o){const f=d.frames[0];if(!f)continue;const m=`${f.image}\0${f.symbol}`;const p=r.get(m)??{...f,selfMs:0,samples:0};p.selfMs+=Math.min(d.weightMs,t-d.timeMs);p.samples++;r.set(m,p)}const a=[...r.values()].sort((d,f)=>f.selfMs-d.selfMs);const s=new Map;for(const d of o.filter(f=>f.main)){const f=s.get(d.tid)??[];f.push(d);s.set(d.tid,f)}const c=[];let l=0;for(const d of s.values()){d.sort((w,y)=>w.timeMs-y.timeMs);let f=[];let m=-1;const p=()=>{if(!f.length)return;const w=f[0].timeMs;const y=Math.min(m,t)-w;let b=0;let v=-1;for(const g of f){const _=Math.min(t,g.timeMs+Math.min(g.weightMs,10));b+=Math.max(0,_-Math.max(v,g.timeMs));v=Math.max(v,_)}if(y>=i&&f.length>=Math.ceil(i/10)&&b/y>=.6){const g=new Map;for(const _ of f)for(const E of _.frames.slice(0,4))g.set(E.symbol,(g.get(E.symbol)??0)+_.weightMs);c.push({startMs:w,durationMs:y,method:"sample-density",sampleCount:f.length,topSymbols:[...g].sort((_,E)=>E[1]-_[1]).slice(0,3).map(_=>_[0])})}f=[]};for(const w of d){if(m>=0&&w.timeMs-m>10)p();l+=Math.max(0,Math.min(w.timeMs+w.weightMs,t)-Math.max(w.timeMs,m));f.push(w);m=Math.max(m,w.timeMs+w.weightMs)}p()}const h=n.filter(d=>d.startMs>=0&&d.startMs<t).map(d=>({...d,durationMs:Math.min(d.durationMs,t-d.startMs)}));const u=[...h,...c.filter(d=>!h.some(f=>d.startMs<f.startMs+f.durationMs&&f.startMs<d.startMs+d.durationMs))].sort((d,f)=>f.durationMs-d.durationMs);return{sampleCount:o.length,mainThreadBusyPct:o.length?Math.min(100,l/t*100):null,hotFunctions:a.slice(0,10),stalls:u.slice(0,10),totals:{hotFunctions:a.length,stalls:u.length},coverage:{cpu:o.length?"captured":"unavailable",stalls:h.length?"os-hang":o.length?"sample-density":"unavailable",memory:"unavailable",frames:"unavailable"}}}var xe=new Set;var Wt=e=>{if(!e||!Number.isInteger(e)||e<1)return false;try{process.kill(e,0);return true}catch(t){return t.code!=="ESRCH"}};var we=e=>e.state==="ready"||e.state==="failed";var Wn=class{constructor(e=process.env.BUOY_NATIVE_PROFILE_DIR||Z.join(Vr.homedir(),".buoy","native-profiles"),t=(i,...n)=>(i.platform==="android"?Hn:Kr)(i,...n)){this.root=e;this.factory=t;this.disposed=false;this.starts=new Set;this.captures=new Set;this.writes=Promise.resolve();this.runs=new Map;xe.add(this)}start(e,t={}){if(this.disposed)return Promise.reject(new Error("This trace host is closed."));const i=this.startRun(e,t);this.starts.add(i);void i.finally(()=>this.starts.delete(i)).catch(()=>{});return i}async startRun(e,t){const i=t.durationMs??6e4;const n=t.thresholdMs??250;if(!Number.isInteger(i)||i<1e3||i>6e4||!Number.isInteger(n)||n<100||n>5e3)throw new Error("Trace time or stall limit is out of range.");await N.mkdir(this.root,{recursive:true,mode:448});const o=Z.join(this.root,`lock-${Jr("sha256").update(e.serial??e.udid??"").digest("hex").slice(0,24)}`);const r=()=>N.open(o,"wx",384);let a;try{a=await r()}catch{const f=`${o}.recover`;try{await N.mkdir(f);try{const m=JSON.parse(await N.readFile(o,"utf8"));if(Wt(m.ownerPid)||Wt(m.recorderPid))throw new Error("live");await N.unlink(o);a=await r()}finally{await N.rmdir(f)}}catch{throw new Error("A trace already owns this sim. Stop it in the host that started it.")}}let s=false;const c=async()=>{if(s)return;s=true;await N.unlink(o).catch(()=>{})};const l=$t();const h=Z.join(this.root,l);let u;let d;try{await a.writeFile(JSON.stringify({ownerPid:process.pid,profileId:l}));await a.close();await N.mkdir(h,{mode:448});const f={schemaVersion:1,profileId:l,state:"starting",target:{...e},startedAt:new Date().toISOString(),durationMs:0,thresholdMs:n,attachRetries:0,memory:null,frames:null,hotThreads:[],sampleCount:0,mainThreadBusyPct:null,hotFunctions:[],stalls:[],totals:{hotFunctions:0,stalls:0},coverage:{cpu:"unavailable",stalls:"unavailable",memory:"unavailable",frames:"unavailable"},warnings:[],artifacts:{tracePath:Z.join(h,e.platform==="android"?"capture.perfetto-trace":"capture.trace"),reportPath:Z.join(h,"report.json")}};d=f;await this.save(f);if(this.disposed)throw new Error("This trace host is closed.");u=this.factory(e,h,i,n,p=>{Gr(o,JSON.stringify({ownerPid:process.pid,profileId:l,recorderPid:p}),{mode:384})});this.captures.add(u);await N.writeFile(o,JSON.stringify({ownerPid:process.pid,profileId:l,recorderPid:u.pid}),{mode:384});const m=u.result.then(p=>{f.state="processing";f.durationMs=p.durationMs;Object.assign(f,p.summary??Yn(p.samples,p.durationMs,n,p.hangs));f.memory=p.memory??null;f.coverage.memory=f.memory?.source??"unavailable";f.attachRetries=u?.attachRetries??0;f.warnings=p.warnings;f.state=f.sampleCount?"ready":"failed";if(!f.sampleCount)f.error="No running CPU samples were captured. This run cannot rule out a stall.";return f}).catch(p=>{f.attachRetries=u?.attachRetries??0;f.state="failed";f.error=String(p instanceof Error?p.message:p).slice(0,1200);return f}).then(async p=>{await this.save(p);return p}).finally(async()=>{this.captures.delete(u);await c();await this.prune()});void m.catch(()=>{});this.runs.set(l,{report:f,capture:u,done:m});try{await u.ready}catch{await m;throw new Error(`${f.error||"Trace did not start."} (profileId: ${l}; attachRetries: ${f.attachRetries})`)}f.attachRetries=u.attachRetries??0;if(!we(f)){f.state="recording";await this.save(f)}return structuredClone(f)}catch(f){u?.cancel();await u?.result.catch(()=>{});if(u)this.captures.delete(u);await a.close().catch(()=>{});await c();if(d&&!this.runs.has(l)){d.state="failed";d.error=String(f instanceof Error?f.message:f).slice(0,1200);await this.save(d).catch(()=>{})}throw f}}async stop(e){const t=this.runs.get(e);if(!t){const i=await this.get(e);if(!we(i))throw new Error("Stop this trace in the host that started it.");return i}if(!we(t.report)&&t.report.state!=="stopping"){t.report.state="stopping";t.capture.stop()}return structuredClone(await t.done)}async wait(e){const t=this.runs.get(e);if(!t)throw new Error("This host does not own that trace.");return structuredClone(await t.done)}async get(e){if(!/^[a-f0-9-]{36}$/.test(e))throw new Error("Bad profile ID.");const t=this.runs.get(e);if(t)return structuredClone(t.report);const i=Z.join(this.root,e,"report.json");const n=await N.stat(i).catch(()=>null);if(!n||n.size>16384)throw new Error("That profile was not found or is too large.");const o=JSON.parse(await N.readFile(i,"utf8"));if(o.schemaVersion!==1||o.profileId!==e||!o.target?.deviceId)throw new Error("That profile is not valid.");return o}async dispose(){this.disposed=true;this.captures.forEach(e=>e.cancel());await Promise.allSettled([...this.starts]);await Promise.allSettled([...this.runs.values()].map(e=>e.done));xe.delete(this)}async save(e){let t=JSON.stringify(e);if(Buffer.byteLength(t)>16384&&(e.hotFunctions.length||e.stalls.length)){e.warnings.push("Some rows were left out to fit the report size cap.");while(Buffer.byteLength(t)>16384&&(e.hotFunctions.length||e.stalls.length)){if(JSON.stringify(e.hotFunctions).length>JSON.stringify(e.stalls).length)e.hotFunctions.pop();else e.stalls.pop();t=JSON.stringify(e)}}if(Buffer.byteLength(t)>16384)throw new Error("Profile report is too large.");const i=`${e.artifacts.reportPath}.${$t()}.tmp`;const n=this.writes.then(async()=>{try{await N.writeFile(i,t,{mode:384});await N.rename(i,e.artifacts.reportPath)}finally{await N.rm(i,{force:true})}});this.writes=n.catch(()=>{});await n}async prune(){const e=[];for(const t of await N.readdir(this.root)){if(!/^[a-f0-9-]{36}$/.test(t))continue;try{const i=await this.get(t);if(we(i))e.push({id:t,at:Date.parse(i.startedAt)})}catch{}}e.sort((t,i)=>i.at-t.at);for(const[t,i]of e.entries())if(t>=10||Date.now()-i.at>864e5){await N.rm(Z.join(this.root,i.id),{recursive:true,force:true});this.runs.delete(i.id)}}};async function qn(){await Promise.allSettled([...xe].map(e=>e.dispose()))}import{z as H}from"zod";var qt=H.object({action:H.enum(["start","stop","record"]),deviceId:H.string().optional(),udid:H.string().uuid().optional(),serial:H.string().regex(/^[A-Za-z0-9_.:-]+$/).optional(),profileId:H.string().uuid().optional(),durationMs:H.number().int().min(1e3).max(6e4).optional(),thresholdMs:H.number().int().min(100).max(5e3).optional()}).strict();var zt=H.object({profileId:H.string().uuid()}).strict();import{execFile as zn}from"child_process";import{promisify as Kn}from"util";var Ne=nt(_e());import{execFile as Gn}from"child_process";import{promisify as Vn}from"util";import{promises as P}from"fs";import ce from"os";import T from"path";import{randomUUID as Jn}from"crypto";import{execFile as Xn}from"child_process";import{promisify as Zn}from"util";import{promises as Y}from"fs";import Kt from"os";import U from"path";import{randomUUID as Gt}from"crypto";var le=Zn(Xn);var ge=(e,t,i)=>Math.min(Math.max(e,t),i);function Vt(e){const t=e;if(t?.code==="ENOENT"){return"Xcode command-line tools (xcrun) not found. Install Xcode to capture the iOS Simulator."}const i=String(t?.stderr||t?.message||e);if(/No devices are booted|Unable to find device|current device/i.test(i)){return"No booted iOS Simulator found. Boot the Simulator with your app running, then try again."}return i.trim()||"Failed to capture the iOS Simulator."}function Qn(e){const t=[137,80,78,71,13,10,26,10];if(e.length<24||!t.every((i,n)=>e[n]===i))return null;return{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}var es="@react_buoy_sync_device_id";async function ye(e){try{return await Y.readdir(e)}catch{return[]}}async function ts(e){const t=[];for(const i of await ye(e))t.push(...await Jt(U.join(e,i)));return t}async function Jt(e){const t=[];const i=U.join(e,"Library","WebKit");for(const n of await ye(i)){const o=U.join(i,n,"WebsiteData","Default");for(const r of await ye(o)){for(const a of await ye(U.join(o,r))){const s=U.join(o,r,a,"LocalStorage","localstorage.sqlite3");try{await Y.access(s);const{stdout:c}=await le("sqlite3",["-readonly",s,`SELECT hex(value) FROM ItemTable WHERE key='${es}' LIMIT 1;`],{timeout:1500});const l=c.trim();if(l)t.push(Buffer.from(l,"hex").toString("utf16le").trim())}catch{}}}}return t}var Xt=new Map;var is=3e4;async function Zt(e){if(process.platform!=="darwin")return{ok:false,error:"Simulator lookup needs macOS."};let t=[];try{const{stdout:n}=await le("xcrun",["simctl","list","devices","booted","-j"],{timeout:1e4});const o=JSON.parse(n).devices;t=Object.entries(o).filter(([r])=>/iOS/.test(r)).flatMap(([,r])=>r.filter(a=>a.state==="Booted"))}catch(n){return{ok:false,error:Vt(n)}}if(t.length===0)return{ok:false,error:"No iOS Simulator is booted."};const i=e?.split("-").pop();if(i){for(const n of t){if(!n.dataPath)continue;const o=U.join(n.dataPath,"Containers","Data","Application");let r=[];try{r=await Y.readdir(o)}catch{continue}for(const a of r){try{const s=(await Y.readFile(U.join(o,a,"Documents","react-buoy","@react_buoy_sync_device_id.json"),"utf8")).trim();if(s===i)return{ok:true,udid:n.udid,how:`matched ${e} to "${n.name}"`}}catch{}}}}if(i&&e){const n=t.map(c=>c.udid).sort().join(",");const o=Xt.get(e);const r=o?.booted===n&&(o.udid!==null||Date.now()-o.at<is);const a=r&&o?.udid?t.find(c=>c.udid===o.udid):void 0;if(a)return{ok:true,udid:a.udid,how:`matched ${e} to "${a.name}" (WebView storage)`};const s=[];if(!r){const c=await Promise.all(t.map(l=>l.dataPath?ts(U.join(l.dataPath,"Containers","Data","Application")):Promise.resolve([])));c.forEach((l,h)=>{if(l.includes(i))s.push(t[h])});Xt.set(e,{booted:n,udid:s.length===1?s[0].udid:null,at:Date.now()})}if(s.length===1){return{ok:true,udid:s[0].udid,how:`matched ${e} to "${s[0].name}" (WebView storage)`}}if(s.length>1){return{ok:false,error:`${s.length} booted simulators (${s.map(c=>c.name).join(", ")}) hold ${e}. Pass the udid, or shut one down.`}}}if(t.length===1)return{ok:true,udid:t[0].udid,how:`only booted simulator "${t[0].name}"`};return{ok:false,error:`${t.length} iOS Simulators are booted (${t.map(n=>n.name).join(", ")}) and none could be matched to ${e??"this device"}. Shut down the others, or capture from a single booted simulator.`}}async function rs(e){const t=new Map;if(process.platform!=="darwin"||e.length===0)return t;let i=[];try{const{stdout:o}=await le("xcrun",["simctl","list","devices","booted","-j"],{timeout:1e4});const r=JSON.parse(o).devices;i=Object.entries(r).filter(([a])=>/iOS/.test(a)).flatMap(([,a])=>a.filter(s=>s.state==="Booted"))}catch{return t}const n=new Map;for(const o of i){if(!o.dataPath)continue;const r=U.join(o.dataPath,"Containers","Data","Application");let a=[];try{a=await Y.readdir(r)}catch{continue}for(const s of a){try{const c=(await Y.readFile(U.join(r,s,"Documents","react-buoy","@react_buoy_sync_device_id.json"),"utf8")).trim();if(c)n.set(c,{name:o.name,udid:o.udid})}catch{}}}for(const o of e){const r=n.get(o.split("-").pop()??"");if(r)t.set(o,r)}return t}async function Qt(e="booted"){if(process.platform!=="darwin"){return{ok:false,error:"Screenshotting a component requires macOS with Xcode's iOS Simulator (uses `xcrun simctl`)."}}const t=U.join(Kt.tmpdir(),`buoy-mcp-sim-${Gt()}.png`);try{await le("xcrun",["simctl","io",e,"screenshot","--type=png",t],{timeout:15e3})}catch(i){return{ok:false,error:Vt(i)}}try{const i=await Y.readFile(t);const n=Qn(i);if(!n)return{ok:false,error:"Captured file wasn't a valid PNG."};return{ok:true,srcPath:t,width:n.width,height:n.height}}catch(i){return{ok:false,error:`Failed to read screenshot: ${i.message}`}}}async function ns(e,t,i){const n=ge(Math.round(i.x),0,Math.max(0,t.width-1));const o=ge(Math.round(i.y),0,Math.max(0,t.height-1));const r=ge(Math.round(i.width),1,t.width-n);const a=ge(Math.round(i.height),1,t.height-o);const s=U.join(Kt.tmpdir(),`buoy-mcp-crop-${Gt()}.png`);try{await le("sips",["-c",String(a),String(r),"--cropOffset",String(o),String(n),e,"--out",s],{timeout:1e4});const c=await Y.readFile(s);return{ok:true,buffer:c,width:r,height:a}}catch(c){const l=c;return{ok:false,error:`Crop failed: ${l?.stderr||l?.message||c}`}}finally{await Y.unlink(s).catch(()=>{});await Y.unlink(e).catch(()=>{})}}var ss=nt(_e());import{execFile as os}from"child_process";import{promisify as as}from"util";import{promises as Q}from"fs";import Le from"os";import Be from"path";import{randomUUID as je}from"crypto";var cs=as(os);async function j(e,t=3e4){const i=Be.join(Le.tmpdir(),`buoy-devicectl-${je()}.json`);try{await cs("xcrun",["devicectl",...e,"--quiet","--json-output",i],{timeout:t,maxBuffer:16*1024*1024});return JSON.parse(await Q.readFile(i,"utf8"))}catch(n){const o=n;let r=(o.stderr||o.stdout||o.message||"").trim();try{const a=JSON.parse(await Q.readFile(i,"utf8"));r=a.error?.userInfo?.NSLocalizedDescription?.string??r}catch{}if(/locked/i.test(r))throw new Error("The iPhone is locked. Unlock it and try again.");throw new Error(`devicectl ${e.slice(0,2).join(" ")} failed: ${r.split("\n").slice(-3).join(" ").slice(0,300)}`)}finally{await Q.unlink(i).catch(()=>void 0)}}async function ls(){if(process.platform!=="darwin")return[];return hs(await j(["list","devices"],15e3).catch(()=>null))}function hs(e){const t=e;return(t?.result?.devices??[]).filter(i=>i.hardwareProperties?.reality==="physical"&&i.hardwareProperties?.platform==="iOS"&&i.connectionProperties?.pairingState==="paired"&&i.connectionProperties?.tunnelState!=="unavailable").map(i=>({udid:i.hardwareProperties.udid,name:i.deviceProperties?.name??"iPhone"})).filter(i=>!!i.udid)}async function ei(e){const t=await j(["device","info","apps","--device",e]);return(t.result?.apps??[]).filter(i=>i.bundleIdentifier&&!i.bundleIdentifier.startsWith("com.apple.")&&i.builtByDeveloper!==false).map(i=>({bundleId:i.bundleIdentifier,name:i.name??i.bundleIdentifier,path:i.url?.replace(/^file:\/\//,"")}))}async function ti(e,t,i){const n=await j(["device","info","files","--device",e,"--domain-type","appDataContainer","--domain-identifier",t],2e4);const o=new Set((n.result?.files??[]).map(a=>a.relativePath??a.name??""));if(!o.has(i))return null;const r=Be.join(Le.tmpdir(),`buoy-devfile-${je()}`);try{await j(["device","copy","from","--device",e,"--domain-type","appDataContainer","--domain-identifier",t,"--source",i,"--destination",r],2e4);return await Q.readFile(r)}finally{await Q.rm(r,{recursive:true,force:true}).catch(()=>void 0)}}async function ii(e,t){const i=await ti(e,t,"Documents/react-buoy/@react_buoy_sync_device_id.json").catch(()=>null);const n=i?.toString("utf8").trim().replace(/^"|"$/g,"")??"";return/^[\w-]{4,64}$/.test(n)?n:null}async function ri(e){const t=Be.join(Le.tmpdir(),`buoy-phone-shot-${je()}.png`);try{await j(["device","capture","screenshot","--device",e,"--destination",t],3e4);return await Q.readFile(t)}finally{await Q.unlink(t).catch(()=>void 0)}}var us="The picture is all black. If the app isn't really showing black, the iPhone's own screen is off: it is asleep, or the Mac shows it in iPhone Mirroring (the phone stays locked then). Quit iPhone Mirroring, unlock the phone and keep its screen on, then try again.";function ds(e){try{const t=ss.PNG.sync.read(e);const i=Math.max(1,Math.floor(t.width*t.height/4e3));for(let n=0;n<t.width*t.height;n+=i){const o=n*4;if(t.data[o]>12||t.data[o+1]>12||t.data[o+2]>12)return false}return true}catch{return false}}async function Fe(e,t,i={}){await j(["device","process","launch","--device",e,...i.terminateExisting?["--terminate-existing"]:[],...i.url?["--payload-url",i.url]:[],t])}async function fs(e,t){await j(["device","process","openURL","--device",e,t])}async function ni(e,t){const i=await ei(e);const n=i.find(a=>a.bundleId===t)?.path;const o=await j(["device","info","processes","--device",e]);const r=(o.result?.runningProcesses??[]).find(a=>n&&decodeURIComponent(a.executable??"").replace(/^file:\/\//,"").startsWith(n.replace(/\/$/,"")));if(!r?.processIdentifier)return false;await j(["device","process","terminate","--device",e,"--pid",String(r.processIdentifier)]);return true}async function ps(e,t){await j(["device","uninstall","app","--device",e,t],12e4)}async function ms(e,t){await j(["device","install","app","--device",e,t],3e5)}async function ws(e,t,i,n){await j(["device","copy","to","--device",e,"--domain-type","appDataContainer","--domain-identifier",t,"--source",i,"--destination",n],3e4)}function gs(e,t,i){return["devicectl","device","capture","screen-record","--device",e,"--destination",t,"--duration",String(i)]}var si=new Map;async function ys(e){const t=si.get(e);if(t)return t;const i=await j(["list","devices"]);const n=i.result?.devices?.find(o=>o.hardwareProperties?.udid===e)?.identifier;if(!n)throw new Error("CoreDevice doesn't list this iPhone right now. Check that it is connected and unlocked.");si.set(e,n);return n}import{execFile as bs}from"child_process";import{promisify as vs}from"util";import{promises as Ue}from"fs";import re from"path";import{WebSocket as _s}from"ws";var Es=vs(bs);var oi=/^(@buoy\/session:|buoy-gg-license|buoy-gg-device-fingerprint$|@react_buoy_sync_device_id$|@react_buoy_install)/;async function He(e){return Ue.readdir(e).catch(()=>[])}async function Ye(e){const t=[];const i=re.join(e,"Library","WebKit");for(const n of await He(i)){const o=re.join(i,n,"WebsiteData","Default");for(const r of await He(o)){for(const a of await He(re.join(o,r))){const s=re.join(o,r,a);const c=re.join(s,"LocalStorage","localstorage.sqlite3");if(!await Ue.access(c).then(()=>true,()=>false))continue;const l=await Ue.readFile(re.join(s,"origin")).then(h=>h.toString("hex"),()=>"");if(l)t.push({db:c,origin:l})}}}return t}var ai=(e,t,i=true)=>Es("sqlite3",[...i?["-readonly"]:[],e,t],{timeout:5e3}).then(n=>n.stdout);async function ks(e){const t=[];for(const{db:i,origin:n}of await Ye(e)){const o=await ai(i,"SELECT key, hex(value) FROM ItemTable;");for(const r of o.split("\n")){const a=r.lastIndexOf("|");if(a<0)continue;const s=r.slice(0,a);if(oi.test(s))t.push({key:s,value:r.slice(a+1),origin:n})}}return t}async function Ss(e,t){if(!t.length)return false;const i=await Ye(e);const n=new Set(t.map(o=>o.origin??""));for(const o of n){const r=o?i.find(s=>s.origin===o):void 0;if(!r)return false;const a=t.filter(s=>(s.origin??"")===o&&/^[0-9A-F]*$/i.test(s.value)).map(s=>`('${s.key.replace(/'/g,"''")}', X'${s.value}')`).join(",");if(a)await ai(r.db,`INSERT OR REPLACE INTO ItemTable (key, value) VALUES ${a};`,false)}return true}async function Ts(e,t=15e3){const i=Date.now()+t;while(Date.now()<i){if((await Ye(e)).length)return true;await new Promise(n=>setTimeout(n,500))}return false}async function As(e,t,i){const n=(await k(e,`pidof ${t}`).catch(()=>"")).trim().split(/\s+/)[0];if(!n)throw new Error("the app is not running");const o=(await K(e,["forward","tcp:0",`localabstract:webview_devtools_remote_${n}`])).trim();try{const r=await(await fetch(`http://127.0.0.1:${o}/json`)).json();const a=r.find(s=>s.type==="page"&&s.webSocketDebuggerUrl);if(!a?.webSocketDebuggerUrl)throw new Error("no debuggable WebView page (needs a debug build)");return await new Promise((s,c)=>{const l=new _s(a.webSocketDebuggerUrl);const h=setTimeout(()=>{l.close();c(new Error("the WebView did not answer"))},5e3);l.on("open",()=>l.send(JSON.stringify({id:1,method:"Runtime.evaluate",params:{expression:i,returnByValue:true}})));l.on("message",u=>{const d=JSON.parse(String(u));if(d.id!==1)return;clearTimeout(h);l.close();if(d.result?.exceptionDetails)c(new Error("the WebView script failed"));else s(d.result?.result?.value)});l.on("error",u=>{clearTimeout(h);c(u)})})}finally{await K(e,["forward","--remove",`tcp:${o}`]).catch(()=>void 0)}}async function ci(e,t,i,n=15e3){const o=Date.now()+n;let r;while(Date.now()<o){try{return await As(e,t,i)}catch(a){r=a;await new Promise(s=>setTimeout(s,700))}}throw r instanceof Error?r:new Error("the WebView did not open")}async function Os(e,t){const i=await k(e,`run-as ${t} sh -c 'ls app_webview/Default/"Local Storage"/leveldb 2>/dev/null'`).catch(()=>"");return/CURRENT|\.log/.test(i)}async function Ms(e,t){if(!(await k(e,`pidof ${t}`).catch(()=>"")).trim()){await k(e,`monkey -p ${t} -c android.intent.category.LAUNCHER 1`)}const i=await ci(e,t,`(() => { const re = ${oi}; const out = []; for (let i = 0; i < localStorage.length; i++) { const k = localStorage.key(i); if (k && re.test(k)) out.push({ key: k, value: localStorage.getItem(k) }); } return out; })()`);return Array.isArray(i)?i.filter(n=>typeof n.key==="string"&&typeof n.value==="string"):[]}async function Is(e,t,i){await ci(e,t,`(() => { for (const r of ${JSON.stringify(i)}) localStorage.setItem(r.key, r.value); setTimeout(() => location.reload(), 50); return true; })()`)}var Cs=/^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i;var $s=/^[0-9A-F]{8}-[0-9A-F]{16}$/i;var Ps=/^[0-9A-F]{40}$/i;function li(e){return typeof e==="string"&&(Cs.test(e)||$s.test(e)||Ps.test(e))}function hi(e,t="udid"){if(li(e))return e;const i=typeof e==="string"?e.slice(0,60):typeof e;throw new Error(`"${i}" is not a ${t}. A simulator udid looks like 1B2C3D4E-5F60-4718-9A2B-3C4D5E6F7A8B (xcrun simctl list devices).`)}var ui=Vn(Gn);var G=new Map;function be(e){return G.get(e)}async function q(e,t,i=2e4,n){try{const{stdout:o}=await ui(e,t,{timeout:i,maxBuffer:16*1024*1024,env:n?{...process.env,...n}:void 0});return o}catch(o){const r=o;if(r.code==="ENOENT")throw new Error(e==="xcrun"?"This needs macOS with Xcode (xcrun simctl).":`${e} is not installed.`);throw new Error(`${e} ${t.slice(0,2).join(" ")} failed: ${(r.stderr||r.message||"").trim().slice(0,300)}`)}}var L=(e,t)=>q("xcrun",["simctl",...e],t);async function We(e,t){const i=await Si(e,t).catch(()=>({}));return q("xcrun",["simctl","launch",e,t],void 0,i)}var he=e=>e.split("-").pop()??"";async function qe(e,t){if(!t){const i=await ze(e,["rn"]);return i.length?i:ze(e,["webview"])}return ze(e,t)}async function ze(e,t){let i=[];try{const o=JSON.parse(await L(["list","devices","booted","-j"])).devices;i=Object.entries(o).filter(([r])=>/iOS/.test(r)).flatMap(([,r])=>r.filter(a=>a.state==="Booted"))}catch{return[]}const n=await Promise.all(i.map(o=>di(e,o.udid,o.dataPath,t)));return i.flatMap((o,r)=>n[r]?[{udid:o.udid,name:o.name,found:n[r]}]:[])}function Ke(e,t){return new Error(`${t.length} booted simulators hold "${e}" (one is probably a clone): ${t.map(i=>`${i.name} (${i.udid})`).join(", ")}. Pass udid to choose, or shut the extra one down.`)}async function di(e,t,i,n){const o=he(e);if(!o)return null;if(!i){try{const c=JSON.parse(await L(["list","devices","-j"])).devices;i=Object.values(c).flat().find(l=>l.udid===t)?.dataPath}catch{return null}}if(!i)return null;const r=T.join(i,"Containers","Data","Application");let a=[];try{a=await P.readdir(r)}catch{return null}const s=await Promise.all(a.map(c=>Ge(T.join(r,c),o,n)));for(const[c,l]of a.entries()){const h=T.join(r,l);if(!s[c])continue;let u=null;try{u=(await q("plutil",["-extract","MCMMetadataIdentifier","raw","-o","-",T.join(h,".com.apple.mobile_container_manager.metadata.plist")])).trim()||null}catch{}return{container:h,bundleId:u,kind:s[c]}}return null}async function Ge(e,t,i=["rn","webview"]){if(i.includes("rn")){try{if((await P.readFile(T.join(e,"Documents","react-buoy","@react_buoy_sync_device_id.json"),"utf8")).trim()===t)return"rn"}catch{}}if(!i.includes("webview"))return null;return(await Jt(e).catch(()=>[])).includes(t)?"webview":null}async function Rs(e,t){const i=he(e);const n=t.serial?[t.serial]:await Bt();if(n.length===0)throw new Error("No Android device or emulator is connected to adb.");for(const o of n){const r=await mn(o).catch(()=>null);const a=await gn(o).catch(()=>[]);const s=[t.bundleId,r,...a].filter((c,l,h)=>!!c&&h.indexOf(c)===l).slice(0,80);for(const c of s){if(await jt(o,c).catch(()=>null)===i)return{platform:"android",serial:o,packageName:c}}}return null}async function Ds(e){const t=he(e);for(const i of await ls()){for(const n of await ei(i.udid).catch(()=>[])){if(await ii(i.udid,n.bundleId)===t)return{platform:"ios-device",udid:i.udid,name:i.name,bundleId:n.bundleId}}}return null}var Ve=new Map;function ue(e,t,i={}){if(i.udid||i.serial||i.bundleId)return fi(e,t,i);const n=`${e}|${t??""}`;const o=Ve.get(n);if(o)return o;const r=fi(e,t,i).finally(()=>Ve.delete(n));Ve.set(n,r);return r}async function fi(e,t,i={}){if(e==="All")throw new Error('Device control acts on one app. Pass its deviceId, not "All".');const n=G.get(e);if(n&&!i.udid&&!i.serial&&await mi(e,n)){if(n.platform==="ios"&&!n.picked){const l=await qe(e,n.idKind?[n.idKind]:void 0);if(l.length>1)throw Ke(e,l);if(!l.some(h=>h.udid===n.udid)){G.delete(e);throw new Error(`The sim no longer holds "${e}". Try the host scan again.`)}}return n}if(n)G.delete(e);if(t==="android"){const l=await Rs(e,i);if(!l){throw new Error(`Could not prove which Android device and app run "${e}". Buoy checks the app's install id with run-as, which needs a debug build of the app on a device adb can reach${i.serial?` (looked on ${i.serial})`:""}.`)}G.set(e,l);return l}if(t&&t!=="ios")throw new Error(`Device control works on iOS Simulators and Android. This app runs on "${t}".`);if(process.platform!=="darwin")throw new Error("iOS control needs macOS with Xcode.");let o=i.udid===void 0?void 0:hi(i.udid,"simulator udid");let r=o??"";let a=null;if(!o){const l=await qe(e);a=l.length===1?l[0]:null;if(l.length>1)throw Ke(e,l);const h=l.length===1?{ok:true,udid:l[0].udid,how:`matched ${e} to "${l[0].name}"`}:await Zt(e);if(!h.ok||!/^matched /.test(h.how)){const u=await Ds(e).catch(()=>null);if(u){G.set(e,u);return u}throw new Error(`Could not find which simulator or iPhone runs "${e}": no booted simulator holds its install id, and no connected iPhone has it (an iPhone must be unlocked, paired, and run a development build).`)}o=h.udid;r=/"([^"]+)"/.exec(h.how)?.[1]??o}const s=a?.udid===o?await Ge(a.found.container,he(e),[a.found.kind])?a.found:null:await di(e,o);if(!s)throw new Error(`Simulator ${o} does not hold "${e}".`);const c={platform:"ios",udid:o,name:r,bundleId:i.bundleId??s?.bundleId??null,container:s?.container??null,picked:!!i.udid,idKind:s.kind};G.set(e,c);return c}async function pi(e,t){if(t.platform==="ios"&&!t.picked){const i=await qe(e);if(i.length>1)throw Ke(e,i)}if(await mi(e,t))return t;G.delete(e);return ue(e,t.platform==="android"?"android":"ios",t.platform==="ios"&&t.picked?{udid:t.udid}:{})}async function mi(e,t){const i=he(e);if(t.platform==="android")return await jt(t.serial,t.packageName).catch(()=>null)===i;if(t.platform==="ios-device")return await ii(t.udid,t.bundleId).catch(()=>null)===i;if(t.container)return!!await Ge(t.container,i);const n=await Zt(e);return n.ok&&n.udid===t.udid&&/^matched /.test(n.how)}function Je(e){if(e.platform==="android")return me(e.packageName);if(e.platform==="ios-device")return e.bundleId;if(!e.bundleId)throw new Error("Could not tell which app this is on the simulator (its container has no bundle metadata).");return e.bundleId}async function xs(e){if(e.platform==="android")return kn(e.serial);if(e.platform==="ios-device")return ri(e.udid);const t=await Qt(e.udid);if(!t.ok||!t.srcPath)throw new Error(t.error??"Screenshot failed.");try{return await P.readFile(t.srcPath)}finally{await P.unlink(t.srcPath).catch(()=>{})}}async function Ns(e,t=1280){if(process.platform==="darwin"){const o=T.join(ce.tmpdir(),`buoy-mcp-img-${Jn()}.png`);const r=o.replace(/\.png$/,".jpg");await P.writeFile(o,e);try{await q("sips",["-Z",String(t),"-s","format","jpeg","-s","formatOptions","70",o,"--out",r],15e3);const a=await q("sips",["-g","pixelWidth","-g","pixelHeight",r]);return{data:(await P.readFile(r)).toString("base64"),mimeType:"image/jpeg",width:Number(/pixelWidth:\s*(\d+)/.exec(a)?.[1]??0),height:Number(/pixelHeight:\s*(\d+)/.exec(a)?.[1]??0)}}finally{await P.unlink(o).catch(()=>{});await P.unlink(r).catch(()=>{})}}const i=Ls(Ne.PNG.sync.read(e),Math.min(t,1024));const n=Ne.PNG.sync.write(i,{colorType:2,deflateLevel:9});return{data:n.toString("base64"),mimeType:"image/png",width:i.width,height:i.height}}function Ls(e,t){const i=Math.min(1,t/Math.max(e.width,e.height));if(i===1)return e;const n=Math.max(1,Math.round(e.width*i));const o=Math.max(1,Math.round(e.height*i));const r=new Ne.PNG({width:n,height:o});const a=e.width/n;const s=e.height/o;for(let c=0;c<o;c++){const l=Math.floor(c*s);const h=Math.max(l+1,Math.floor((c+1)*s));for(let u=0;u<n;u++){const d=Math.floor(u*a);const f=Math.max(d+1,Math.floor((u+1)*a));const m=[0,0,0,0];let p=0;for(let y=l;y<h;y++){for(let b=d;b<f;b++){const v=(y*e.width+b)*4;for(let g=0;g<4;g++)m[g]+=e.data[v+g];p++}}const w=(c*n+u)*4;for(let y=0;y<4;y++)r.data[w+y]=Math.round(m[y]/p)}}return r}var Xe=new Set;async function Bs(e,t){let i=false;try{if(e.platform==="ios"){const n=await L(["status_bar",e.udid,"list"]).catch(()=>"");const o=n.split(/^=+$/m).slice(1).join("").trim();if(!o){await L(["status_bar",e.udid,"override","--time","9:41","--batteryState","charged","--batteryLevel","100","--wifiBars","3","--cellularBars","4"]);i=true}}else if(e.platform==="android"){const n=(await k(e.serial,"settings get global sysui_demo_allowed").catch(()=>"")).trim();if(n!=="1"&&!Xe.has(e.serial)){Xe.add(e.serial);await Ft(e.serial,true);i=true}}if(i)await new Promise(n=>setTimeout(n,400))}catch{i=false}try{return{result:await t(),steadied:i}}finally{if(i){if(e.platform==="ios")await L(["status_bar",e.udid,"clear"]).catch(()=>void 0);else if(e.platform==="android"){await Ft(e.serial,false);await k(e.serial,"settings put global sysui_demo_allowed 0").catch(()=>void 0);Xe.delete(e.serial)}}}}async function js(e){const t=Je(e);if(e.platform==="ios-device"){const i=await ni(e.udid,t);return i?`Quit ${t}.`:`${t} was not running.`}if(e.platform==="ios"){await L(["terminate",e.udid,t]).catch(i=>{if(!/found nothing to terminate|not running/i.test(String(i)))throw i})}else{await k(e.serial,`am force-stop ${D(t)}`)}return`Quit ${t}.`}async function Ze(e){const t=Je(e);if(e.platform==="ios")await We(e.udid,t);else if(e.platform==="ios-device")await Fe(e.udid,t);else await k(e.serial,`monkey -p ${D(t)} -c android.intent.category.LAUNCHER 1`);return`Launched ${t}.`}async function Fs(e){if(e.platform==="ios-device"){await Fe(e.udid,"com.apple.Preferences");return"Sent to the background for real (Settings opened on top)."}if(e.platform==="ios"){await L(["launch",e.udid,"com.apple.Preferences"]);return"Sent to the background for real (Settings opened on top)."}await k(e.serial,"input keyevent 3");return"Sent to the background for real (pressed Home)."}async function Us(e,t){if(!/^[a-zA-Z][\w+.-]*:/.test(t))throw new Error(`"${t}" is not a link with a scheme (like https://\u2026 or myapp://\u2026).`);if(e.platform==="ios-device"){await fs(e.udid,t);return"Opened through the OS on the iPhone."}if(e.platform==="ios"){await L(["openurl",e.udid,t]);return'Opened through the OS. A custom scheme may first show an "Open in" prompt on the simulator.'}await k(e.serial,`am start -a android.intent.action.VIEW -d ${D(t)}`);return"Opened through the OS (an Android VIEW intent)."}var Hs=["buoy-license","react-buoy/@react_buoy_sync_device_id.json","react-buoy/@react_buoy_install.json"];async function wi(e){const t=T.join(e,"Info.plist");const i=a=>q("plutil",["-extract",a,"raw","-o","-",t]).then(s=>s.trim()).catch(()=>"");const n=await i("CFBundleIdentifier");if(!n)throw new Error(`${e} has no Info.plist with a bundle id. Pass a built .app folder.`);const o=(await q("plutil",["-extract","CFBundleSupportedPlatforms","json","-o","-",t]).catch(()=>"[]")).toLowerCase();const r=(await i("DTPlatformName")).toLowerCase();return{bundleId:n,simulator:o.includes("iphonesimulator")||r==="iphonesimulator"}}async function Ys(e){const t=[process.env.ANDROID_HOME,process.env.ANDROID_SDK_ROOT,T.join(ce.homedir(),"Library","Android","sdk"),T.join(ce.homedir(),"Android","Sdk")].filter(i=>!!i);for(const i of t){const n=T.join(i,"build-tools");const o=(await P.readdir(n).catch(()=>[])).sort().reverse();for(const r of o){for(const a of["aapt2","aapt"]){const s=T.join(n,r,a);try{await P.access(s)}catch{continue}const c=await q(s,a==="aapt2"?["dump","packagename",e]:["dump","badging",e]).catch(()=>"");const l=a==="aapt2"?c.trim().split("\n")[0]:/package: name='([^']+)'/.exec(c)?.[1]??"";if(l)return l}}}throw new Error("Could not read the APK's package name (needs aapt2 from the Android SDK build-tools). Nothing was changed.")}async function Ws(e,t,i=true){const n=Je(e);const o=T.resolve(t);try{await P.access(o)}catch{throw new Error(`No build at ${o}. Pass appPath: the built .app folder (iOS Simulator) or .apk file (Android). Nothing was changed.`)}if(e.platform==="android")return qs(e,n,o,i);if(e.platform==="ios-device")return zs(e,n,o,i);if(!/\.app\/?$/i.test(o))throw new Error("On the iOS Simulator, appPath must be a built .app folder. Nothing was changed.");const r=await wi(o);if(r.bundleId!==n)throw new Error(`${T.basename(o)} is ${r.bundleId}, but this app is ${n}. Nothing was changed.`);if(!r.simulator)throw new Error(`${T.basename(o)} is a device build, not a simulator build. Nothing was changed.`);const a=i?await P.mkdtemp(T.join(ce.tmpdir(),"buoy-mcp-keep-")):null;try{let s=false;let c=[];if(a){await P.chmod(a,448);const h=(await L(["get_app_container",e.udid,n,"data"]).catch(()=>"")).trim();if(h)s=await gi(T.join(h,"Documents"),a);if(h&&!s){try{c=await ks(h)}catch(u){throw new Error(`Couldn't read Buoy's sign-in from the app's WebView storage (${u.message}). Nothing was changed. Pass keepBuoy: false to reinstall anyway.`)}}}await L(["terminate",e.udid,n]).catch(()=>void 0);await L(["uninstall",e.udid,n]);try{await L(["install",e.udid,o],18e4)}catch(h){throw new Error(`The app was removed, but installing ${T.basename(o)} failed: ${h.message}. Install it again by hand.`)}let l="";if(a&&s){try{const h=(await L(["get_app_container",e.udid,n,"data"])).trim();await gi(a,T.join(h,"Documents"));e.container=h;l=" Kept Buoy's sign-in and device id, so the same deviceId still works."}catch(h){l=` Buoy's sign-in could not be restored (${h.message}): sign in to Buoy again in the app.`}}else if(c.length){try{const h=(await L(["get_app_container",e.udid,n,"data"])).trim();await We(e.udid,n);const u=await Ts(h);await L(["terminate",e.udid,n]).catch(()=>void 0);if(!u||!await Ss(h,c))throw new Error("the app made no WebView storage");e.container=h;l=" Kept Buoy's sign-in and device id, so the same deviceId still works."}catch(h){l=` Buoy's sign-in could not be restored (${h.message}): sign in to Buoy again in the app.`}}else if(i){l=" The old install had no Buoy sign-in to keep."}await We(e.udid,n);return`Reinstalled ${n} from ${o} and launched it. The app's own data is gone, like a new user.${l}`}finally{if(a)await P.rm(a,{recursive:true,force:true}).catch(()=>void 0)}}async function gi(e,t){let i=false;for(const n of Hs){const o=T.join(e,n);try{await P.access(o)}catch{continue}const r=T.join(t,n);await P.mkdir(T.dirname(r),{recursive:true});await P.cp(o,r,{recursive:true,force:true});i=true}return i}async function qs(e,t,i,n){if(!/\.apk$/i.test(i))throw new Error("On Android, appPath must be an .apk file. Nothing was changed.");const o=await Ys(i);if(o!==t)throw new Error(`${T.basename(i)} is ${o}, but this app is ${t}. Nothing was changed.`);const r=[];if(n){const c=await k(e.serial,`run-as ${D(t)} find files/buoy-license files/react-buoy -maxdepth 1 -type f 2>/dev/null`).catch(()=>"");const l=c.split("\n").map(h=>h.trim()).filter(h=>h.startsWith("files/buoy-license/")||/react-buoy\/@react_buoy_(sync_device_id|install)\.json$/.test(h));for(const h of l){const u=await k(e.serial,`run-as ${D(t)} base64 ${D(h)}`).catch(()=>"");if(u.trim())r.push({rel:h,data:u.replace(/\s+/g,"")})}}let a=[];if(n&&!r.length&&await Os(e.serial,t)){try{a=await Ms(e.serial,t)}catch(c){throw new Error(`Couldn't read Buoy's sign-in from the app's WebView (${c.message}). Nothing was changed. Pass keepBuoy: false to reinstall anyway.`)}}await K(e.serial,["install","-r","-d",i],18e4);await k(e.serial,`pm clear ${D(t)}`);let s="";if(a.length){await Ze(e);try{await Is(e.serial,t,a);s=" Kept Buoy's sign-in and device id, so the same deviceId still works."}catch(c){s=` Buoy's sign-in could not be restored (${c.message}): sign in to Buoy again in the app.`}return`Reinstalled ${t} from ${i} and launched it. The app's data is gone, like a new user.${s}`}if(r.length){try{for(const c of r){const l=T.posix.dirname(c.rel);await k(e.serial,`echo ${D(c.data)} | run-as ${D(t)} sh -c ${D(`mkdir -p ${D(l)} && base64 -d > ${D(c.rel)}`)}`)}s=" Kept Buoy's sign-in and device id, so the same deviceId still works."}catch(c){s=` Buoy's sign-in could not be restored (${c.message}): sign in to Buoy again in the app.`}}else if(n){s=" No Buoy sign-in could be read to keep (needs a debug build): sign in to Buoy again in the app."}await Ze(e);return`Reinstalled ${t} from ${i} and launched it. The app's data is gone, like a new user.${s}`}async function zs(e,t,i,n){if(!/\.app\/?$/i.test(i))throw new Error("On an iPhone, appPath must be a built .app folder (a device build). Nothing was changed.");const o=await wi(i);if(o.bundleId!==t)throw new Error(`${T.basename(i)} is ${o.bundleId}, but this app is ${t}. Nothing was changed.`);if(o.simulator)throw new Error(`${T.basename(i)} is a simulator build, not a device build. Nothing was changed.`);const r=["Documents/buoy-license/buoy-gg-license.json","Documents/react-buoy/@react_buoy_sync_device_id.json","Documents/react-buoy/@react_buoy_install.json"];const a=n?await P.mkdtemp(T.join(ce.tmpdir(),"buoy-mcp-keep-")):null;try{const s=[];if(a){await P.chmod(a,448);for(const l of r){let h;try{h=await ti(e.udid,t,l)}catch(d){throw new Error(`Could not back up Buoy's ${T.basename(l)} from the iPhone (${d.message}). Nothing was changed.`)}if(!h)continue;const u=T.join(a,l.replace(/\//g,"__"));await P.writeFile(u,h,{mode:384});s.push({rel:l,local:u})}}await ni(e.udid,t).catch(()=>false);await ps(e.udid,t);try{await ms(e.udid,i)}catch(l){throw new Error(`The app was removed, but installing ${T.basename(i)} failed: ${l.message}. Install it again from Xcode.`)}let c="";if(s.length){try{for(const u of s)await ws(e.udid,t,u.local,u.rel);const l=s.some(u=>u.rel.includes("buoy-license"));const h=s.some(u=>u.rel.includes("sync_device_id"));c=l?` Kept Buoy's sign-in${h?" and device id, so the same deviceId still works":""}.`:` Kept the device id; there was no Buoy sign-in to keep, so sign in to Buoy in the app.`}catch(l){c=` Buoy's sign-in could not be restored (${l.message}): sign in to Buoy again in the app.`}}else if(n)c=" The old install had no Buoy sign-in to keep.";await Fe(e.udid,t);return`Reinstalled ${t} on the iPhone and launched it. The app's own data is gone, like a new user.${c}`}finally{if(a)await P.rm(a,{recursive:true,force:true}).catch(()=>void 0)}}async function Ks(e){const t=JSON.parse(await L(["list","devices","available","-j"])).devices;const i=Object.entries(t).filter(([s])=>/iOS/.test(s)).flatMap(([,s])=>s);const n=e.trim().toLowerCase();const o=i.filter(s=>s.udid.toLowerCase()===n||s.name.toLowerCase()===n);const r=o.length?o:i.filter(s=>s.name.toLowerCase().includes(n));if(r.length===0)throw new Error(`No iOS Simulator matches "${e}". Run \`xcrun simctl list devices available\` to see them.`);if(r.length>1)throw new Error(`${r.length} simulators match "${e}": ${r.map(s=>`${s.name} (${s.udid})`).join(", ")}. Pass the udid.`);const a=r[0];if(a.state!=="Booted"){try{await ui("xcrun",["simctl","boot",a.udid],{timeout:12e4,env:Ei()})}catch(s){const c=s;throw new Error(`xcrun simctl boot failed: ${(c.stderr||c.message||"").trim().slice(0,300)}`)}}await q("open",["-a","Simulator","--args","-CurrentDeviceUDID",a.udid]).catch(()=>void 0);return a.state==="Booted"?`${a.name} (${a.udid}) was already booted.`:`Booted ${a.name} (${a.udid}).`}var Gs=Kn(zn);async function Qe(e,t,i={},n={}){if(t==="All")throw new Error('Device control acts on one app. Pass its deviceId, not "All".');let o;let r=true;try{o=e.requireProDevice(t)}catch(c){const l=c instanceof Error?c.message:String(c);const h=l.startsWith(ee)&&/is not connected right now/.test(l)?/"([^"]+)"/.exec(l)?.[1]:void 0;const u=h?e.deviceWasAllowed(h):false;if(!n.allowOffline||!h||!u)throw c;o=h;r=false}const a=e.listDevices().find(c=>c.deviceId===o)?.platform;let s=await ue(o,a,i);if(n.changesApp&&be(o)===s)s=await pi(o,s);return{id:o,host:s,online:r}}function Vs(e){if(e.platform==="ios")return`simulator "${e.name}"`;if(e.platform==="ios-device")return`iPhone "${e.name}"`;return`Android device ${e.serial}`}var et=new Map;function yi(e){if(!et.has(e))et.set(e,Gs("which",[e],{timeout:3e3}).then(()=>true,()=>false));return et.get(e)}async function Js(e,t){let i=t;if(e.platform==="android"){const o=be(e.deviceId);const r=o??(e.isConnected?await Promise.race([ue(e.deviceId,"android").catch(()=>void 0),new Promise(s=>setTimeout(()=>s(void 0),3e3))]):void 0);const a=r?.platform==="android"&&!!rt(r.serial)||await yi("adb");return{kind:"android",...r&&r.platform==="android"?{id:r.serial,name:r.packageName}:{},can:["screenshot","screen_recording","baseline diffs","quit/launch/reinstall","real lifecycle events","native_tap/type/swipe","native_inspect"],...!a?{missing:["adb (Android SDK platform-tools)"]}:!r?{missing:["proof of which phone runs it (needs a debug build reachable by adb)"]}:{}}}if(e.platform!=="ios")return{kind:e.platform??"unknown",can:[],missing:["host control works on iOS and Android apps"]};let n=be(e.deviceId);if(!n&&!i&&e.isConnected){n=await Promise.race([ue(e.deviceId,"ios").catch(()=>void 0),new Promise(o=>setTimeout(()=>o(void 0),4e3))])}if(!i&&n?.platform==="ios")i={name:n.name,udid:n.udid};if(n?.platform==="ios-device"){return{kind:"physical iPhone",id:n.udid,name:n.name,can:["screenshot","screen_recording (if this iPhone supports it)","baseline diffs","quit/launch/reinstall","open_url","native_tap/type/swipe/pinch (by query, testID, nativeTag or x/y; not label)","native_inspect (the app's side only)"]}}if(i){const o=await yi("idb");return{kind:"ios simulator",id:i.udid,name:i.name,can:["screenshot","screen_recording","baseline diffs","quit/launch/reinstall/boot","real lifecycle events",...o?["native_tap/type/swipe/pinch","native_inspect"]:[]],...o?{}:{missing:["idb for real touches (brew install facebook/fb/idb-companion && pipx install fb-idb)"]}}}return void 0}function Xs(e,t){const i=async o=>{const{profileId:r}=zt.parse(o);const a=await t.get(r);await Qe(e,a.target.deviceId,{udid:a.target.udid,serial:a.target.serial},{allowOffline:true});return a};const n=async o=>{const r=qt.parse(o);if(r.udid&&r.serial)throw new Error("Choose a sim ID or an Android serial, not both.");if(r.action==="stop"){if(!r.profileId)throw new Error("Stop needs a profileId.");if(r.deviceId||r.udid||r.serial||r.durationMs||r.thresholdMs)throw new Error("Stop takes only a profileId.");await i({profileId:r.profileId});return t.stop(r.profileId)}if(r.profileId)throw new Error("Start a new run without a profileId.");const{id:a,host:s}=await Qe(e,r.deviceId,{udid:r.udid,serial:r.serial},{changesApp:true});if(s.platform==="android"){if(r.udid)throw new Error("Use a serial for an Android trace.");const h=await Un(s.serial,s.packageName);const u=await t.start({platform:"android",deviceId:a,serial:s.serial,appId:s.packageName,pid:h},{durationMs:r.durationMs??(r.action==="record"?1e4:6e4),thresholdMs:r.thresholdMs});return r.action==="record"?t.wait(u.profileId):u}if(s.platform!=="ios"||!s.bundleId)throw new Error("Choose an iOS Simulator or an Android app.");if(r.serial)throw new Error("Use a sim ID for an iOS trace.");const c=await It(s.udid,s.bundleId);const l=await t.start({deviceId:a,udid:s.udid,appId:s.bundleId,pid:c},{durationMs:r.durationMs??(r.action==="record"?1e4:6e4),thresholdMs:r.thresholdMs});return r.action==="record"?t.wait(l.profileId):l};return{get:i,run:n}}export{pe as readProjectKey,Ee as BROKER_PORT,mt as BROKER_URL,wt as DEFAULT_ACTION_TIMEOUT_MS,yt as DEFAULT_SNAPSHOT_TIMEOUT_MS,Se as DASHBOARD_NAME,B as ALL_DEVICES,ee as NOT_READY_MARK,$r as SyncClient,It as simPid,te as MIN_SECRET_LENGTH,tn as setRecordingProbe,nn as fillSecrets,xt as scrubSecrets,an as typedSecretValues,cn as secretValues,D as q,De as adbFor,K as adb,k as adbShell,Bt as adbSerials,hn as adbScreenSize,fn as adbAppFrame,vn as adbTap,_n as adbSwipe,En as adbText,Sn as adbUiNodes,On as findUiNodes,Wn as ProfileService,qn as stopNativeProfiles,qt as profileInput,zt as getProfileInput,_e as require_png,rs as resolveSims,Qt as captureIosFramebuffer,ns as cropToBuffer,ri as phoneScreenshot,us as BLACK_PHONE_NOTE,ds as isAllBlack,gs as phoneRecordArgs,ys as coreDeviceId,li as isDeviceUdid,hi as assertUdid,be as rememberedHost,ue as findHost,pi as verifyHost,xs as hostPng,Ns as toSmallImage,Bs as withSteadyStatusBar,js as hostQuit,Ze as hostLaunch,Fs as hostBackground,Us as hostOpenUrl,Ws as hostReinstall,Ks as bootSimulator,Qe as hostTarget,Vs as hostLabel,Js as hostSummary,Xs as profileActions};
|