@buoy-gg/mcp 7.0.56 → 7.0.58

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.
@@ -0,0 +1 @@
1
+ import{createRequire as e}from"module";const c=e(import.meta.url);import{cliPath as r,describeDevices as i,describeDiagnosis as t,describeInputs as o,proRequired as s,readEntitlement as m,run as u}from"./chunk-6EOB4FHJ.mjs";import"./chunk-MTTWR4T7.mjs";export{r as cliPath,i as describeDevices,t as describeDiagnosis,o as describeInputs,s as proRequired,m as readEntitlement,u as run};
@@ -0,0 +1,5 @@
1
+ import{createRequire as p}from"module";const j=p(import.meta.url);import{execFile as m}from"child_process";import{promisify as d}from"util";import{existsSync as l,readFileSync as h}from"fs";import u from"os";import i from"path";var f=d(m);function y(){const n=process.env.BUOY_SIMCAM_DIR;const e=[...n?[i.join(n,"buoycam")]:[],"/Applications/Buoy.app/Contents/Resources/simcam/buoycam",i.join(u.homedir(),"Applications/Buoy.app/Contents/Resources/simcam/buoycam")];let t=__dirname;for(let o=0;o<8;o++){e.push(i.join(t,"apps/desktop/resources/simcam/buoycam"));const a=i.dirname(t);if(a===t)break;t=a}return e}var s=null;function c(){if(s)return s;s=y().find(n=>l(n))??null;return s}async function g(n){const e=c();if(!e){return{ok:false,error:"The simulator camera components were not found. They ship with the Buoy desktop app \u2014 install it, or set BUOY_SIMCAM_DIR to the directory holding `buoycam`."}}try{const{stdout:t}=await f(e,[...n,"--json"],{timeout:3e4});return{ok:true,data:JSON.parse(t)}}catch(t){const o=t.stdout??"";try{return{ok:false,data:JSON.parse(o)}}catch{return{ok:false,error:t.message||"the simulator camera CLI failed"}}}}function b(n){const e=n.filter(r=>r.level==="fail");const t=n.filter(r=>r.level==="warn");if(e.length===0&&t.length===0){return"The simulator camera is healthy \u2014 every check passed."}const o=[];for(const r of[...e,...t]){o.push(`- **${r.name}** (${r.level}): ${r.detail}`);if(r.fix)o.push(` ${r.fix}`)}const a=e.length>0?`${e.length} problem${e.length===1?"":"s"} found.`:`No hard failures, but ${t.length} thing${t.length===1?"":"s"} worth knowing.`;return[a,"",...o].join("\n")}function _(n,e){if(n.length===0){return"No booted simulators.\n\n_Boot one first \u2014 `xcrun simctl boot <udid>`, or open Simulator.app. The camera attaches to a simulator, so nothing else here works until one is running._"}const t=[n.length===1?"1 booted simulator:":`${n.length} booted simulators:`,...n.map(o=>`- **${o.name}** (${o.runtime}) \u2014 \`${o.udid}\``)];if(n.length>1&&e===null){t.push("","_More than one is booted, so pass `udid` to list a specific one's apps \u2014 and to every other camera tool that takes one._");return t.join("\n")}if(e===null){t.push("","_Could not read the installed apps._");return t.join("\n")}if(e.length===0){t.push("","No apps installed on it.");return t.join("\n")}t.push("",`${e.length} installed app${e.length===1?"":"s"}:`,...e.map(o=>`- ${o.name} \u2014 \`${o.bundleId}\``),"","_Pass one of these bundle ids to `camera_launch` to relaunch it with the camera attached._");return t.join("\n")}function $(n,e){const t=[];t.push(n.length?'**Mac cameras** (for `kind: "camera"`):':"**Mac cameras**: none found.");if(n.length){t.push(...n.map(o=>`- ${o.name} \u2014 \`${o.uniqueId}\``))}else{t.push("_If this Mac has a webcam, Buoy has not been granted Camera permission (System Settings \u2192 Privacy & Security \u2192 Camera)._")}t.push("");t.push(e.length?'**Capturable windows** (for `kind: "screen"`, `value: "window:<id>"`):':"**Capturable windows**: none found.");if(e.length){t.push(...e.map(o=>`- ${o.app}${o.title?` \u2014 ${o.title}`:""} \u2014 \`window:${o.id}\``))}else{t.push("_Screen Recording permission is almost certainly missing (System Settings \u2192 Privacy & Security \u2192 Screen & System Audio Recording)._")}return t.join("\n")}function k(){const n=process.env.BUOY_ENTITLEMENT_FILE;if(n)return[n];return[i.join(u.homedir(),"Library/Application Support/Buoy/entitlement.json"),i.join(u.homedir(),"Library/Application Support/buoy-desktop/entitlement.json")]}function S(){for(const n of k()){try{if(!l(n))continue;const e=JSON.parse(h(n,"utf8"));return{tier:e.tier==="pro"||e.tier==="free"?e.tier:"locked",isPro:e.isPro===true&&e.hasDesktopAccess===true&&typeof e.validUntil==="number"&&Number.isFinite(e.validUntil)&&Date.now()<e.validUntil&&typeof e.proValidUntil==="number"&&Number.isFinite(e.proValidUntil)&&Date.now()<e.proValidUntil}}catch{}}return{tier:"locked",isPro:false}}function v(n){return`\`${n}\` is a Buoy Pro feature.
2
+
3
+ The simulator camera itself is free \u2014 the Mac camera, image, video, test-pattern and QR sources all work in the Buoy desktop panel with a Free account. Pro adds the screen region, the non-QR symbologies (PDF417, Aztec, DataMatrix, Code 128), and driving any of it from an agent.
4
+
5
+ _Tell the user to enter a licence in Buoy Desktop, or see https://buoy.gg/pricing. Don't try to work around it \u2014 use the desktop panel by hand instead._`}export{c as cliPath,g as run,b as describeDiagnosis,_ as describeDevices,$ as describeInputs,S as readEntitlement,v as proRequired};
@@ -0,0 +1 @@
1
+ import{createRequire as _}from"module";const n=_(import.meta.url);var i=Object.create;var u=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var a=Object.getOwnPropertyNames;var y=Object.getPrototypeOf;var c=Object.prototype.hasOwnProperty;var s=(e=>typeof n!=="undefined"?n:typeof Proxy!=="undefined"?new Proxy(e,{get:(r,t)=>(typeof n!=="undefined"?n:r)[t]}):e)(function(e){if(typeof n!=="undefined")return n.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var O=(e,r)=>function t(){return r||(0,e[a(e)[0]])((r={exports:{}}).exports,r),r.exports};var l=(e,r,t,p)=>{if(r&&typeof r==="object"||typeof r==="function"){for(let o of a(r))if(!c.call(e,o)&&o!==t)u(e,o,{get:()=>r[o],enumerable:!(p=f(r,o))||p.enumerable})}return e};var P=(e,r,t)=>(t=e!=null?i(y(e)):{},l(r||!e||!e.__esModule?u(t,"default",{value:e,enumerable:true}):t,e));export{s as __require,O as __commonJS,P as __toESM};
@@ -1,4 +1,4 @@
1
- import{createRequire as Ze}from"module";import{execFileSync as Se}from"child_process";import{join as de}from"path";import{closeSync as ke,linkSync as ve,mkdirSync as et,openSync as xe,readFileSync as tt,renameSync as nt,statSync as Ee,unlinkSync as Y,writeFileSync as Te,writeSync as st}from"fs";import{tmpdir as rt}from"os";import{pathToFileURL as at}from"url";var ot=`(() => {
1
+ import{createRequire as Ze}from"module";const vt=Ze(import.meta.url);import{createRequire as et}from"module";import{execFileSync as Se}from"child_process";import{join as de}from"path";import{closeSync as ke,linkSync as ve,mkdirSync as tt,openSync as xe,readFileSync as nt,renameSync as st,statSync as Ee,unlinkSync as Y,writeFileSync as Te,writeSync as rt}from"fs";import{tmpdir as at}from"os";import{pathToFileURL as ot}from"url";var it=`(() => {
2
2
  const supported = (t) => {
3
3
  try { return (PerformanceObserver.supportedEntryTypes || []).includes(t); } catch { return false; }
4
4
  };
@@ -126,7 +126,7 @@ import{createRequire as Ze}from"module";import{execFileSync as Se}from"child_pro
126
126
  }),
127
127
  };
128
128
  return true;
129
- })()`;var it=`(() => (window.__buoyBench ? window.__buoyBench.end() : null))()`;var lt=`(() => (window.__buoyBench ? window.__buoyBench.collect() : null))()`;var ct=`new Promise((resolve) => {
129
+ })()`;var lt=`(() => (window.__buoyBench ? window.__buoyBench.end() : null))()`;var ct=`(() => (window.__buoyBench ? window.__buoyBench.collect() : null))()`;var ut=`new Promise((resolve) => {
130
130
  let n = 0, t0 = 0;
131
131
  const tick = (t) => {
132
132
  if (!t0) t0 = t;
@@ -142,5 +142,5 @@ import{createRequire as Ze}from"module";import{execFileSync as Se}from"child_pro
142
142
  const ext = gl.getExtension("WEBGL_debug_renderer_info");
143
143
  return String(gl.getParameter(ext ? ext.UNMASKED_RENDERER_WEBGL : gl.RENDERER));
144
144
  } catch { return null; }
145
- })()`;function fe(e){return/swiftshader|llvmpipe|softpipe|software|microsoft basic render/i.test(e)}function _e(e,r){if(e.length===0)return null;const t=[...e].sort((s,n)=>s-n);const a=Math.ceil(r/100*t.length)-1;return t[Math.min(t.length-1,Math.max(0,a))]}var ut=[30,60,75,90,120,144,165,240];function Ce(e){for(const r of ut)if(Math.abs(e-r)/r<=.08)return r;return Math.max(1,Math.round(e))}function he(e,r){let t=r>>>0;const a=()=>{t=t+1831565813>>>0;let n=t;n=Math.imul(n^n>>>15,n|1);n^=n+Math.imul(n^n>>>7,n|61);return((n^n>>>14)>>>0)/4294967296};const s=[...e];for(let n=s.length-1;n>0;n--){const i=Math.floor(a()*(n+1));[s[n],s[i]]=[s[i],s[n]]}return s}function Ae(e,r,t){const a=new URL(e);const s=r.path??t;if(s)a.pathname=s.startsWith("/")?s:`${a.pathname.replace(/\/$/,"")}/${s}`;for(const[n,i]of Object.entries(r.params??{}))a.searchParams.set(n,i);return a.toString()}var dt=["STATE_PRESENTED_ALL","STATE_PRESENTED_PARTIAL","STATE_DROPPED"];function Le(e){const r=d=>e.find(M=>M.name===d&&M.ts!=null);const t=r("__buoy_bench_start");const a=r("__buoy_bench_end");if(!t||!a||a.ts<=t.ts)return null;if(t.pid!==a.pid)return null;const s=t.pid;const n={STATE_PRESENTED_ALL:0,STATE_PRESENTED_PARTIAL:1,STATE_DROPPED:2};const i=new Map;for(const d of e){if(d.name!=="PipelineReporter"||d.ph!=="b")continue;if(s!=null&&d.pid!==s)continue;if(d.ts==null||d.ts<t.ts||d.ts>a.ts)continue;const M=d.args?.frame_reporter;if(!M||M.frame_sequence==null)continue;const S=M.state;if(!dt.includes(S))continue;const T=`${M.layer_tree_host_id??""}:${M.frame_source??""}:${M.frame_sequence}`;const b=Boolean(M.affects_smoothness)&&S!=="STATE_PRESENTED_ALL";const A=i.get(T);if(!A)i.set(T,{state:S,fault:b});else{if(n[S]>n[A.state])A.state=S;A.fault=A.fault||b}}let o=0;let c=0;let p=0;let h=0;for(const d of i.values()){if(d.state==="STATE_PRESENTED_ALL")o++;else if(d.state==="STATE_PRESENTED_PARTIAL")c++;else p++;if(d.fault)h++}const u=o+c+p;if(u===0)return null;const l=(a.ts-t.ts)/1e6;return{full:o,partial:c,dropped:p,faults:h,shownFps:x((o+c)/l),droppedPct:x(h/u*100)}}function Fe(e,r){const t=e.split("+");const a=t[t.length-1];const s=new Set(t.slice(0,-1).map(o=>o.toLowerCase()));if(r.key!==a)return false;const n=a.toLowerCase();const i={ctrl:s.has("control")||n==="control",alt:s.has("alt")||n==="alt",meta:s.has("meta")||s.has("command")||n==="meta",shift:s.has("shift")||n==="shift"};if(Boolean(r.ctrl)!==i.ctrl||Boolean(r.alt)!==i.alt||Boolean(r.meta)!==i.meta)return false;if(!i.shift&&r.shift&&a.length!==1)return false;if(i.shift&&!r.shift)return false;return true}function Ne(e){const r=[];for(const t of e){const a=t.startTime+t.duration;const s=r.find(i=>i.interactionId===t.interactionId&&Math.abs(i.startTime+i.duration-a)<=8);if(!s){r.push({...t});continue}const n=s.startTime+s.duration;s.startTime=Math.min(s.startTime,t.startTime);s.processingStart=Math.min(s.processingStart,t.processingStart);s.processingEnd=Math.max(s.processingEnd,t.processingEnd);s.duration=Math.max(n,a)-s.startTime;if(t.processingEnd-t.processingStart>0)s.name=t.name}return r}function De(e,r){const{start:t,end:a}=e;const s=Math.max(1,a-t);const n=[];for(let m=1;m<e.frames.length;m++)n.push(e.frames[m]-e.frames[m-1]);const i=1e3/r;const o=_e(n,95);let c=null;if(e.longTasks){let m=0;for(const k of e.longTasks){const W=Math.min(k.end,a)-Math.max(k.start,t);if(W>0)m+=W}c=x(Math.min(100,m/s*100))}let p=null;let h=null;let u=null;let l=[];let d=0;if(e.loaf){let m=0;let k=0;let W=0;const te=new Map;for(const E of e.loaf){if(E.startTime>=t&&E.startTime<=a&&E.startTime+E.duration>a)d++;if(E.startTime<t||E.startTime+E.duration>a)continue;m+=E.blockingDuration;if(E.styleAndLayoutStart>E.startTime)k+=Math.max(0,E.startTime+E.duration-E.styleAndLayoutStart);for(const L of E.scripts){W+=L.forced;const f=te.get(L.name)??{ms:0,count:0};f.ms+=L.duration;f.count++;te.set(L.name,f)}}p=x(m);h=x(k);u=x(W);l=[...te.entries()].map(([E,L])=>({name:E,ms:x(L.ms),count:L.count})).sort((E,L)=>L.ms-E.ms).slice(0,5)}let M=null;if(e.shifts){let m=0;for(const k of e.shifts)if(!k.hadRecentInput&&k.startTime>=t&&k.startTime<=a)m+=k.value;M=Math.round(m*1e3)/1e3}let S=null;let T=null;if(e.events){const m=Ne(e.events.filter(k=>k.startTime>=t&&k.startTime<=a));S=new Set(m.map(k=>k.interactionId)).size;for(const k of m)if(!T||k.duration>T.duration)T=k}const b=T;const A=e.frames;const ee=[...(e.clicks??[]).map(m=>m.t),...(e.keys??[]).map(m=>m.t)];const I=ee.length?Math.max(...ee):null;const se=I==null||A.filter(m=>m>I).length>=2;const ae=A.length===0?s:Math.max(0,A[0]-t,a-A[A.length-1]);return{edgeSilenceMs:x(ae),frameGapCount:n.length,inputCaptureComplete:se,resized:Boolean(e.resized),interactions:S,worstInteractionMs:b?x(b.duration):null,worstInputDelayMs:b?x(Math.max(0,b.processingStart-b.startTime)):null,worstProcessingMs:b?x(Math.max(0,b.processingEnd-b.processingStart)):null,worstPresentationMs:b?x(Math.max(0,b.startTime+b.duration-b.processingEnd)):null,fps:x(e.frames.length/s*1e3),frameGapP95Ms:o==null?null:x(o),frameGapMaxMs:n.length?x(Math.max(...n)):null,longFrames:n.filter(m=>m>i*1.5).length,longTaskPct:c,loafBlockingMs:p,styleLayoutMs:h,forcedLayoutMs:u,topScripts:l,loafCutOff:d,layoutShift:M,domNodesStart:e.domStart,domNodesEnd:e.domEnd}}function Be(e,r,t){const a=o=>o in e&&o in r?r[o]-e[o]:null;const s=o=>{const c=a(o);return c==null?null:x(c*1e3)};const n=s("TaskDuration");const i=s("Timestamp")??t;return{mainThreadPct:n==null?null:x(n/Math.max(1,i)*100),scriptMs:s("ScriptDuration"),styleMs:s("RecalcStyleDuration"),layoutMs:s("LayoutDuration"),layoutCount:a("LayoutCount"),styleCount:a("RecalcStyleCount"),heapMb:"JSHeapUsedSize"in r?x(r.JSHeapUsedSize/(1024*1024)):null}}function Ie(e,r){const t=[];if(e.droppedPct!=null&&e.droppedPct>=5)t.push(`${e.droppedPct}% of frames dropped or partial`);if(e.fps!=null&&e.fps<r*.95)t.push("Low main-thread frame rate");if(e.frameGapCount&&e.longFrames/e.frameGapCount>=.02)t.push("Frequent long frames");const a=Math.max(e.frameGapMaxMs??0,e.edgeSilenceMs??0);if(a>=100)t.push(`Freeze of ${Math.round(a)} ms`);if(e.mainThreadPct!=null&&e.mainThreadPct>=80)t.push("Main thread near full");if(e.longTaskPct!=null&&e.longTaskPct>=20)t.push("Long tasks block the main thread");if(e.forcedLayoutMs!=null&&e.forcedLayoutMs>=50)t.push(`Forced layout in scripts (${Math.round(e.forcedLayoutMs)} ms)`);if(e.layoutShift!=null&&e.layoutShift>=.1)t.push(`Layout shifts (${e.layoutShift})`);if(e.worstInteractionMs!=null&&e.worstInteractionMs>=200){t.push(`Slow response to input (${Math.round(e.worstInteractionMs)} ms)`)}return t}function pe(e,r){const t=e.filter(c=>!c.failed&&c.fps!=null);const a=[...t].sort((c,p)=>(c.fps??0)-(p.fps??0));const s=t.filter(c=>c.droppedPct!=null).length;const n=t.length>0&&s===t.length;const i=n?[...t].sort((c,p)=>c.droppedPct-p.droppedPct):a;const o=i.length?i[Math.floor((i.length-1)/2)]:null;return{caseName:e[0]?.caseName??"",cpu:e[0]?.cpu??1,validRuns:t.length,plannedRuns:e.length,isBaseline:false,deltaDroppedPct:null,tracedRuns:s,...(()=>{const c=n?t.map(h=>h.droppedPct):[];const p=t.map(h=>h.worstInteractionMs).filter(h=>h!=null);return{droppedMin:c.length?Math.min(...c):null,droppedMax:c.length?Math.max(...c):null,inputMin:p.length?Math.min(...p):null,inputMax:p.length?Math.max(...p):null}})(),runs:e,median:o,fpsMin:a.length?a[0].fps:null,fpsMax:a.length?a[a.length-1].fps:null,flags:o?Ie(n?o:{...o,droppedPct:null},r):[]}}var ze="Playwright isn't installed in this project. Install it, then run again:\n npm i -D playwright && npx playwright install chromium";async function Oe(e){const r=Ze(de(e,"noop.js"));for(const t of["playwright","playwright-core"]){let a;try{a=r.resolve(t)}catch{continue}const s=await import(at(a).href);const n=s.chromium??s.default?.chromium;if(n)return n}throw new Error(`${ze}
146
- (Searched from ${e}. Pass projectRoot to look elsewhere.)`)}async function G(e,r,t){let a;const s=new Promise(n=>{a=setTimeout(()=>n("timeout"),r)});try{const n=await Promise.race([e,s]);if(n==="timeout"){e.catch(()=>{});return await t()}return n}finally{clearTimeout(a)}}function ft(e){try{return Se("git",["rev-parse","HEAD"],{cwd:e,stdio:["ignore","pipe","ignore"],timeout:3e3}).toString().trim()}catch{return null}}function ht(e){try{const r=Se("git",["status","--porcelain"],{cwd:e,stdio:["ignore","pipe","ignore"],timeout:5e3});return r.toString().trim().length>0}catch{return null}}async function U(e,r){if(!e)return true;return G(e.close().then(()=>true,()=>false),r,async()=>false)}function je(e){if(!e.cases?.length)throw new Error("Give at least one case.");const r=new Set;for(const i of e.cases){if(!i.name?.trim())throw new Error("Every case needs a name.");if(r.has(i.name))throw new Error(`Two cases are named "${i.name}". Give each case its own name.`);r.add(i.name)}const t=(i,o,c,p)=>{if(i==null)return;if(!Number.isInteger(i)||i<c||i>p)throw new Error(`${o} must be a whole number from ${c} to ${p}.`)};const a=(i,o,c)=>{if(i==null)return;if(!Number.isFinite(i)||i<c)throw new Error(`${o} must be a number of at least ${c}.`)};const s=(i,o,c,p)=>{if(i==null)return;if(!Number.isFinite(i)||i<c||i>p)throw new Error(`${o} must be from ${c} to ${p}.`)};t(e.runsPerCase,"runsPerCase",1,10);s(e.durationMs,"durationMs",500,12e4);s(e.settleMs,"settleMs",0,6e4);s(e.refreshHz,"refreshHz",1,500);const n=e.cpuSlowdown??[];for(const i of n)s(i,"each cpuSlowdown rate",1,20);if(new Set(n).size!==n.length)throw new Error("cpuSlowdown has the same rate twice.");if(e.viewport){t(e.viewport.width,"viewport.width",200,8e3);t(e.viewport.height,"viewport.height",200,8e3);s(e.viewport.deviceScaleFactor,"viewport.deviceScaleFactor",.5,4)}t(e.extraRoundsWhenClose,"extraRoundsWhenClose",0,10);s(e.maxBatchMs,"maxBatchMs",1e4,6*60*6e4);for(const[i,o]of Object.entries(e.budgets??{}))s(o,`budgets.${i}`,0,1e9);for(const i of e.steps??[]){if("wait"in i)s(i.wait,"a wait step",0,6e4);if("scroll"in i)s(i.scroll,"a scroll step",-1e5,1e5)}new URL(e.url)}function re(e){return e.replace(/\u001b\[[0-9;]*m/g,"").split("\n")[0].trim().slice(0,160)}var pt=["disabled-by-default-devtools.timeline.frame","blink.user_timing"];var mt=["devtools.timeline","disabled-by-default-devtools.timeline","disabled-by-default-devtools.timeline.frame","blink.user_timing","loading","latencyInfo","v8.execute"];var me=15e7;var we=e=>new Promise(r=>setTimeout(r,e));async function Ge(e,r){const t=new Map;for(const a of r){if("expect"in a)continue;const s="click"in a?a.click:"type"in a?a.type.selector:null;if(!s||t.has(s))continue;const n=await e.$(s);const i=n?await n.boundingBox():null;if(!i)throw new Error(`step target ${s} isn't on the page (or isn't visible)`);t.set(s,{x:i.x+i.width/2,y:i.y+i.height/2})}return t}async function wt(e,r,t){for(const a of r){if("click"in a){const s=t.get(a.click);await e.mouse.click(s.x,s.y)}else if("press"in a)await e.keyboard.press(a.press);else if("type"in a){const s=t.get(a.type.selector);await e.mouse.click(s.x,s.y);await e.keyboard.type(a.type.text)}else if("scroll"in a)await e.mouse.wheel(0,a.scroll);else if("wait"in a)await we(a.wait)}}function Ue(e,r,t=[]){const a=[...r].sort((h,u)=>h.t-u.t);const s=[...t].sort((h,u)=>h.t-u.t);let n=0;let i=0;let o=-Infinity;const c=h=>{while(n<a.length){const u=a[n++];if(u.t>=o&&u.hit.includes(h)){o=u.t;return true}}return false};const p=h=>{while(i<s.length){const u=s[i++];if(u.t>=o&&Fe(h,u)){o=u.t;return true}}return false};for(const[h,u]of e.entries()){if("click"in u&&!c(u.click)){return`step ${h+1} (click ${u.click}) has no real click on its target, in order (moved, covered, or ignored)`}if("type"in u){if(!c(u.type.selector))return`step ${h+1} (type into ${u.type.selector}) has no real click on its target`;for(const l of u.type.text){if(!p(l))return`step ${h+1} (type "${u.type.text}") didn't deliver the key "${l}"`}}if("press"in u&&!p(u.press))return`step ${h+1} (press ${u.press}) didn't deliver that key`}return null}async function gt(e,r){for(const t of r){if(!("expect"in t))continue;const{selector:a,text:s}=t.expect;const n=await e.evaluate(`(() => { const n = document.querySelector(${JSON.stringify(a)}); if (!n) return false; ${s==null?"return true;":`return (n.textContent || "").includes(${JSON.stringify(s)});`} })()`);if(n)return s==null?a:`${a} contains "${s}"`}return null}async function yt(e,r){for(const[t,a]of r){const s=await e.$(t);const n=s?await s.boundingBox():null;if(!n)return`step target ${t} was gone after the run`;const i=n.x+n.width/2;const o=n.y+n.height/2;if(Math.abs(i-a.x)>4||Math.abs(o-a.y)>4){return`step target ${t} moved during the run, so a click may have missed it`}}return null}function He(e){for(const r of e){if(!r.found)return`expected ${r.selector} at the end of the run, but it isn't there`;const t=r.matched??(r.text==null||(r.content??"").includes(r.text));if(!t){return`expected ${r.selector} to contain "${r.text}" at the end of the run, got "${(r.content??"").slice(0,60)}"`}}return null}async function We(e){const{metrics:r}=await e.send("Performance.getMetrics");return Object.fromEntries(r.map(t=>[t.name,t.value]))}async function Mt(e,r=()=>{}){je(e);const t=await qe(e.lockWaitMs??20*6e4,r);const a={browserLeftRunning:false};try{return await bt(e,r,a)}finally{t(a.browserLeftRunning)}}async function qe(e,r=()=>{},t=2e3){const a=Date.now()+Math.max(0,e);let s=false;for(;;){try{return Je()}catch(n){const i=n?.message??String(n);const o=/Another web benchmark|already running/.test(i);if(!o||Date.now()+t>a)throw n;if(!s){r("another web benchmark is running; waiting for it to finish");s=true}await new Promise(c=>setTimeout(c,t))}}}var Z=de(rt(),"buoy-web-bench.lock");var ge=false;function Je(){if(ge){throw new Error("A web benchmark is already running in this process (or left a stuck browser). Wait, or restart the MCP server.")}const e=`${process.pid}:${Date.now()}:${Math.random().toString(36).slice(2)}`;const r=JSON.stringify({pid:process.pid,token:e,startedAt:new Date().toISOString()});const t=()=>{const n=`${Z}.${e.replace(/[^\w]/g,"")}.tmp`;try{Te(n,r);ve(n,Z);return true}catch{return false}finally{try{Y(n)}catch{}}};const a=()=>{try{return JSON.parse(tt(Z,"utf8"))}catch{return null}};const s=n=>{if(!n)return false;try{process.kill(n,0);return true}catch{return false}};if(!t()){const n=a();if(!n)throw new Error("Another web benchmark is starting. Wait for it to finish.");if(s(n.pid)){throw new Error(`Another web benchmark is running (pid ${n.pid}, since ${n.startedAt}). Two at once would skew both; wait for it to finish.`)}const i=`${Z}.cleanup`;let o=null;try{if(Date.now()-Ee(i).mtimeMs>3e4){const c=`${i}.${e.replace(/[^\w]/g,"")}.old`;nt(i,c);if(Date.now()-Ee(c).mtimeMs>3e4)Y(c);else{try{ve(c,i)}catch{}Y(c)}}}catch{}try{o=xe(i,"wx")}catch{throw new Error("Another web benchmark is clearing an old lock. Try again in a moment.")}try{const c=a();if(c?.token===n.token)Y(Z);if(!t())throw new Error("Another web benchmark just started. Wait for it to finish.")}finally{ke(o);try{Y(i)}catch{}}}ge=true;return(n=false)=>{if(n)return;ge=false;if(a()?.token===e){try{Y(Z)}catch{}}}}async function bt(e,r,t){const a=e.runsPerCase??3;const s=Math.max(500,e.durationMs??5e3);const n=Math.max(0,e.settleMs??1e3);const i=e.frameTrace!==false;const o=(e.cpuSlowdown?.length?e.cpuSlowdown:[1]).map(f=>Math.max(1,f));let c=e.refreshHz??60;const p={width:e.viewport?.width??1280,height:e.viewport?.height??800,deviceScaleFactor:e.viewport?.deviceScaleFactor??1};const h=[];const u=[];let l=null;if(a<2)h.push("runsPerCase=1 gives no spread; use 3 or more before choosing.");const d=await Oe(e.projectRoot??process.cwd());const M=await d.launch({headless:!e.headed,args:["--enable-gpu"]});const S=[];const T=[];const b=Date.now();const A=new Date(b).toISOString();const ee=e.maxBatchMs??30*6e4;let I=false;let se=false;const ae={commit:ft(e.projectRoot??process.cwd()),dirty:ht(e.projectRoot??process.cwd())};try{const f={viewport:{width:p.width,height:p.height},deviceScaleFactor:p.deviceScaleFactor};const $=await G(M.newContext(f),3e4,async()=>{t.browserLeftRunning=true;throw new Error("Chromium didn't open a page in 30 s. Restart the MCP server and try again.")});const R=3e4+15e3+n+s+6e4;const V=async(g,w,v,_)=>{const C={};return G(F(C,g,w,v,_),R,async()=>{C.cancelled=true;if(!C.ctx)I=true;else if(!await U(C.ctx,1e4))I=true;return{...q(g.name,w,v),failed:`run took over ${Math.round(R/1e3)} s (the page may be stuck in a loop)`}})};const q=(g,w,v)=>({caseName:g,cpu:w,round:v,fps:null,frameGapP95Ms:null,frameGapMaxMs:null,longFrames:0,longTaskPct:null,loafBlockingMs:null,styleLayoutMs:null,forcedLayoutMs:null,topScripts:[],layoutShift:null,domNodesStart:null,domNodesEnd:null,mainThreadPct:null,scriptMs:null,styleMs:null,layoutMs:null,layoutCount:null,styleCount:null,heapMb:null,pageErrors:[]});const F=async(g,w,v,_,C)=>{const J=await M.newContext(f);g.ctx=J;if(g.cancelled){await U(J,1e4);throw new Error("run was cancelled during setup")}const y=await J.newPage();const z=[];y.on("pageerror",P=>z.push(String(P?.message??P)));const N={...q(w.name,v,_),pageErrors:z};try{const P=await J.newCDPSession(y);await P.send("Performance.enable",{timeDomain:"timeTicks"});if(v>1)await P.send("Emulation.setCPUThrottlingRate",{rate:v});const oe=await y.goto(Ae(e.url,w,e.path),{waitUntil:"load",timeout:3e4});if(oe&&oe.status()>=400)return{...N,failed:`HTTP ${oe.status()}`};if(e.readySelector)await y.waitForSelector(e.readySelector,{state:"attached",timeout:15e3});await we(n);const Me=C||i;if(Me){await P.send("Tracing.start",{transferMode:"ReturnAsStream",categories:(C?mt:pt).join(",")})}const ie=e.steps?.length?await Ge(y,e.steps):new Map;if(e.steps?.length){const K=await gt(y,e.steps);if(K)return{...N,failed:`expect ${K} already held before the steps, so it can't prove they worked`};const Q=[...ie.keys()];const j=e.steps.flatMap(X=>"expect"in X?[X.expect]:[]);await y.evaluate(`window.__buoyBenchClickTargets = ${JSON.stringify(Q)}; window.__buoyBenchExpects = ${JSON.stringify(j)}; true`)}const le=await We(P);await y.evaluate(ot);const be=Date.now();if(e.steps?.length)await wt(y,e.steps,ie);const $e=s-(Date.now()-be);if($e>0)await we($e);await y.evaluate(it);const ce=await We(P);const Xe=Date.now()-be;const O=await y.evaluate(lt);let D=null;if(Me){const K=new Promise(B=>P.on("Tracing.tracingComplete",ne=>B(ne.stream)));await P.send("Tracing.end");const Q=await K;const j=C?xe(C,"w"):null;const X=[];let ue=0;try{for(;;){const B=await P.send("IO.read",{handle:Q});const ne=B.base64Encoded?Buffer.from(B.data,"base64").toString("utf8"):B.data;ue+=ne.length;if(j!=null)st(j,ne);if(ue<=me)X.push(ne);else X.length=0;if(B.eof)break}}finally{await P.send("IO.close",{handle:Q}).catch(()=>{});if(j!=null)ke(j)}if(C)u.push(C);if(ue>me){h.push(`Trace for ${w.name} was over ${Math.round(me/1e6)} MB, so its frames weren't counted. A page that busy shows its problem in the table already.`)}else{try{const B=JSON.parse(X.join(""));D=Le(Array.isArray(B)?B:B.traceEvents??[])}catch{D=null}}}if(!O)return{...N,failed:"probe returned nothing (page navigated away?)"};if(O.hidden)return{...N,failed:"page was hidden during the run"};if(O.resized)return{...N,failed:"the window size or zoom changed during the run"};if(z.length&&!e.allowPageErrors){return{...N,failed:`page error: ${re(z[0])}`}}if(e.steps?.length){const K=Ue(e.steps,O.clicks??[],O.keys??[]);if(K)return{...N,failed:K};const Q=He(O.expectResults??[]);if(Q)return{...N,failed:Q};const j=await yt(y,ie);if(j)return{...N,failed:j}}const Ye="Timestamp"in le&&"Timestamp"in ce?x((ce.Timestamp-le.Timestamp)*1e3):null;return{...N,...De(O,c),...Be(le,ce,Xe),probeSpanMs:x(O.end-O.start),cdpSpanMs:Ye,shownFps:D?.shownFps??null,droppedFrames:D?.faults??null,frameCounts:D?{full:D.full,partial:D.partial,dropped:D.dropped,faults:D.faults}:null,droppedPct:D?.droppedPct??null}}catch(P){return{...N,failed:re(P?.message??String(P))}}finally{await U(y,5e3);if(!await U(J,1e4))I=true}};{const g=await G($.newPage(),15e3,async()=>{throw new Error("Chromium didn't open the GPU check page in 15 s.")});try{l=await G(g.evaluate(Re),5e3,async()=>null)??null}catch{l=null}finally{await U(g,5e3)}if(l)r(`GPU ${l}`);if(l&&fe(l)){h.push(`Chrome is drawing with a software GPU (${l}). Canvas, WebGL and Skia pages run several times slower than on a real screen, so their numbers mean little. Try headed: true, or a machine with a GPU.`)}}if(e.refreshHz==null){const g=await G($.newPage(),15e3,async()=>{throw new Error("Chromium didn't open the calibration page in 15 s.")});try{await G(g.goto("about:blank"),15e3,async()=>null);const w=await G(g.evaluate(ct),5e3,async()=>{throw new Error("idle rate check timed out")});c=Ce(w);r(`idle frame rate ${w.toFixed(1)} \u2192 ${c} Hz`)}catch{h.push("Couldn't measure the idle frame rate; frame budgets assume 60 Hz.")}finally{await U(g,5e3)}}r("warmup load");await V(e.cases[0],1,-1);const H=async(g,w,v,_)=>{if(I){S.push({...q(g.name,w,v),failed:"an earlier run left the browser stuck; batch stopped"});return}if(Date.now()-b>ee){S.push({...q(g.name,w,v),failed:"batch time limit reached; run skipped"});return}r(_);T.push({cpu:w,round:v,caseName:g.name});S.push(await V(g,w,v))};for(const g of o){for(let w=0;w<a;w++){const v=he(e.cases,2654435769^w*7919+g);for(const _ of v)await H(_,g,w,`cpu ${g}x \xB7 round ${w+1}/${a} \xB7 ${_.name}`)}for(let w=0;w<(e.extraRoundsWhenClose??0);w++){const v=e.cases.map(y=>pe(S.filter(z=>z.cpu===g&&z.caseName===y.name),c));for(const y of v)y.control=e.cases.find(z=>z.name===y.caseName)?.control===true;const _=new Set([...ye(v).flat(),...v.filter(y=>!y.control&&y.validRuns<2&&y.plannedRuns>0).map(y=>y.caseName)]);if(_.size===0)break;const C=a+w;const J=he(e.cases.filter(y=>_.has(y.name)),20973^C*7919+g);for(const y of J)await H(y,g,C,`cpu ${g}x \xB7 extra round ${w+1} (close call) \xB7 ${y.name}`)}}if(e.traceDir){et(e.traceDir,{recursive:true});for(const[g,w]of e.cases.entries()){const v=de(e.traceDir,`${String(g+1).padStart(2,"0")}-${w.name.replace(/[^\w.-]+/g,"_")}.json`);if(I||Date.now()-b>ee){h.push(`Trace run for ${w.name} skipped (batch time limit or a stuck browser).`);continue}r(`trace \xB7 ${w.name}`);const _=await V(w,o[0],-1,v);if(_.failed)h.push(`Trace run for ${w.name} failed: ${_.failed}`)}}await U($,1e4)}finally{const f=await U(M,15e3);t.browserLeftRunning=!f;if(!f){se=true;h.push("Chromium didn't close in 15 s and may still be running. The benchmark lock stays held; restart the MCP server before the next batch.")}}const m=[];for(const f of o){for(const $ of e.cases){m.push(pe(S.filter(R=>R.cpu===f&&R.caseName===$.name),c))}}for(const f of m)f.control=e.cases.find($=>$.name===f.caseName)?.control===true;for(const f of o){const $=m.filter(F=>F.cpu===f);const R=$.find(F=>!F.control);if(!R)continue;R.isBaseline=true;const V=F=>F.droppedMin!=null?F.median?.droppedPct??null:null;const q=V(R);for(const F of $.filter(H=>H!==R&&!H.control)){const H=V(F);F.deltaDroppedPct=q!=null&&H!=null?x(H-q):null}}if(e.allowPageErrors&&S.some(f=>f.pageErrors.length)){h.push("Some runs threw page errors (kept because allowPageErrors is on).")}if(I)h.push("A browser context wouldn't close, so the batch stopped early. Restart and run again.");if(e.budgets)for(const f of m)f.budget=Pe(f,e.budgets,e.steps??[]);const k=m.filter(f=>f.median).length;const W=m.every(f=>f.validRuns===f.plannedRuns);const te=k===0?"failed":k<m.length||!W?"partial":"ok";const E=m.filter(f=>f.median&&f.validRuns>0&&f.validRuns<f.plannedRuns);for(const f of E){const $=Array.from(new Set(f.runs.filter(R=>R.failed).map(R=>R.failed)));h.push(`${f.caseName} (CPU ${f.cpu}x): ${f.plannedRuns-f.validRuns} of ${f.plannedRuns} runs failed: ${$.join("; ")}`)}for(const f of m){const $=f.median;if($?.probeSpanMs&&$.cdpSpanMs&&Math.abs($.cdpSpanMs-$.probeSpanMs)/$.probeSpanMs>.15){h.push(`${f.caseName} (CPU ${f.cpu}x): Chrome's counters cover ${Math.round($.cdpSpanMs)} ms but frames cover ${Math.round($.probeSpanMs)} ms, so its cost columns include extra time.`)}if($?.probeSpanMs!=null&&$.probeSpanMs<s*.8){h.push(`${f.caseName} (CPU ${f.cpu}x) recorded ${Math.round($.probeSpanMs)} ms, not the ${s} ms asked for.`)}}const L={status:te,hasSteps:Boolean(e.steps?.length),browserLeftRunning:se,replay:{startedAt:A,...ae,runOrder:T},url:e.url,browser:`Chromium ${M.version()}${e.headed?"":" (headless)"}`,gpu:l,viewport:p,refreshHz:c,durationMs:s,runsPerCase:a,results:m,traces:u,warnings:h};if(e.outFile){try{Te(e.outFile,JSON.stringify({options:e,result:L},null,2))}catch(f){L.warnings.push(`Couldn't write outFile ${e.outFile}: ${re(f?.message??String(f))}`)}}return L}var Ke=1.5;var Qe=2;function ye(e){const r=e.filter(n=>!n.control&&n.median&&n.validRuns>1);const t=r.filter(n=>n.droppedMin!=null);const a=[];if(t.length>=2&&t.length===r.length){const n=[...t].sort((i,o)=>i.median.droppedPct-o.median.droppedPct);for(let i=1;i<n.length;i++){const[o,c]=[n[i-1],n[i]];const p=c.median.droppedPct-o.median.droppedPct<=Ke;if(c.droppedMin<=o.droppedMax||p)a.push([o.caseName,c.caseName])}return a}if(t.length>0)return a;const s=r.filter(n=>n.median.fps!=null).sort((n,i)=>i.median.fps-n.median.fps);for(let n=1;n<s.length;n++){const[i,o]=[s[n-1],s[n]];const c=i.median.fps-o.median.fps<=Qe;if((o.fpsMax??0)>=(i.fpsMin??0)||c)a.push([i.caseName,o.caseName])}return a}function Ve(e){const r=e.filter(o=>!o.control&&o.median);const t=[];const a=r.filter(o=>o.validRuns<2);if(a.length)t.push(`**Too few runs to rank:** ${a.map(o=>o.caseName).join(", ")} (need 2 or more valid runs).`);const s=r.filter(o=>o.validRuns>1);const n=s.filter(o=>o.droppedMin!=null);if(n.length>0&&n.length<s.length){t.push(`**Not enough traced runs to compare smoothness:** ${s.filter(o=>!n.includes(o)).map(o=>`${o.caseName} (${o.tracedRuns}/${o.validRuns} traced)`).join(", ")}.`);return t.join("\n")}const i=new Map(s.map(o=>[o.caseName,o]));for(const[o,c]of ye(e)){const p=i.get(o);const h=i.get(c);t.push(n.length?`**Too close to call:** ${o} (${p.droppedMin}\u2013${p.droppedMax}% dropped) and ${c} (${h.droppedMin}\u2013${h.droppedMax}%) overlap or sit within ${Ke} pp. No clear winner between them; more runs may separate them.`:`**Too close to call:** ${o} and ${c} overlap or sit within ${Qe} FPS. No clear winner between them; more runs may separate them.`)}return t.length?t.join("\n"):null}function $t(e){const r=(o,c=1)=>o==null?"\u2014":o.toFixed(c);const t=[];t.push(`### Web benchmark \xB7 ${e.url}`);t.push(`${e.browser}${e.gpu?` \xB7 GPU: ${e.gpu}${fe(e.gpu)?" \u26A0\uFE0F software":""}`:""} \xB7 ${e.viewport.width}\xD7${e.viewport.height} @${e.viewport.deviceScaleFactor}x \xB7 ${e.runsPerCase} runs${e.results.some(o=>o.plannedRuns>e.runsPerCase)?" (more for close calls)":""} \xD7 ${e.durationMs/1e3}s per case, fresh browser context each run, case order shuffled each round.`);const a=e.results.map(o=>o.median?.probeSpanMs).filter(o=>o!=null);const s=e.results.map(o=>o.median?.cdpSpanMs).filter(o=>o!=null);const n=o=>`${(Math.min(...o)/1e3).toFixed(2)}\u2013${(Math.max(...o)/1e3).toFixed(2)} s`;if(a.length){t.push(`Recorded: frames over ${n(a)}${s.length?`; cost columns (Chrome's counters) over ${n(s)}`:""}, not the requested time.`)}if(e.replay?.dirty)t.push(`Note: the project had uncommitted changes, so commit ${e.replay.commit?.slice(0,10)??"?"} alone doesn't describe the code that ran.`);t.push("");t.push(`**How to read this.** Dropped % is the smoothness number: frames Chrome dropped or only partly produced that it says hurt smoothness, as a share of the frames it wanted, from its own verdicts in a trace. Rank on it first, and only across variants doing the same work. Shown FPS counts frames Chrome produced (headless Chrome displays nothing), so an animation that changes less often needs fewer; don't compare it across variants that update at different rates. Main-thread FPS and frame gaps come from requestAnimationFrame. Main thread % is Chrome's task time for the run. Desktop numbers, even with CPU slowdown, don't stand in for a real phone's GPU, memory or heat.`);const i=Array.from(new Set(e.results.map(o=>o.cpu)));for(const o of i){t.push("");t.push(o===1?`#### No CPU slowdown`:`#### CPU slowed ${o}x`);t.push("");t.push(`| Case | Runs | Shown FPS | Dropped % | Main-thread FPS (range) | p95 gap ms | Worst gap ms | Main thread % | Script ms | Style ms | Layout ms | Layouts | DOM nodes | JS heap MB | Flags |`);t.push(`|------|-----:|----------:|----------:|------------------------:|-----------:|-------------:|--------------:|----------:|---------:|----------:|--------:|----------:|--------:|-------|`);for(const l of e.results.filter(d=>d.cpu===o)){const d=l.median;if(!d){const T=l.runs.find(b=>b.failed)?.failed??"no valid runs";t.push(`| ${l.caseName} | 0/${l.plannedRuns} | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u274C FAILED (${T}) |`);continue}const M=l.fpsMin!=null&&l.fpsMax!=null&&l.validRuns>1?` (${r(l.fpsMin)}\u2013${r(l.fpsMax)})`:"";const S=l.flags.length?`\u26A0\uFE0F ${l.flags.join("; ")}`:"no flags";t.push(`| ${l.isBaseline?`${l.caseName} (baseline)`:l.control?`${l.caseName} (control)`:l.caseName} | ${l.validRuns}/${l.plannedRuns} | ${r(d.shownFps)} | ${l.droppedMin==null&&l.tracedRuns>0?`\u2014 (${l.tracedRuns}/${l.validRuns} traced)`:r(l.droppedMin==null?null:d.droppedPct)}${l.droppedMin!=null&&l.validRuns>1?` [${l.droppedMin}\u2013${l.droppedMax}]`:""}${l.deltaDroppedPct!=null?` (${l.deltaDroppedPct>0?"+":""}${l.deltaDroppedPct} pp)`:""} | ${r(d.fps)}${M} | ${r(d.frameGapP95Ms)} | ${r(d.frameGapMaxMs)} | ${r(d.mainThreadPct)} | ${r(d.scriptMs,0)} | ${r(d.styleMs,0)} | ${r(d.layoutMs,0)} | ${r(d.layoutCount,0)} | ${r(d.domNodesEnd,0)} | ${r(d.heapMb)} | ${S} |`)}const c=Ve(e.results.filter(l=>l.cpu===o));if(c){t.push("");t.push(c)}const p=e.results.filter(l=>l.cpu===o&&l.budget);if(p.length){t.push("");t.push(`Budget:`);for(const l of p){const d=l.budget;const M=[...d.over,...d.unknown.map(T=>`${T} not measured`)].join("; ");const S=l.control?`${l.caseName} (control)`:l.caseName;t.push(`- **${S}**: ${d.meets?"meets every limit":`misses (${M||"no result"})`}`)}}const h=e.results.filter(l=>l.cpu===o&&l.median?.interactions!=null&&(e.hasSteps||l.median.interactions>0)).map(l=>{const d=l.median;const M=l.inputMin!=null&&l.inputMax!=null?` \xB7 slowest across all runs ${l.inputMax} ms (range ${l.inputMin}\u2013${l.inputMax})`:"";const S=l.runs.filter(b=>!b.failed&&b.fps!=null&&b.inputCaptureComplete===false).length;const T=S?` \xB7 \u26A0\uFE0F timing incomplete in ${S} run(s): the last input's paint came after the run ended, so its time is unknown (leave more time after the last step)`:"";if(!d.interactions)return`- **${l.caseName}**: middle run had no input entry (inputs under 16 ms aren't reported)${M}${T}`;return`- **${l.caseName}**: middle run: ${d.interactions} reported (16 ms or more), slowest ${d.worstInteractionMs} ms (waiting ${d.worstInputDelayMs} \xB7 handlers ${d.worstProcessingMs} \xB7 to paint ${d.worstPresentationMs})${M}${T}`});if(h.length){t.push("");t.push(`Input response (scripted steps, Event Timing). Inputs under 16 ms aren't reported. Under 200 ms is good.`);t.push(...h)}const u=e.results.filter(l=>l.cpu===o&&l.median&&l.median.topScripts.length).map(l=>`- **${l.caseName}**: ${l.median.topScripts.slice(0,3).map(d=>`${d.name} ${d.ms} ms`).join(", ")}`);if(u.length){t.push("");t.push(`Scripts in slow frames (Long Animation Frames; frames that ran past the end of a run are left out):`);t.push(...u)}}if(e.traces.length){t.push("");t.push(`Chrome traces (open in DevTools \u2192 Performance \u2192 Load profile): ${e.traces.join(", ")}`)}if(e.warnings.length){t.push("");t.push(...e.warnings.map(o=>`\u26A0\uFE0F ${o}`))}return t.join("\n")}export{Pe as checkBudgets,Re as GPU_RENDERER_JS,fe as isSoftwareGpu,_e as percentile,Ce as snapRefreshRate,he as shuffled,Ae as caseUrl,Le as summarizeFrameTrace,Fe as keyMatches,Ne as mergeInteractions,De as summarizeProbe,Be as cdpDeltas,Ie as webBenchFlags,pe as aggregateCase,ze as PLAYWRIGHT_MISSING,Oe as loadChromium,G as withDeadline,U as closeWithin,je as validateWebBenchOptions,re as oneLine,Ge as locateStepTargets,Ue as missedInput,He as expectFailureFrom,Mt as runWebBench,qe as waitForHostLock,Je as takeHostLock,ye as tiedPairs,Ve as closeCall,$t as formatWebBenchMarkdown};
145
+ })()`;function fe(e){return/swiftshader|llvmpipe|softpipe|software|microsoft basic render/i.test(e)}function _e(e,r){if(e.length===0)return null;const t=[...e].sort((s,n)=>s-n);const a=Math.ceil(r/100*t.length)-1;return t[Math.min(t.length-1,Math.max(0,a))]}var dt=[30,60,75,90,120,144,165,240];function Ce(e){for(const r of dt)if(Math.abs(e-r)/r<=.08)return r;return Math.max(1,Math.round(e))}function he(e,r){let t=r>>>0;const a=()=>{t=t+1831565813>>>0;let n=t;n=Math.imul(n^n>>>15,n|1);n^=n+Math.imul(n^n>>>7,n|61);return((n^n>>>14)>>>0)/4294967296};const s=[...e];for(let n=s.length-1;n>0;n--){const i=Math.floor(a()*(n+1));[s[n],s[i]]=[s[i],s[n]]}return s}function Ae(e,r,t){const a=new URL(e);const s=r.path??t;if(s)a.pathname=s.startsWith("/")?s:`${a.pathname.replace(/\/$/,"")}/${s}`;for(const[n,i]of Object.entries(r.params??{}))a.searchParams.set(n,i);return a.toString()}var ft=["STATE_PRESENTED_ALL","STATE_PRESENTED_PARTIAL","STATE_DROPPED"];function Le(e){const r=d=>e.find(M=>M.name===d&&M.ts!=null);const t=r("__buoy_bench_start");const a=r("__buoy_bench_end");if(!t||!a||a.ts<=t.ts)return null;if(t.pid!==a.pid)return null;const s=t.pid;const n={STATE_PRESENTED_ALL:0,STATE_PRESENTED_PARTIAL:1,STATE_DROPPED:2};const i=new Map;for(const d of e){if(d.name!=="PipelineReporter"||d.ph!=="b")continue;if(s!=null&&d.pid!==s)continue;if(d.ts==null||d.ts<t.ts||d.ts>a.ts)continue;const M=d.args?.frame_reporter;if(!M||M.frame_sequence==null)continue;const S=M.state;if(!ft.includes(S))continue;const T=`${M.layer_tree_host_id??""}:${M.frame_source??""}:${M.frame_sequence}`;const b=Boolean(M.affects_smoothness)&&S!=="STATE_PRESENTED_ALL";const A=i.get(T);if(!A)i.set(T,{state:S,fault:b});else{if(n[S]>n[A.state])A.state=S;A.fault=A.fault||b}}let o=0;let c=0;let p=0;let h=0;for(const d of i.values()){if(d.state==="STATE_PRESENTED_ALL")o++;else if(d.state==="STATE_PRESENTED_PARTIAL")c++;else p++;if(d.fault)h++}const u=o+c+p;if(u===0)return null;const l=(a.ts-t.ts)/1e6;return{full:o,partial:c,dropped:p,faults:h,shownFps:x((o+c)/l),droppedPct:x(h/u*100)}}function Fe(e,r){const t=e.split("+");const a=t[t.length-1];const s=new Set(t.slice(0,-1).map(o=>o.toLowerCase()));if(r.key!==a)return false;const n=a.toLowerCase();const i={ctrl:s.has("control")||n==="control",alt:s.has("alt")||n==="alt",meta:s.has("meta")||s.has("command")||n==="meta",shift:s.has("shift")||n==="shift"};if(Boolean(r.ctrl)!==i.ctrl||Boolean(r.alt)!==i.alt||Boolean(r.meta)!==i.meta)return false;if(!i.shift&&r.shift&&a.length!==1)return false;if(i.shift&&!r.shift)return false;return true}function Ne(e){const r=[];for(const t of e){const a=t.startTime+t.duration;const s=r.find(i=>i.interactionId===t.interactionId&&Math.abs(i.startTime+i.duration-a)<=8);if(!s){r.push({...t});continue}const n=s.startTime+s.duration;s.startTime=Math.min(s.startTime,t.startTime);s.processingStart=Math.min(s.processingStart,t.processingStart);s.processingEnd=Math.max(s.processingEnd,t.processingEnd);s.duration=Math.max(n,a)-s.startTime;if(t.processingEnd-t.processingStart>0)s.name=t.name}return r}function De(e,r){const{start:t,end:a}=e;const s=Math.max(1,a-t);const n=[];for(let m=1;m<e.frames.length;m++)n.push(e.frames[m]-e.frames[m-1]);const i=1e3/r;const o=_e(n,95);let c=null;if(e.longTasks){let m=0;for(const k of e.longTasks){const q=Math.min(k.end,a)-Math.max(k.start,t);if(q>0)m+=q}c=x(Math.min(100,m/s*100))}let p=null;let h=null;let u=null;let l=[];let d=0;if(e.loaf){let m=0;let k=0;let q=0;const te=new Map;for(const E of e.loaf){if(E.startTime>=t&&E.startTime<=a&&E.startTime+E.duration>a)d++;if(E.startTime<t||E.startTime+E.duration>a)continue;m+=E.blockingDuration;if(E.styleAndLayoutStart>E.startTime)k+=Math.max(0,E.startTime+E.duration-E.styleAndLayoutStart);for(const L of E.scripts){q+=L.forced;const f=te.get(L.name)??{ms:0,count:0};f.ms+=L.duration;f.count++;te.set(L.name,f)}}p=x(m);h=x(k);u=x(q);l=[...te.entries()].map(([E,L])=>({name:E,ms:x(L.ms),count:L.count})).sort((E,L)=>L.ms-E.ms).slice(0,5)}let M=null;if(e.shifts){let m=0;for(const k of e.shifts)if(!k.hadRecentInput&&k.startTime>=t&&k.startTime<=a)m+=k.value;M=Math.round(m*1e3)/1e3}let S=null;let T=null;if(e.events){const m=Ne(e.events.filter(k=>k.startTime>=t&&k.startTime<=a));S=new Set(m.map(k=>k.interactionId)).size;for(const k of m)if(!T||k.duration>T.duration)T=k}const b=T;const A=e.frames;const ee=[...(e.clicks??[]).map(m=>m.t),...(e.keys??[]).map(m=>m.t)];const I=ee.length?Math.max(...ee):null;const se=I==null||A.filter(m=>m>I).length>=2;const ae=A.length===0?s:Math.max(0,A[0]-t,a-A[A.length-1]);return{edgeSilenceMs:x(ae),frameGapCount:n.length,inputCaptureComplete:se,resized:Boolean(e.resized),interactions:S,worstInteractionMs:b?x(b.duration):null,worstInputDelayMs:b?x(Math.max(0,b.processingStart-b.startTime)):null,worstProcessingMs:b?x(Math.max(0,b.processingEnd-b.processingStart)):null,worstPresentationMs:b?x(Math.max(0,b.startTime+b.duration-b.processingEnd)):null,fps:x(e.frames.length/s*1e3),frameGapP95Ms:o==null?null:x(o),frameGapMaxMs:n.length?x(Math.max(...n)):null,longFrames:n.filter(m=>m>i*1.5).length,longTaskPct:c,loafBlockingMs:p,styleLayoutMs:h,forcedLayoutMs:u,topScripts:l,loafCutOff:d,layoutShift:M,domNodesStart:e.domStart,domNodesEnd:e.domEnd}}function Be(e,r,t){const a=o=>o in e&&o in r?r[o]-e[o]:null;const s=o=>{const c=a(o);return c==null?null:x(c*1e3)};const n=s("TaskDuration");const i=s("Timestamp")??t;return{mainThreadPct:n==null?null:x(n/Math.max(1,i)*100),scriptMs:s("ScriptDuration"),styleMs:s("RecalcStyleDuration"),layoutMs:s("LayoutDuration"),layoutCount:a("LayoutCount"),styleCount:a("RecalcStyleCount"),heapMb:"JSHeapUsedSize"in r?x(r.JSHeapUsedSize/(1024*1024)):null}}function Ie(e,r){const t=[];if(e.droppedPct!=null&&e.droppedPct>=5)t.push(`${e.droppedPct}% of frames dropped or partial`);if(e.fps!=null&&e.fps<r*.95)t.push("Low main-thread frame rate");if(e.frameGapCount&&e.longFrames/e.frameGapCount>=.02)t.push("Frequent long frames");const a=Math.max(e.frameGapMaxMs??0,e.edgeSilenceMs??0);if(a>=100)t.push(`Freeze of ${Math.round(a)} ms`);if(e.mainThreadPct!=null&&e.mainThreadPct>=80)t.push("Main thread near full");if(e.longTaskPct!=null&&e.longTaskPct>=20)t.push("Long tasks block the main thread");if(e.forcedLayoutMs!=null&&e.forcedLayoutMs>=50)t.push(`Forced layout in scripts (${Math.round(e.forcedLayoutMs)} ms)`);if(e.layoutShift!=null&&e.layoutShift>=.1)t.push(`Layout shifts (${e.layoutShift})`);if(e.worstInteractionMs!=null&&e.worstInteractionMs>=200){t.push(`Slow response to input (${Math.round(e.worstInteractionMs)} ms)`)}return t}function pe(e,r){const t=e.filter(c=>!c.failed&&c.fps!=null);const a=[...t].sort((c,p)=>(c.fps??0)-(p.fps??0));const s=t.filter(c=>c.droppedPct!=null).length;const n=t.length>0&&s===t.length;const i=n?[...t].sort((c,p)=>c.droppedPct-p.droppedPct):a;const o=i.length?i[Math.floor((i.length-1)/2)]:null;return{caseName:e[0]?.caseName??"",cpu:e[0]?.cpu??1,validRuns:t.length,plannedRuns:e.length,isBaseline:false,deltaDroppedPct:null,tracedRuns:s,...(()=>{const c=n?t.map(h=>h.droppedPct):[];const p=t.map(h=>h.worstInteractionMs).filter(h=>h!=null);return{droppedMin:c.length?Math.min(...c):null,droppedMax:c.length?Math.max(...c):null,inputMin:p.length?Math.min(...p):null,inputMax:p.length?Math.max(...p):null}})(),runs:e,median:o,fpsMin:a.length?a[0].fps:null,fpsMax:a.length?a[a.length-1].fps:null,flags:o?Ie(n?o:{...o,droppedPct:null},r):[]}}var ze="Playwright isn't installed in this project. Install it, then run again:\n npm i -D playwright && npx playwright install chromium";async function Oe(e){const r=et(de(e,"noop.js"));for(const t of["playwright","playwright-core"]){let a;try{a=r.resolve(t)}catch{continue}const s=await import(ot(a).href);const n=s.chromium??s.default?.chromium;if(n)return n}throw new Error(`${ze}
146
+ (Searched from ${e}. Pass projectRoot to look elsewhere.)`)}async function G(e,r,t){let a;const s=new Promise(n=>{a=setTimeout(()=>n("timeout"),r)});try{const n=await Promise.race([e,s]);if(n==="timeout"){e.catch(()=>{});return await t()}return n}finally{clearTimeout(a)}}function ht(e){try{return Se("git",["rev-parse","HEAD"],{cwd:e,stdio:["ignore","pipe","ignore"],timeout:3e3}).toString().trim()}catch{return null}}function pt(e){try{const r=Se("git",["status","--porcelain"],{cwd:e,stdio:["ignore","pipe","ignore"],timeout:5e3});return r.toString().trim().length>0}catch{return null}}async function U(e,r){if(!e)return true;return G(e.close().then(()=>true,()=>false),r,async()=>false)}function je(e){if(!e.cases?.length)throw new Error("Give at least one case.");const r=new Set;for(const i of e.cases){if(!i.name?.trim())throw new Error("Every case needs a name.");if(r.has(i.name))throw new Error(`Two cases are named "${i.name}". Give each case its own name.`);r.add(i.name)}const t=(i,o,c,p)=>{if(i==null)return;if(!Number.isInteger(i)||i<c||i>p)throw new Error(`${o} must be a whole number from ${c} to ${p}.`)};const a=(i,o,c)=>{if(i==null)return;if(!Number.isFinite(i)||i<c)throw new Error(`${o} must be a number of at least ${c}.`)};const s=(i,o,c,p)=>{if(i==null)return;if(!Number.isFinite(i)||i<c||i>p)throw new Error(`${o} must be from ${c} to ${p}.`)};t(e.runsPerCase,"runsPerCase",1,10);s(e.durationMs,"durationMs",500,12e4);s(e.settleMs,"settleMs",0,6e4);s(e.refreshHz,"refreshHz",1,500);const n=e.cpuSlowdown??[];for(const i of n)s(i,"each cpuSlowdown rate",1,20);if(new Set(n).size!==n.length)throw new Error("cpuSlowdown has the same rate twice.");if(e.viewport){t(e.viewport.width,"viewport.width",200,8e3);t(e.viewport.height,"viewport.height",200,8e3);s(e.viewport.deviceScaleFactor,"viewport.deviceScaleFactor",.5,4)}t(e.extraRoundsWhenClose,"extraRoundsWhenClose",0,10);s(e.maxBatchMs,"maxBatchMs",1e4,6*60*6e4);for(const[i,o]of Object.entries(e.budgets??{}))s(o,`budgets.${i}`,0,1e9);for(const i of e.steps??[]){if("wait"in i)s(i.wait,"a wait step",0,6e4);if("scroll"in i)s(i.scroll,"a scroll step",-1e5,1e5)}new URL(e.url)}function re(e){return e.replace(/\u001b\[[0-9;]*m/g,"").split("\n")[0].trim().slice(0,160)}var mt=["disabled-by-default-devtools.timeline.frame","blink.user_timing"];var wt=["devtools.timeline","disabled-by-default-devtools.timeline","disabled-by-default-devtools.timeline.frame","blink.user_timing","loading","latencyInfo","v8.execute"];var me=15e7;var we=e=>new Promise(r=>setTimeout(r,e));async function Ge(e,r){const t=new Map;for(const a of r){if("expect"in a)continue;const s="click"in a?a.click:"type"in a?a.type.selector:null;if(!s||t.has(s))continue;const n=await e.$(s);const i=n?await n.boundingBox():null;if(!i)throw new Error(`step target ${s} isn't on the page (or isn't visible)`);t.set(s,{x:i.x+i.width/2,y:i.y+i.height/2})}return t}async function gt(e,r,t){for(const a of r){if("click"in a){const s=t.get(a.click);await e.mouse.click(s.x,s.y)}else if("press"in a)await e.keyboard.press(a.press);else if("type"in a){const s=t.get(a.type.selector);await e.mouse.click(s.x,s.y);await e.keyboard.type(a.type.text)}else if("scroll"in a)await e.mouse.wheel(0,a.scroll);else if("wait"in a)await we(a.wait)}}function Ue(e,r,t=[]){const a=[...r].sort((h,u)=>h.t-u.t);const s=[...t].sort((h,u)=>h.t-u.t);let n=0;let i=0;let o=-Infinity;const c=h=>{while(n<a.length){const u=a[n++];if(u.t>=o&&u.hit.includes(h)){o=u.t;return true}}return false};const p=h=>{while(i<s.length){const u=s[i++];if(u.t>=o&&Fe(h,u)){o=u.t;return true}}return false};for(const[h,u]of e.entries()){if("click"in u&&!c(u.click)){return`step ${h+1} (click ${u.click}) has no real click on its target, in order (moved, covered, or ignored)`}if("type"in u){if(!c(u.type.selector))return`step ${h+1} (type into ${u.type.selector}) has no real click on its target`;for(const l of u.type.text){if(!p(l))return`step ${h+1} (type "${u.type.text}") didn't deliver the key "${l}"`}}if("press"in u&&!p(u.press))return`step ${h+1} (press ${u.press}) didn't deliver that key`}return null}async function yt(e,r){for(const t of r){if(!("expect"in t))continue;const{selector:a,text:s}=t.expect;const n=await e.evaluate(`(() => { const n = document.querySelector(${JSON.stringify(a)}); if (!n) return false; ${s==null?"return true;":`return (n.textContent || "").includes(${JSON.stringify(s)});`} })()`);if(n)return s==null?a:`${a} contains "${s}"`}return null}async function Mt(e,r){for(const[t,a]of r){const s=await e.$(t);const n=s?await s.boundingBox():null;if(!n)return`step target ${t} was gone after the run`;const i=n.x+n.width/2;const o=n.y+n.height/2;if(Math.abs(i-a.x)>4||Math.abs(o-a.y)>4){return`step target ${t} moved during the run, so a click may have missed it`}}return null}function He(e){for(const r of e){if(!r.found)return`expected ${r.selector} at the end of the run, but it isn't there`;const t=r.matched??(r.text==null||(r.content??"").includes(r.text));if(!t){return`expected ${r.selector} to contain "${r.text}" at the end of the run, got "${(r.content??"").slice(0,60)}"`}}return null}async function qe(e){const{metrics:r}=await e.send("Performance.getMetrics");return Object.fromEntries(r.map(t=>[t.name,t.value]))}async function bt(e,r=()=>{}){je(e);const t=await We(e.lockWaitMs??20*6e4,r);const a={browserLeftRunning:false};try{return await $t(e,r,a)}finally{t(a.browserLeftRunning)}}async function We(e,r=()=>{},t=2e3){const a=Date.now()+Math.max(0,e);let s=false;for(;;){try{return Je()}catch(n){const i=n?.message??String(n);const o=/Another web benchmark|already running/.test(i);if(!o||Date.now()+t>a)throw n;if(!s){r("another web benchmark is running; waiting for it to finish");s=true}await new Promise(c=>setTimeout(c,t))}}}var Z=de(at(),"buoy-web-bench.lock");var ge=false;function Je(){if(ge){throw new Error("A web benchmark is already running in this process (or left a stuck browser). Wait, or restart the MCP server.")}const e=`${process.pid}:${Date.now()}:${Math.random().toString(36).slice(2)}`;const r=JSON.stringify({pid:process.pid,token:e,startedAt:new Date().toISOString()});const t=()=>{const n=`${Z}.${e.replace(/[^\w]/g,"")}.tmp`;try{Te(n,r);ve(n,Z);return true}catch{return false}finally{try{Y(n)}catch{}}};const a=()=>{try{return JSON.parse(nt(Z,"utf8"))}catch{return null}};const s=n=>{if(!n)return false;try{process.kill(n,0);return true}catch{return false}};if(!t()){const n=a();if(!n)throw new Error("Another web benchmark is starting. Wait for it to finish.");if(s(n.pid)){throw new Error(`Another web benchmark is running (pid ${n.pid}, since ${n.startedAt}). Two at once would skew both; wait for it to finish.`)}const i=`${Z}.cleanup`;let o=null;try{if(Date.now()-Ee(i).mtimeMs>3e4){const c=`${i}.${e.replace(/[^\w]/g,"")}.old`;st(i,c);if(Date.now()-Ee(c).mtimeMs>3e4)Y(c);else{try{ve(c,i)}catch{}Y(c)}}}catch{}try{o=xe(i,"wx")}catch{throw new Error("Another web benchmark is clearing an old lock. Try again in a moment.")}try{const c=a();if(c?.token===n.token)Y(Z);if(!t())throw new Error("Another web benchmark just started. Wait for it to finish.")}finally{ke(o);try{Y(i)}catch{}}}ge=true;return(n=false)=>{if(n)return;ge=false;if(a()?.token===e){try{Y(Z)}catch{}}}}async function $t(e,r,t){const a=e.runsPerCase??3;const s=Math.max(500,e.durationMs??5e3);const n=Math.max(0,e.settleMs??1e3);const i=e.frameTrace!==false;const o=(e.cpuSlowdown?.length?e.cpuSlowdown:[1]).map(f=>Math.max(1,f));let c=e.refreshHz??60;const p={width:e.viewport?.width??1280,height:e.viewport?.height??800,deviceScaleFactor:e.viewport?.deviceScaleFactor??1};const h=[];const u=[];let l=null;if(a<2)h.push("runsPerCase=1 gives no spread; use 3 or more before choosing.");const d=await Oe(e.projectRoot??process.cwd());const M=await d.launch({headless:!e.headed,args:["--enable-gpu"]});const S=[];const T=[];const b=Date.now();const A=new Date(b).toISOString();const ee=e.maxBatchMs??30*6e4;let I=false;let se=false;const ae={commit:ht(e.projectRoot??process.cwd()),dirty:pt(e.projectRoot??process.cwd())};try{const f={viewport:{width:p.width,height:p.height},deviceScaleFactor:p.deviceScaleFactor};const $=await G(M.newContext(f),3e4,async()=>{t.browserLeftRunning=true;throw new Error("Chromium didn't open a page in 30 s. Restart the MCP server and try again.")});const R=3e4+15e3+n+s+6e4;const V=async(g,w,v,_)=>{const C={};return G(F(C,g,w,v,_),R,async()=>{C.cancelled=true;if(!C.ctx)I=true;else if(!await U(C.ctx,1e4))I=true;return{...W(g.name,w,v),failed:`run took over ${Math.round(R/1e3)} s (the page may be stuck in a loop)`}})};const W=(g,w,v)=>({caseName:g,cpu:w,round:v,fps:null,frameGapP95Ms:null,frameGapMaxMs:null,longFrames:0,longTaskPct:null,loafBlockingMs:null,styleLayoutMs:null,forcedLayoutMs:null,topScripts:[],layoutShift:null,domNodesStart:null,domNodesEnd:null,mainThreadPct:null,scriptMs:null,styleMs:null,layoutMs:null,layoutCount:null,styleCount:null,heapMb:null,pageErrors:[]});const F=async(g,w,v,_,C)=>{const J=await M.newContext(f);g.ctx=J;if(g.cancelled){await U(J,1e4);throw new Error("run was cancelled during setup")}const y=await J.newPage();const z=[];y.on("pageerror",P=>z.push(String(P?.message??P)));const N={...W(w.name,v,_),pageErrors:z};try{const P=await J.newCDPSession(y);await P.send("Performance.enable",{timeDomain:"timeTicks"});if(v>1)await P.send("Emulation.setCPUThrottlingRate",{rate:v});const oe=await y.goto(Ae(e.url,w,e.path),{waitUntil:"load",timeout:3e4});if(oe&&oe.status()>=400)return{...N,failed:`HTTP ${oe.status()}`};if(e.readySelector)await y.waitForSelector(e.readySelector,{state:"attached",timeout:15e3});await we(n);const Me=C||i;if(Me){await P.send("Tracing.start",{transferMode:"ReturnAsStream",categories:(C?wt:mt).join(",")})}const ie=e.steps?.length?await Ge(y,e.steps):new Map;if(e.steps?.length){const K=await yt(y,e.steps);if(K)return{...N,failed:`expect ${K} already held before the steps, so it can't prove they worked`};const Q=[...ie.keys()];const j=e.steps.flatMap(X=>"expect"in X?[X.expect]:[]);await y.evaluate(`window.__buoyBenchClickTargets = ${JSON.stringify(Q)}; window.__buoyBenchExpects = ${JSON.stringify(j)}; true`)}const le=await qe(P);await y.evaluate(it);const be=Date.now();if(e.steps?.length)await gt(y,e.steps,ie);const $e=s-(Date.now()-be);if($e>0)await we($e);await y.evaluate(lt);const ce=await qe(P);const Xe=Date.now()-be;const O=await y.evaluate(ct);let D=null;if(Me){const K=new Promise(B=>P.on("Tracing.tracingComplete",ne=>B(ne.stream)));await P.send("Tracing.end");const Q=await K;const j=C?xe(C,"w"):null;const X=[];let ue=0;try{for(;;){const B=await P.send("IO.read",{handle:Q});const ne=B.base64Encoded?Buffer.from(B.data,"base64").toString("utf8"):B.data;ue+=ne.length;if(j!=null)rt(j,ne);if(ue<=me)X.push(ne);else X.length=0;if(B.eof)break}}finally{await P.send("IO.close",{handle:Q}).catch(()=>{});if(j!=null)ke(j)}if(C)u.push(C);if(ue>me){h.push(`Trace for ${w.name} was over ${Math.round(me/1e6)} MB, so its frames weren't counted. A page that busy shows its problem in the table already.`)}else{try{const B=JSON.parse(X.join(""));D=Le(Array.isArray(B)?B:B.traceEvents??[])}catch{D=null}}}if(!O)return{...N,failed:"probe returned nothing (page navigated away?)"};if(O.hidden)return{...N,failed:"page was hidden during the run"};if(O.resized)return{...N,failed:"the window size or zoom changed during the run"};if(z.length&&!e.allowPageErrors){return{...N,failed:`page error: ${re(z[0])}`}}if(e.steps?.length){const K=Ue(e.steps,O.clicks??[],O.keys??[]);if(K)return{...N,failed:K};const Q=He(O.expectResults??[]);if(Q)return{...N,failed:Q};const j=await Mt(y,ie);if(j)return{...N,failed:j}}const Ye="Timestamp"in le&&"Timestamp"in ce?x((ce.Timestamp-le.Timestamp)*1e3):null;return{...N,...De(O,c),...Be(le,ce,Xe),probeSpanMs:x(O.end-O.start),cdpSpanMs:Ye,shownFps:D?.shownFps??null,droppedFrames:D?.faults??null,frameCounts:D?{full:D.full,partial:D.partial,dropped:D.dropped,faults:D.faults}:null,droppedPct:D?.droppedPct??null}}catch(P){return{...N,failed:re(P?.message??String(P))}}finally{await U(y,5e3);if(!await U(J,1e4))I=true}};{const g=await G($.newPage(),15e3,async()=>{throw new Error("Chromium didn't open the GPU check page in 15 s.")});try{l=await G(g.evaluate(Re),5e3,async()=>null)??null}catch{l=null}finally{await U(g,5e3)}if(l)r(`GPU ${l}`);if(l&&fe(l)){h.push(`Chrome is drawing with a software GPU (${l}). Canvas, WebGL and Skia pages run several times slower than on a real screen, so their numbers mean little. Try headed: true, or a machine with a GPU.`)}}if(e.refreshHz==null){const g=await G($.newPage(),15e3,async()=>{throw new Error("Chromium didn't open the calibration page in 15 s.")});try{await G(g.goto("about:blank"),15e3,async()=>null);const w=await G(g.evaluate(ut),5e3,async()=>{throw new Error("idle rate check timed out")});c=Ce(w);r(`idle frame rate ${w.toFixed(1)} \u2192 ${c} Hz`)}catch{h.push("Couldn't measure the idle frame rate; frame budgets assume 60 Hz.")}finally{await U(g,5e3)}}r("warmup load");await V(e.cases[0],1,-1);const H=async(g,w,v,_)=>{if(I){S.push({...W(g.name,w,v),failed:"an earlier run left the browser stuck; batch stopped"});return}if(Date.now()-b>ee){S.push({...W(g.name,w,v),failed:"batch time limit reached; run skipped"});return}r(_);T.push({cpu:w,round:v,caseName:g.name});S.push(await V(g,w,v))};for(const g of o){for(let w=0;w<a;w++){const v=he(e.cases,2654435769^w*7919+g);for(const _ of v)await H(_,g,w,`cpu ${g}x \xB7 round ${w+1}/${a} \xB7 ${_.name}`)}for(let w=0;w<(e.extraRoundsWhenClose??0);w++){const v=e.cases.map(y=>pe(S.filter(z=>z.cpu===g&&z.caseName===y.name),c));for(const y of v)y.control=e.cases.find(z=>z.name===y.caseName)?.control===true;const _=new Set([...ye(v).flat(),...v.filter(y=>!y.control&&y.validRuns<2&&y.plannedRuns>0).map(y=>y.caseName)]);if(_.size===0)break;const C=a+w;const J=he(e.cases.filter(y=>_.has(y.name)),20973^C*7919+g);for(const y of J)await H(y,g,C,`cpu ${g}x \xB7 extra round ${w+1} (close call) \xB7 ${y.name}`)}}if(e.traceDir){tt(e.traceDir,{recursive:true});for(const[g,w]of e.cases.entries()){const v=de(e.traceDir,`${String(g+1).padStart(2,"0")}-${w.name.replace(/[^\w.-]+/g,"_")}.json`);if(I||Date.now()-b>ee){h.push(`Trace run for ${w.name} skipped (batch time limit or a stuck browser).`);continue}r(`trace \xB7 ${w.name}`);const _=await V(w,o[0],-1,v);if(_.failed)h.push(`Trace run for ${w.name} failed: ${_.failed}`)}}await U($,1e4)}finally{const f=await U(M,15e3);t.browserLeftRunning=!f;if(!f){se=true;h.push("Chromium didn't close in 15 s and may still be running. The benchmark lock stays held; restart the MCP server before the next batch.")}}const m=[];for(const f of o){for(const $ of e.cases){m.push(pe(S.filter(R=>R.cpu===f&&R.caseName===$.name),c))}}for(const f of m)f.control=e.cases.find($=>$.name===f.caseName)?.control===true;for(const f of o){const $=m.filter(F=>F.cpu===f);const R=$.find(F=>!F.control);if(!R)continue;R.isBaseline=true;const V=F=>F.droppedMin!=null?F.median?.droppedPct??null:null;const W=V(R);for(const F of $.filter(H=>H!==R&&!H.control)){const H=V(F);F.deltaDroppedPct=W!=null&&H!=null?x(H-W):null}}if(e.allowPageErrors&&S.some(f=>f.pageErrors.length)){h.push("Some runs threw page errors (kept because allowPageErrors is on).")}if(I)h.push("A browser context wouldn't close, so the batch stopped early. Restart and run again.");if(e.budgets)for(const f of m)f.budget=Pe(f,e.budgets,e.steps??[]);const k=m.filter(f=>f.median).length;const q=m.every(f=>f.validRuns===f.plannedRuns);const te=k===0?"failed":k<m.length||!q?"partial":"ok";const E=m.filter(f=>f.median&&f.validRuns>0&&f.validRuns<f.plannedRuns);for(const f of E){const $=Array.from(new Set(f.runs.filter(R=>R.failed).map(R=>R.failed)));h.push(`${f.caseName} (CPU ${f.cpu}x): ${f.plannedRuns-f.validRuns} of ${f.plannedRuns} runs failed: ${$.join("; ")}`)}for(const f of m){const $=f.median;if($?.probeSpanMs&&$.cdpSpanMs&&Math.abs($.cdpSpanMs-$.probeSpanMs)/$.probeSpanMs>.15){h.push(`${f.caseName} (CPU ${f.cpu}x): Chrome's counters cover ${Math.round($.cdpSpanMs)} ms but frames cover ${Math.round($.probeSpanMs)} ms, so its cost columns include extra time.`)}if($?.probeSpanMs!=null&&$.probeSpanMs<s*.8){h.push(`${f.caseName} (CPU ${f.cpu}x) recorded ${Math.round($.probeSpanMs)} ms, not the ${s} ms asked for.`)}}const L={status:te,hasSteps:Boolean(e.steps?.length),browserLeftRunning:se,replay:{startedAt:A,...ae,runOrder:T},url:e.url,browser:`Chromium ${M.version()}${e.headed?"":" (headless)"}`,gpu:l,viewport:p,refreshHz:c,durationMs:s,runsPerCase:a,results:m,traces:u,warnings:h};if(e.outFile){try{Te(e.outFile,JSON.stringify({options:e,result:L},null,2))}catch(f){L.warnings.push(`Couldn't write outFile ${e.outFile}: ${re(f?.message??String(f))}`)}}return L}var Ke=1.5;var Qe=2;function ye(e){const r=e.filter(n=>!n.control&&n.median&&n.validRuns>1);const t=r.filter(n=>n.droppedMin!=null);const a=[];if(t.length>=2&&t.length===r.length){const n=[...t].sort((i,o)=>i.median.droppedPct-o.median.droppedPct);for(let i=1;i<n.length;i++){const[o,c]=[n[i-1],n[i]];const p=c.median.droppedPct-o.median.droppedPct<=Ke;if(c.droppedMin<=o.droppedMax||p)a.push([o.caseName,c.caseName])}return a}if(t.length>0)return a;const s=r.filter(n=>n.median.fps!=null).sort((n,i)=>i.median.fps-n.median.fps);for(let n=1;n<s.length;n++){const[i,o]=[s[n-1],s[n]];const c=i.median.fps-o.median.fps<=Qe;if((o.fpsMax??0)>=(i.fpsMin??0)||c)a.push([i.caseName,o.caseName])}return a}function Ve(e){const r=e.filter(o=>!o.control&&o.median);const t=[];const a=r.filter(o=>o.validRuns<2);if(a.length)t.push(`**Too few runs to rank:** ${a.map(o=>o.caseName).join(", ")} (need 2 or more valid runs).`);const s=r.filter(o=>o.validRuns>1);const n=s.filter(o=>o.droppedMin!=null);if(n.length>0&&n.length<s.length){t.push(`**Not enough traced runs to compare smoothness:** ${s.filter(o=>!n.includes(o)).map(o=>`${o.caseName} (${o.tracedRuns}/${o.validRuns} traced)`).join(", ")}.`);return t.join("\n")}const i=new Map(s.map(o=>[o.caseName,o]));for(const[o,c]of ye(e)){const p=i.get(o);const h=i.get(c);t.push(n.length?`**Too close to call:** ${o} (${p.droppedMin}\u2013${p.droppedMax}% dropped) and ${c} (${h.droppedMin}\u2013${h.droppedMax}%) overlap or sit within ${Ke} pp. No clear winner between them; more runs may separate them.`:`**Too close to call:** ${o} and ${c} overlap or sit within ${Qe} FPS. No clear winner between them; more runs may separate them.`)}return t.length?t.join("\n"):null}function St(e){const r=(o,c=1)=>o==null?"\u2014":o.toFixed(c);const t=[];t.push(`### Web benchmark \xB7 ${e.url}`);t.push(`${e.browser}${e.gpu?` \xB7 GPU: ${e.gpu}${fe(e.gpu)?" \u26A0\uFE0F software":""}`:""} \xB7 ${e.viewport.width}\xD7${e.viewport.height} @${e.viewport.deviceScaleFactor}x \xB7 ${e.runsPerCase} runs${e.results.some(o=>o.plannedRuns>e.runsPerCase)?" (more for close calls)":""} \xD7 ${e.durationMs/1e3}s per case, fresh browser context each run, case order shuffled each round.`);const a=e.results.map(o=>o.median?.probeSpanMs).filter(o=>o!=null);const s=e.results.map(o=>o.median?.cdpSpanMs).filter(o=>o!=null);const n=o=>`${(Math.min(...o)/1e3).toFixed(2)}\u2013${(Math.max(...o)/1e3).toFixed(2)} s`;if(a.length){t.push(`Recorded: frames over ${n(a)}${s.length?`; cost columns (Chrome's counters) over ${n(s)}`:""}, not the requested time.`)}if(e.replay?.dirty)t.push(`Note: the project had uncommitted changes, so commit ${e.replay.commit?.slice(0,10)??"?"} alone doesn't describe the code that ran.`);t.push("");t.push(`**How to read this.** Dropped % is the smoothness number: frames Chrome dropped or only partly produced that it says hurt smoothness, as a share of the frames it wanted, from its own verdicts in a trace. Rank on it first, and only across variants doing the same work. Shown FPS counts frames Chrome produced (headless Chrome displays nothing), so an animation that changes less often needs fewer; don't compare it across variants that update at different rates. Main-thread FPS and frame gaps come from requestAnimationFrame. Main thread % is Chrome's task time for the run. Desktop numbers, even with CPU slowdown, don't stand in for a real phone's GPU, memory or heat.`);const i=Array.from(new Set(e.results.map(o=>o.cpu)));for(const o of i){t.push("");t.push(o===1?`#### No CPU slowdown`:`#### CPU slowed ${o}x`);t.push("");t.push(`| Case | Runs | Shown FPS | Dropped % | Main-thread FPS (range) | p95 gap ms | Worst gap ms | Main thread % | Script ms | Style ms | Layout ms | Layouts | DOM nodes | JS heap MB | Flags |`);t.push(`|------|-----:|----------:|----------:|------------------------:|-----------:|-------------:|--------------:|----------:|---------:|----------:|--------:|----------:|--------:|-------|`);for(const l of e.results.filter(d=>d.cpu===o)){const d=l.median;if(!d){const T=l.runs.find(b=>b.failed)?.failed??"no valid runs";t.push(`| ${l.caseName} | 0/${l.plannedRuns} | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u274C FAILED (${T}) |`);continue}const M=l.fpsMin!=null&&l.fpsMax!=null&&l.validRuns>1?` (${r(l.fpsMin)}\u2013${r(l.fpsMax)})`:"";const S=l.flags.length?`\u26A0\uFE0F ${l.flags.join("; ")}`:"no flags";t.push(`| ${l.isBaseline?`${l.caseName} (baseline)`:l.control?`${l.caseName} (control)`:l.caseName} | ${l.validRuns}/${l.plannedRuns} | ${r(d.shownFps)} | ${l.droppedMin==null&&l.tracedRuns>0?`\u2014 (${l.tracedRuns}/${l.validRuns} traced)`:r(l.droppedMin==null?null:d.droppedPct)}${l.droppedMin!=null&&l.validRuns>1?` [${l.droppedMin}\u2013${l.droppedMax}]`:""}${l.deltaDroppedPct!=null?` (${l.deltaDroppedPct>0?"+":""}${l.deltaDroppedPct} pp)`:""} | ${r(d.fps)}${M} | ${r(d.frameGapP95Ms)} | ${r(d.frameGapMaxMs)} | ${r(d.mainThreadPct)} | ${r(d.scriptMs,0)} | ${r(d.styleMs,0)} | ${r(d.layoutMs,0)} | ${r(d.layoutCount,0)} | ${r(d.domNodesEnd,0)} | ${r(d.heapMb)} | ${S} |`)}const c=Ve(e.results.filter(l=>l.cpu===o));if(c){t.push("");t.push(c)}const p=e.results.filter(l=>l.cpu===o&&l.budget);if(p.length){t.push("");t.push(`Budget:`);for(const l of p){const d=l.budget;const M=[...d.over,...d.unknown.map(T=>`${T} not measured`)].join("; ");const S=l.control?`${l.caseName} (control)`:l.caseName;t.push(`- **${S}**: ${d.meets?"meets every limit":`misses (${M||"no result"})`}`)}}const h=e.results.filter(l=>l.cpu===o&&l.median?.interactions!=null&&(e.hasSteps||l.median.interactions>0)).map(l=>{const d=l.median;const M=l.inputMin!=null&&l.inputMax!=null?` \xB7 slowest across all runs ${l.inputMax} ms (range ${l.inputMin}\u2013${l.inputMax})`:"";const S=l.runs.filter(b=>!b.failed&&b.fps!=null&&b.inputCaptureComplete===false).length;const T=S?` \xB7 \u26A0\uFE0F timing incomplete in ${S} run(s): the last input's paint came after the run ended, so its time is unknown (leave more time after the last step)`:"";if(!d.interactions)return`- **${l.caseName}**: middle run had no input entry (inputs under 16 ms aren't reported)${M}${T}`;return`- **${l.caseName}**: middle run: ${d.interactions} reported (16 ms or more), slowest ${d.worstInteractionMs} ms (waiting ${d.worstInputDelayMs} \xB7 handlers ${d.worstProcessingMs} \xB7 to paint ${d.worstPresentationMs})${M}${T}`});if(h.length){t.push("");t.push(`Input response (scripted steps, Event Timing). Inputs under 16 ms aren't reported. Under 200 ms is good.`);t.push(...h)}const u=e.results.filter(l=>l.cpu===o&&l.median&&l.median.topScripts.length).map(l=>`- **${l.caseName}**: ${l.median.topScripts.slice(0,3).map(d=>`${d.name} ${d.ms} ms`).join(", ")}`);if(u.length){t.push("");t.push(`Scripts in slow frames (Long Animation Frames; frames that ran past the end of a run are left out):`);t.push(...u)}}if(e.traces.length){t.push("");t.push(`Chrome traces (open in DevTools \u2192 Performance \u2192 Load profile): ${e.traces.join(", ")}`)}if(e.warnings.length){t.push("");t.push(...e.warnings.map(o=>`\u26A0\uFE0F ${o}`))}return t.join("\n")}export{Pe as checkBudgets,Re as GPU_RENDERER_JS,fe as isSoftwareGpu,_e as percentile,Ce as snapRefreshRate,he as shuffled,Ae as caseUrl,Le as summarizeFrameTrace,Fe as keyMatches,Ne as mergeInteractions,De as summarizeProbe,Be as cdpDeltas,Ie as webBenchFlags,pe as aggregateCase,ze as PLAYWRIGHT_MISSING,Oe as loadChromium,G as withDeadline,U as closeWithin,je as validateWebBenchOptions,re as oneLine,Ge as locateStepTargets,Ue as missedInput,He as expectFailureFrom,bt as runWebBench,We as waitForHostLock,Je as takeHostLock,ye as tiedPairs,Ve as closeCall,St as formatWebBenchMarkdown};
@@ -0,0 +1,47 @@
1
+ import{createRequire as x}from"module";const B=x(import.meta.url);import{PACKAGE_NAME as l,VERSION as y}from"./chunk-XUPR22SJ.mjs";import{spawnSync as p}from"child_process";import i from"fs";import _ from"os";import a from"path";function h(){return{BUOY_VERIFY:"auto"}}var v="https://registry.npmjs.org";var m=3e3;function j(e={}){const t=e.cwd??process.cwd();const r=e.skillSrcDir??a.resolve(__dirname,"../skill");const n=e.log??(c=>process.stdout.write(c+"\n"));const o=E(e,n);const s=[];f(a.join(t,".mcp.json"),"mcpServers",o,t,s);i.mkdirSync(a.join(t,".cursor"),{recursive:true});f(a.join(t,".cursor","mcp.json"),"mcpServers",o,t,s);if(i.existsSync(a.join(t,".vscode"))){f(a.join(t,".vscode","mcp.json"),"servers",o,t,s)}F(r,t,s);k(a.join(t,"CLAUDE.md"),t,s);k(a.join(t,"AGENTS.md"),t,s);n("Buoy MCP configuration written:");for(const c of s)n(" \u2022 "+c);n("");n("Next steps:");n(" 1. Restart your editor (or reconnect the MCP server) to load `buoy`.");n(" 2. Complete account setup, then open an app with the required Buoy tools and sync integration.");n(" 3. Confirm the device and tools are available. MCP data/action access requires Pro.");if(o.command==="npx"){n("");n(`The editor launches the server with \`npx -y ${l}@latest\`, so it stays`);n(`Registry access may be needed. Re-run \`npx -y ${l}@latest init\` to refresh configuration.`)}else if(!e.cliPath){n("");n(`This uses a pinned local install (no network on startup). To update it later,`);n(`To update the local package, run: npx -y ${o.args[0]?.replace(/dist[/\\]cli\.js$/,"")??""}@latest init --local`)}}function E(e,t){if(e.cliPath){return{type:"stdio",command:"node",args:[e.cliPath],env:h()}}const r=e.mode??"auto";if(r==="npx"){return g()}if(r==="local"){return b(e,t)}const n=C();if(n.reachable){return g()}t(`\u26A0 npm registry ${n.registry?`(${n.registry}) `:""}looks unreachable \u2014 \`npx @latest\` would hang your editor at startup. Falling back to a pinned local install.`);return b(e,t)}function g(){return{type:"stdio",command:"npx",args:["-y",`${l}@latest`],env:h()}}function b(e,t){const r=e.installDir??process.env.BUOY_MCP_HOME??a.join(_.homedir(),".buoy-mcp");const n=e.registry??v;const o=A(r,n,t);if(o){return{type:"stdio",command:"node",args:[o],env:h()}}t("\u26A0 local install failed \u2014 writing the npx entry instead (see the error above).");return g()}function A(e,t,r){const n=`${l}@${y==="0.0.0-dev"?"latest":y}`;i.mkdirSync(e,{recursive:true});const o=process.platform==="win32"?"npm.cmd":"npm";r(`Installing ${n} into ${e} from ${t} \u2026`);const s=p(o,["install",n,"--prefix",e,"--registry",t,`--@buoy-gg:registry=${t}`,"--no-audit","--no-fund","--loglevel=error"],{stdio:["ignore","ignore","inherit"],encoding:"utf8"});if(s.status!==0){r(`\u26A0 npm install exited with code ${s.status??"?"}${s.error?` (${s.error.message})`:""}.`);return null}const c=a.join(e,"node_modules",l,"dist","cli.js");if(!i.existsSync(c)){r(`\u26A0 install finished but ${c} is missing.`);return null}return c}function C(){const e=P();if(!e)return{reachable:false,registry:null};if(/^https?:\/\/registry\.npmjs\.org\/?$/i.test(e)){return{reachable:true,registry:e}}return{reachable:R(e),registry:e}}function P(){const e=w("@buoy-gg:registry");if(e)return e;const t=w("registry");return t??v}function w(e){const t=process.platform==="win32"?"npm.cmd":"npm";const r=p(t,["config","get",e],{encoding:"utf8",timeout:m});if(r.status!==0||typeof r.stdout!=="string")return null;const n=r.stdout.trim();if(!n||n==="undefined"||n==="null")return null;return n}function R(e){const t=p(process.execPath,["-e",I,e,String(m)],{encoding:"utf8",timeout:m+2e3});return t.status===0}var I=`
2
+ const url = process.argv[1];
3
+ const ms = Number(process.argv[2]) || 3000;
4
+ const c = new AbortController();
5
+ const t = setTimeout(() => c.abort(), ms);
6
+ fetch(url, { method: 'HEAD', signal: c.signal })
7
+ .then(() => { clearTimeout(t); process.exit(0); })
8
+ .catch(() => {
9
+ // A HEAD may be rejected while the host is up; a bare TCP-level failure is
10
+ // what we care about. fetch throwing a TypeError = network unreachable.
11
+ clearTimeout(t);
12
+ process.exit(1);
13
+ });
14
+ `;function f(e,t,r,n,o){let s={};if(i.existsSync(e)){try{s=JSON.parse(i.readFileSync(e,"utf8"))}catch{i.copyFileSync(e,e+".bak");o.push(`existing ${u(e,n)} was invalid JSON \u2014 backed up to ${u(e,n)}.bak`);s={}}}else{i.mkdirSync(a.dirname(e),{recursive:true})}const c=s[t]&&typeof s[t]==="object"?s[t]:{};const $="buoy"in c;c.buoy=r;s[t]=c;i.writeFileSync(e,JSON.stringify(s,null,2)+"\n");o.push(`${$?"updated":"added"} \`buoy\` in ${u(e,n)}`)}var d="# Debugging with Buoy";var T=`${d}
15
+
16
+ Use Buoy to inspect a connected app when runtime evidence helps the task.
17
+ Complete account, tool integration and connection setup first. MCP data and
18
+ action access requires Pro. Follow the user's scope and repository instructions.
19
+
20
+ - \`get_triage\`: summarize available device and tool observations, then choose
21
+ a relevant tool. Missing data is not proof that no problem occurred.
22
+ - \`run_flow\`: run supported tap, type, wait and assertion steps to reproduce
23
+ a failure or check a change. Review the flow before running it.
24
+ - \`get_events\`: inspect retained events from connected tools.
25
+ - \`get_network_requests\`: inspect captured requests and responses. Capture
26
+ depends on installed hooks, transport and when recording began.
27
+ - \`network_conditions\`: take the app offline or add latency to check error
28
+ and retry paths. Development builds only. Restore \`normal\` when finished.
29
+ - \`get_console\`: inspect retained console and error output. A terminated or
30
+ disconnected app may not provide new data.
31
+ - \`get_storage\`: inspect registered storage backends and available keys.
32
+ - \`reload_app\`: reload where supported. Confirm the device remains connected;
33
+ a reload does not guarantee recovery from a crash.
34
+ - \`get_snapshot\`: inspect the available state of a specific tool.
35
+
36
+ After a behavior change, repeat the failing interaction when the environment
37
+ allows it. Use relevant tests and report what was actually checked. For a
38
+ performance change, compare \`measure_renders\` runs with the same steps and
39
+ \`compareToPrevious: true\`; inspect the measurements and their limitations.
40
+ Use \`run_flow\` for supported UI checks. Do not claim a fix was verified when
41
+ only source review or a build was performed.
42
+
43
+ \`BUOY_VERIFY\` controls verification reminders: \`auto\` uses edit-based
44
+ reminders, \`always\` repeats until checked, and \`never\` disables them.
45
+ A reminder is not a test result. Choose checks appropriate to the change;
46
+ prose-only edits do not require running device actions.
47
+ `;function k(e,t,r){let n="";if(i.existsSync(e)){try{n=i.readFileSync(e,"utf8")}catch{r.push(`\u26A0 could not read ${u(e,t)} \u2014 skipped the debugging guide`);return}if(n.includes(d)){r.push(`${u(e,t)} already has the "${d}" block; preserved existing text. Review it against the current bundled guidance before relying on older instructions.`);return}}const o=n===""?"":n.endsWith("\n")?"\n":"\n\n";try{i.writeFileSync(e,n+o+T);r.push(`${n?"appended":"created"} "${d}" block in ${u(e,t)}`)}catch{r.push(`\u26A0 could not write ${u(e,t)} \u2014 skipped the debugging guide`)}}function F(e,t,r){if(!i.existsSync(e)){r.push("\u26A0 no bundled skills found \u2014 skipped");return}const n=i.readdirSync(e,{withFileTypes:true}).filter(o=>o.isDirectory()&&i.existsSync(a.join(e,o.name,"SKILL.md")));if(n.length===0){r.push("\u26A0 no skills with a SKILL.md found \u2014 skipped");return}for(const o of n){const s=a.join(e,o.name);const c=a.join(t,".claude","skills",o.name);S(s,c);r.push(`installed the ${o.name} skill \u2192 ${u(a.join(c,"SKILL.md"),t)}`)}}function S(e,t){i.mkdirSync(t,{recursive:true});for(const r of i.readdirSync(e,{withFileTypes:true})){const n=a.join(e,r.name);const o=a.join(t,r.name);if(r.isDirectory())S(n,o);else i.copyFileSync(n,o)}}function u(e,t){const r=a.relative(t,e);return r&&!r.startsWith("..")?r:e}export{j as runInit};