@zibby/mcp-browser 0.1.5 → 0.1.6

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,14 @@
1
+ #!/usr/bin/env node
2
+ var ne=Object.create;var W=Object.defineProperty;var re=Object.getOwnPropertyDescriptor;var se=Object.getOwnPropertyNames;var ie=Object.getPrototypeOf,oe=Object.prototype.hasOwnProperty;var ae=(e,n,r,s)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of se(n))!oe.call(e,t)&&t!==r&&W(e,t,{get:()=>n[t],enumerable:!(s=re(n,t))||s.enumerable});return e};var le=(e,n,r)=>(r=e!=null?ne(ie(e)):{},ae(n||!e||!e.__esModule?W(r,"default",{value:e,enumerable:!0}):r,e));const{spawn:ce,execFileSync:de}=require("child_process"),m=require("fs"),S=require("path"),A=S.join(__dirname,"..","src","stable-id-inject.js"),Y=S.join(require("os").tmpdir(),"zibby-mcp-debug.log");function i(e){const n=`[${new Date().toISOString()}] ${e}
3
+ `;m.appendFileSync(Y,n),console.error(`[Zibby MCP] ${e}`)}m.appendFileSync(Y,`
4
+ === Zibby MCP Started ${new Date().toISOString()} PID:${process.pid} ===
5
+ `),i(`Args: ${process.argv.slice(2).join(" ")}`),i(`Env ZIBBY_NODE_SESSION_PATH=${process.env.ZIBBY_NODE_SESSION_PATH||"(not set)"}`),i(`Env ZIBBY_SESSION_PATH=${process.env.ZIBBY_SESSION_PATH||"(not set)"}`),i(`Env ZIBBY_SESSION_INFO=${process.env.ZIBBY_SESSION_INFO||"(not set)"}`);let o=null,B="none";const w=[];let q=null;function fe(){if(process.env.ZIBBY_NODE_SESSION_PATH){o=process.env.ZIBBY_NODE_SESSION_PATH,B="env:ZIBBY_NODE_SESSION_PATH",m.mkdirSync(o,{recursive:!0}),i(`\u{1F4C1} Session from env: ${o}`);return}const e=process.argv.slice(2);for(const r of e)if(r.startsWith("--output-dir=")){o=r.split("=").slice(1).join("="),B="arg:--output-dir",m.mkdirSync(o,{recursive:!0}),i(`\u{1F4C1} Session from --output-dir: ${o}`);return}if(process.env.ZIBBY_SESSION_PATH){const r=S.join(process.env.ZIBBY_SESSION_PATH,".session-info.json");if(m.existsSync(r))try{const s=JSON.parse(m.readFileSync(r,"utf-8")),t=s.currentNode||"execute_live";o=S.join(s.sessionPath||process.env.ZIBBY_SESSION_PATH,t),B="file:per-session",m.mkdirSync(o,{recursive:!0}),i(`\u{1F4C1} Session from per-session file: ${o}`);return}catch(s){i(`\u26A0\uFE0F Per-session file read error: ${s.message}`)}}const n=[process.env.ZIBBY_SESSION_INFO,S.join(process.cwd(),".zibby/output/.session-info.json"),S.join(process.cwd(),"test-results/.session-info.json")].filter(Boolean);for(const r of n)if(m.existsSync(r))try{const s=JSON.parse(m.readFileSync(r,"utf-8")),t=s.currentNode||"execute_live";if(s.sessionPath){o=S.join(s.sessionPath,t),B=`file:shared(${S.basename(r)})`,m.mkdirSync(o,{recursive:!0}),i(`\u26A0\uFE0F Session from SHARED file (race-prone): ${o}`);return}}catch(s){i(`\u26A0\uFE0F Shared file read error: ${s.message}`)}i("\u26A0\uFE0F No session path resolved \u2014 events will not be recorded")}function G(e){const n=Math.floor(e/1e3),r=Math.floor(n/3600),s=Math.floor(n%3600/60),t=n%60,a=e%1e3;return`${String(r).padStart(2,"0")}:${String(s).padStart(2,"0")}:${String(t).padStart(2,"0")}.${String(a).padStart(3,"0")}`}function y(e,n){if(!o)return;const r=Date.now();q||(q=r);const s=r-q,t={id:w.length,type:e,timestamp:new Date(r).toISOString(),videoOffsetMs:s,videoOffsetFormatted:G(s),data:n,_recordedBy:"zibby-wrapper-v2"};return w.push(t),i(`\u{1F4DD} Event #${t.id}: ${e}${n.stableId?` [${n.stableId}]`:""}`),e==="close"?pe():N(),t.id}function ue(e,n){if(!o||e<0||e>=w.length)return;const r=w[e];r&&!r.data.stableId&&n&&(r.data.stableId=n,i(`\u{1F517} Event #${e} stableId: ${n}`),N())}function me(e){function a(f,d,u){if(d>=f.length)return null;const I=f[d];let b=1,z=128;for(;b<=8&&!(I&z);)b++,z>>=1;if(b>8||d+b>f.length)return null;let C=u?I:I&z-1;for(let Z=1;Z<b;Z++)C=C<<8|f[d+Z];return{value:C,length:b}}function l(f,d){if(d>=f.length)return null;const u=a(f,d,!0);if(!u)return null;d+=u.length;const I=a(f,d,!1);return I?(d+=I.length,{id:u.value,dataStart:d,dataSize:I.value,dataEnd:d+I.value}):null}function _(f,d,u){let I=0;for(let b=0;b<u;b++)I=I<<8|f[d+b];return I}function E(f,d,u){return u===4?f.readFloatBE(d):u===8?f.readDoubleBE(d):null}let c=0,p=1e6,v=null;const P=l(e,c);if(!P)return null;c=P.dataEnd;const j=l(e,c);if(!j||j.id!==408125543)return null;c=j.dataStart;const te=Math.min(j.dataEnd,e.length);for(;c<te;){const f=l(e,c);if(!f)break;if(f.id===357149030){let d=f.dataStart;for(;d<f.dataEnd;){const u=l(e,d);if(!u)break;u.id===2807729?p=_(e,u.dataStart,u.dataSize):u.id===17545&&(v=E(e,u.dataStart,u.dataSize)),d=u.dataEnd}break}c=f.dataEnd}return v!==null?v*p/1e6:null}function Se(e){try{const n=de("ffprobe",["-v","error","-show_entries","format=duration","-of","default=noprint_wrappers=1:nokey=1",e],{encoding:"utf-8",timeout:5e3}),r=parseFloat(n.trim());if(!isNaN(r)){const s=Math.round(r*1e3);return i(`\u{1F4CF} Video duration (ffprobe): ${s}ms`),s}}catch{i("\u26A0\uFE0F ffprobe not available, using WebM parser fallback")}try{const n=m.readFileSync(e),r=me(n);if(r){const s=Math.round(r);return i(`\u{1F4CF} Video duration (WebM parser): ${s}ms`),s}}catch(n){i(`\u26A0\uFE0F WebM parsing failed: ${n.message}`)}return null}function pe(){if(!o||w.length===0)return;let e=null,n=0;const r=40;let s=0,t=0;for(;!e&&n<r;){n++;let c=null;if(m.existsSync(S.join(o,"recording.webm")))c=S.join(o,"recording.webm"),n===1&&i("Found recording.webm");else try{const p=m.readdirSync(o);n===1&&i(`Directory contains ${p.length} files: ${p.join(", ")}`);const v=p.filter(P=>P.endsWith(".webm"));n===1&&v.length>0&&i(`Found ${v.length} .webm file(s): ${v.join(", ")}`),v.length>0&&(c=S.join(o,v[0]))}catch(p){n===1&&i(`\u26A0\uFE0F Error reading directory: ${p.message}`)}if(c&&m.existsSync(c))try{const v=m.statSync(c).size;if(v>0&&v===s){if(t++,t>=2){e=c,i(`\u2705 Video file stable after ${n} attempts: ${S.basename(e)} (${v} bytes)`);break}}else t=0,s=v}catch(p){i(`\u26A0\uFE0F Error checking file: ${p.message}`)}if(!e){n%5===0&&i(`\u23F3 Waiting for stable video file... (attempt ${n}/${r}, size: ${s})`);const p=Date.now();for(;Date.now()-p<500;);}}if(!e){i(`\u26A0\uFE0F Video file not found/stable after ${n} attempts in: ${o}`),N();return}const a=Se(e);if(!a){i("\u26A0\uFE0F Could not determine video duration, using fallback"),N();return}const l=w.find(c=>c.type==="close");if(!l){i("\u26A0\uFE0F No close event found"),N();return}const _=new Date(l.timestamp).getTime(),E=_-a;i(`\u{1F4CA} Calculated video start: ${new Date(E).toISOString()}`),i(` Close timestamp: ${_}ms`),i(` Video duration: ${a}ms`),w.forEach(c=>{const p=new Date(c.timestamp).getTime();c.videoOffsetMs=p-E,c.videoOffsetFormatted=G(c.videoOffsetMs)}),i(`\u2705 Recalculated ${w.length} events using accurate video start time`),N()}function N(){if(o)try{const e=S.join(o,"events.json");m.writeFileSync(e,JSON.stringify(w,null,2))}catch{}}i(`CWD: ${process.cwd()}`),fe();const U=process.argv.slice(2);let O=null;for(const e of U){const n=e.match(/^--save-video=(\d+)x(\d+)$/);if(n){O={width:parseInt(n[1],10),height:parseInt(n[2],10)};break}}const h=U.filter(e=>!e.startsWith("--save-video"));m.existsSync(A)?(h.unshift("--init-script",A),i(`\u2705 Stable ID injection enabled: ${A}`)):i(`\u26A0\uFE0F Stable ID script not found: ${A}`);const x=h.findIndex(e=>e==="--caps");if(x>=0&&h[x+1])h[x+1].includes("devtools")||(h[x+1]+=",devtools");else{const e=h.findIndex(n=>n.startsWith("--caps="));e>=0?h[e].includes("devtools")||(h[e]+=",devtools"):h.push("--caps","devtools")}if(o){const e=h.findIndex(n=>n.startsWith("--output-dir"));e!==-1?h[e]=`--output-dir=${o}`:h.push("--output-dir",o),i(`\u{1F4F9} Video output: ${o} (source: ${B})`)}const ve=S.dirname(require.resolve("@playwright/mcp")),he=S.join(ve,"cli.js"),g=ce(process.execPath,[he,...h],{stdio:["pipe","pipe","inherit"]});let $=!1,H=!1,Q=8e5;const D=new Set;function Ie(){if(H||!o)return;H=!0;const e=Q++;D.add(e);const n=o?S.join(o,"recording.webm"):"recording.webm",r={jsonrpc:"2.0",id:e,method:"tools/call",params:{name:"browser_start_video",arguments:{filename:n,...O?{size:O}:{}}}};try{g.stdin.write(JSON.stringify(r)+`
6
+ `),i(`\u{1F3AC} Auto-started video recording${O?` (${O.width}x${O.height})`:""}`)}catch(s){D.delete(e),i(`\u26A0\uFE0F browser_start_video send failed: ${s.message}`)}}function _e(){if(!H)return;const e=Q++;D.add(e);const n={jsonrpc:"2.0",id:e,method:"tools/call",params:{name:"browser_stop_video",arguments:{}}};try{g.stdin.write(JSON.stringify(n)+`
7
+ `),i("\u{1F3AC} Auto-stopped video recording")}catch(r){D.delete(e),i(`\u26A0\uFE0F browser_stop_video send failed: ${r.message}`)}}const ge=2e3,be=5e3,K="frames";let X=0,we=1e6;const R=new Set;let k=null;function L(){k&&(clearInterval(k),k=null)}function $e(){if(o)try{m.mkdirSync(S.join(o,K),{recursive:!0})}catch{}}function J(){return S.join(o,K,`live-frame-${Date.now()}.png`)}function ye(){L(),!(!$||!o)&&(k=setInterval(()=>{if(!$||!o){L();return}ee(!0,J())},be))}function ee(e,n){if(!o||!$)return;$e();const r=Date.now();if(!e&&r-X<ge)return;X=r;const s=n||J(),t=we++;R.add(t);const a={jsonrpc:"2.0",id:t,method:"tools/call",params:{name:"browser_take_screenshot",arguments:{type:"png",filename:s}}};try{g.stdin.write(JSON.stringify(a)+`
8
+ `),i(`\u{1F4F8} live-frame queued: ${s}`)}catch(l){R.delete(t),i(`\u26A0\uFE0F live-frame send failed: ${l.message}`)}}function Ee(e){!e||typeof e!="string"||!e.startsWith("browser_")||e==="browser_close"||e==="browser_take_screenshot"||!o||!$||ee(!1,J())}const T=new Map;let Ne=9e5;const V=new Map;let F="";process.stdin.on("data",e=>{F+=e.toString();const n=F.split(`
9
+ `);F=n.pop()||"";for(const r of n)if(r.trim()){try{const s=JSON.parse(r);if(s.method&&i(`\u{1F4E8} STDIN: ${s.method} ${s.params?.name||""}`),s.method==="tools/call"&&s.params){const t=s.params.name,a=s.params.arguments||{};T.set(s.id,{toolName:t,toolArgs:a});let l=-1;t==="browser_navigate"?($=!0,ye(),l=y("navigate",{url:a.url})):t==="browser_click"?l=y("click",{element:a.element,ref:a.ref}):t==="browser_type"?l=y("type",{element:a.element,ref:a.ref,text:a.text}):t==="browser_select_option"?l=y("select",{element:a.element,ref:a.ref,values:a.values}):t==="browser_fill_form"?l=y("fill_form",{fields:a.fields}):t==="browser_hover"?l=y("hover",{element:a.element,ref:a.ref}):t==="browser_close"?(_e(),l=y("close",{}),$=!1,L()):t==="browser_snapshot"&&(l=y("snapshot",{})),l>=0&&(T.get(s.id).eventId=l)}}catch{}g.stdin.write(r+`
10
+ `)}}),process.stdin.on("end",()=>{F.trim()&&g.stdin.write(F+`
11
+ `),g.stdin.end()});let M="";g.stdout.on("data",e=>{M+=e.toString();const n=M.split(`
12
+ `);M=n.pop()||"";for(const r of n){if(!r.trim())continue;let s=!0;try{const t=JSON.parse(r);if(t.id&&D.has(t.id))D.delete(t.id),t.error&&i(`\u26A0\uFE0F Video RPC error: ${t.error?.message||JSON.stringify(t.error)}`),s=!1;else if(t.id&&R.has(t.id))R.delete(t.id),t.error&&i(`\u26A0\uFE0F Screenshot RPC error: ${t.error?.message||JSON.stringify(t.error)}`),s=!1;else if(t.id&&T.has(t.id)){const a=T.get(t.id);if(T.delete(t.id),!t.error&&$&&Ee(a.toolName),!t.error&&a.toolName==="browser_navigate"&&Ie(),!t.error&&$&&a.eventId>=0&&["browser_click","browser_type","browser_select_option","browser_fill_form","browser_hover"].includes(a.toolName)){const _=Ne++,E={jsonrpc:"2.0",id:_,method:"tools/call",params:{name:"browser_evaluate",arguments:{function:"() => { const match = document.cookie.match(/(?:^|; )__zibbyLastStableId=([^;]*)/); const id = match ? decodeURIComponent(match[1]) : (window.__zibbyLastStableId || null); return id; }"}}};i(`\u{1F50D} Querying stable ID for event #${a.eventId}`),V.set(_,{eventId:a.eventId}),g.stdin.write(JSON.stringify(E)+`
13
+ `)}}if(t.id&&V.has(t.id)){const a=V.get(t.id);if(V.delete(t.id),i(`\u{1F4E5} Stable ID response for event #${a.eventId}: ${JSON.stringify(t.result||t.error||"no result").slice(0,200)}`),t.result?.content?.[0]?.text){const l=t.result.content[0].text,_=l.match(/zibby-[a-zA-Z0-9-]+/);_?(i(`\u2705 Found stable ID: ${_[0]}`),ue(a.eventId,_[0])):i(`\u26A0\uFE0F No stable ID found in: ${l.slice(0,100)}`)}else t.error&&i(`\u274C Stable ID query error: ${t.error?.message||JSON.stringify(t.error)}`);s=!1}}catch{}s&&process.stdout.write(r+`
14
+ `)}}),g.on("close",e=>{L(),$=!1,M.trim()&&process.stdout.write(M),process.exit(e||0)}),process.on("SIGINT",()=>{g.kill("SIGINT")}),process.on("SIGTERM",()=>{g.kill("SIGTERM")});
@@ -0,0 +1 @@
1
+ "use strict";const g=Object.create,p=Object.defineProperty,b=Object.getOwnPropertyDescriptor,L=Object.getOwnPropertyNames,x=Object.getPrototypeOf,H=Object.prototype.hasOwnProperty,y=(e,r)=>{for(const t in r)p(e,t,{get:r[t],enumerable:!0})},w=(e,r,t,s)=>{if(r&&typeof r=="object"||typeof r=="function")for(const o of L(r))!H.call(e,o)&&o!==t&&p(e,o,{get:()=>r[o],enumerable:!(s=b(r,o))||s.enumerable});return e},P=(e,r,t)=>(t=e!=null?g(x(e)):{},w(r||!e||!e.__esModule?p(t,"default",{value:e,enumerable:!0}):t,e)),M=e=>w(p({},"__esModule",{value:!0}),e),I={};y(I,{commaSeparatedList:()=>d,configFromCLIOptions:()=>l,defaultConfig:()=>m,dotenvFileLoader:()=>E,headerParser:()=>R,numberParser:()=>f,outputDir:()=>C,outputFile:()=>V,resolutionParser:()=>T,resolveCLIConfig:()=>N,resolveConfig:()=>D}),module.exports=M(I);const v=P(require("fs")),h=P(require("os")),a=P(require("path")),W=require("playwright-core"),A=require("playwright-core/lib/utilsBundle"),G={fileExistsAsync:async e=>v.default.existsSync(e)},Y=require("../sdk/server"),m={browser:{browserName:"chromium",launchOptions:{channel:"chrome",headless:h.default.platform()==="linux"&&!process.env.DISPLAY,chromiumSandbox:!0},contextOptions:{viewport:null}},server:{},saveTrace:!1,timeouts:{action:5e3,navigation:6e4}};async function D(e){return _(m,e)}async function N(e){const r=await O(e.config),t=B(),s=l(e);let o=m;return o=_(o,r),o=_(o,t),o=_(o,s),await F(o),o}async function F(e){if(e.browser.initScript){for(const r of e.browser.initScript)if(!await(0,G.fileExistsAsync)(r))throw new Error(`Init script file does not exist: ${r}`)}if(e.sharedBrowserContext&&e.saveVideo)throw new Error("saveVideo is not supported when sharedBrowserContext is true")}function l(e){let r,t;switch(e.browser){case"chrome":case"chrome-beta":case"chrome-canary":case"chrome-dev":case"chromium":case"msedge":case"msedge-beta":case"msedge-canary":case"msedge-dev":r="chromium",t=e.browser;break;case"firefox":r="firefox";break;case"webkit":r="webkit";break}const s={channel:t,executablePath:e.executablePath,headless:e.headless};if(e.sandbox===!1&&(s.chromiumSandbox=!1),e.proxyServer&&(s.proxy={server:e.proxyServer},e.proxyBypass&&(s.proxy.bypass=e.proxyBypass)),e.device&&e.cdpEndpoint)throw new Error("Device emulation is not supported with cdpEndpoint.");const o=e.device?W.devices[e.device]:{};return e.storageState&&(o.storageState=e.storageState),e.userAgent&&(o.userAgent=e.userAgent),e.viewportSize&&(o.viewport=e.viewportSize),e.ignoreHttpsErrors&&(o.ignoreHTTPSErrors=!0),e.blockServiceWorkers&&(o.serviceWorkers="block"),e.grantPermissions&&(o.permissions=e.grantPermissions),e.saveVideo&&(o.recordVideo={dir:S(),size:e.saveVideo}),{browser:{browserName:r,isolated:e.isolated,userDataDir:e.userDataDir,launchOptions:s,contextOptions:o,cdpEndpoint:e.cdpEndpoint,cdpHeaders:e.cdpHeader,initPage:e.initPage,initScript:e.initScript},server:{port:e.port,host:e.host,allowedHosts:e.allowedHosts},capabilities:e.caps,saveSession:e.saveSession,saveTrace:e.saveTrace,saveVideo:e.saveVideo,secrets:e.secrets,sharedBrowserContext:e.sharedBrowserContext,outputDir:e.outputDir,imageResponses:e.imageResponses,testIdAttribute:e.testIdAttribute,timeouts:{action:e.timeoutAction,navigation:e.timeoutNavigation}}}function B(){const e={};e.allowedHosts=d(process.env.PLAYWRIGHT_MCP_ALLOWED_HOSTNAMES),e.blockServiceWorkers=u(process.env.PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS),e.browser=n(process.env.PLAYWRIGHT_MCP_BROWSER),e.caps=d(process.env.PLAYWRIGHT_MCP_CAPS),e.cdpEndpoint=n(process.env.PLAYWRIGHT_MCP_CDP_ENDPOINT),e.cdpHeader=R(process.env.PLAYWRIGHT_MCP_CDP_HEADERS,{}),e.config=n(process.env.PLAYWRIGHT_MCP_CONFIG),e.device=n(process.env.PLAYWRIGHT_MCP_DEVICE),e.executablePath=n(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH),e.grantPermissions=d(process.env.PLAYWRIGHT_MCP_GRANT_PERMISSIONS),e.headless=u(process.env.PLAYWRIGHT_MCP_HEADLESS),e.host=n(process.env.PLAYWRIGHT_MCP_HOST),e.ignoreHttpsErrors=u(process.env.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS);const r=n(process.env.PLAYWRIGHT_MCP_INIT_PAGE);r&&(e.initPage=[r]);const t=n(process.env.PLAYWRIGHT_MCP_INIT_SCRIPT);return t&&(e.initScript=[t]),e.isolated=u(process.env.PLAYWRIGHT_MCP_ISOLATED),process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES==="omit"&&(e.imageResponses="omit"),e.sandbox=u(process.env.PLAYWRIGHT_MCP_SANDBOX),e.outputDir=n(process.env.PLAYWRIGHT_MCP_OUTPUT_DIR),e.port=f(process.env.PLAYWRIGHT_MCP_PORT),e.proxyBypass=n(process.env.PLAYWRIGHT_MCP_PROXY_BYPASS),e.proxyServer=n(process.env.PLAYWRIGHT_MCP_PROXY_SERVER),e.saveTrace=u(process.env.PLAYWRIGHT_MCP_SAVE_TRACE),e.saveVideo=T("--save-video",process.env.PLAYWRIGHT_MCP_SAVE_VIDEO),e.secrets=E(process.env.PLAYWRIGHT_MCP_SECRETS_FILE),e.storageState=n(process.env.PLAYWRIGHT_MCP_STORAGE_STATE),e.testIdAttribute=n(process.env.PLAYWRIGHT_MCP_TEST_ID_ATTRIBUTE),e.timeoutAction=f(process.env.PLAYWRIGHT_MCP_TIMEOUT_ACTION),e.timeoutNavigation=f(process.env.PLAYWRIGHT_MCP_TIMEOUT_NAVIGATION),e.userAgent=n(process.env.PLAYWRIGHT_MCP_USER_AGENT),e.userDataDir=n(process.env.PLAYWRIGHT_MCP_USER_DATA_DIR),e.viewportSize=T("--viewport-size",process.env.PLAYWRIGHT_MCP_VIEWPORT_SIZE),l(e)}async function O(e){if(!e)return{};try{return JSON.parse(await v.default.promises.readFile(e,"utf8"))}catch(r){throw new Error(`Failed to load config file: ${e}, ${r}`)}}function S(){return a.default.join(process.env.PW_TMPDIR_FOR_TEST??h.default.tmpdir(),"playwright-mcp-output")}function C(e,r){const t=(0,Y.firstRootPath)(r);return e.outputDir??(t?a.default.join(t,".playwright-mcp"):void 0)??a.default.join(S(),String(r.timestamp))}async function V(e,r,t,s){const o=await j(e,r,t,s);return(0,A.debug)("pw:mcp:file")(s.reason,o),o}async function j(e,r,t,s){const o=C(e,r);if(s.origin==="code")return a.default.resolve(o,t);if(s.origin==="llm"){t=t.split("\\").join("/");const c=a.default.resolve(o,t);if(!c.startsWith(a.default.resolve(o)+a.default.sep))throw new Error(`Resolved file path ${c} is outside of the output directory ${o}. Use relative file names to stay within the output directory.`);return c}return a.default.join(o,k(t))}function i(e){return Object.fromEntries(Object.entries(e??{}).filter(([r,t])=>t!==void 0))}function _(e,r){const t={...i(e.browser),...i(r.browser),browserName:r.browser?.browserName??e.browser?.browserName??"chromium",isolated:r.browser?.isolated??e.browser?.isolated??!1,launchOptions:{...i(e.browser?.launchOptions),...i(r.browser?.launchOptions),assistantMode:!0},contextOptions:{...i(e.browser?.contextOptions),...i(r.browser?.contextOptions)}};return t.browserName!=="chromium"&&t.launchOptions&&delete t.launchOptions.channel,{...i(e),...i(r),browser:t,server:{...i(e.server),...i(r.server)},timeouts:{...i(e.timeouts),...i(r.timeouts)}}}function d(e){if(e)return e.split(",").map(r=>r.trim())}function E(e){if(e)return A.dotenv.parse(v.default.readFileSync(e,"utf8"))}function f(e){if(e)return+e}function T(e,r){if(r){if(r.includes("x")){const[t,s]=r.split("x").map(o=>+o);if(isNaN(t)||isNaN(s)||t<=0||s<=0)throw new Error(`Invalid resolution format: use ${e}="800x600"`);return{width:t,height:s}}if(r.includes(",")){const[t,s]=r.split(",").map(o=>+o);if(isNaN(t)||isNaN(s)||t<=0||s<=0)throw new Error(`Invalid resolution format: use ${e}="800x600"`);return{width:t,height:s}}throw new Error(`Invalid resolution format: use ${e}="800x600"`)}}function R(e,r){if(!e)return r||{};const t=r||{},[s,o]=e.split(":").map(c=>c.trim());return t[s]=o,t}function u(e){if(e==="true"||e==="1")return!0;if(e==="false"||e==="0")return!1}function n(e){return e?e.trim():void 0}function k(e){const r=s=>s.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g,"-"),t=e.lastIndexOf(".");return t===-1?r(e):r(e.substring(0,t))+"."+r(e.substring(t+1))}
@@ -0,0 +1 @@
1
+ "use strict";const m=Object.create,l=Object.defineProperty,P=Object.getOwnPropertyDescriptor,T=Object.getOwnPropertyNames,y=Object.getPrototypeOf,v=Object.prototype.hasOwnProperty,B=(r,t)=>{for(const e in t)l(r,e,{get:t[e],enumerable:!0})},p=(r,t,e,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(const s of T(t))!v.call(r,s)&&s!==e&&l(r,s,{get:()=>t[s],enumerable:!(o=P(t,s))||o.enumerable});return r},b=(r,t,e)=>(e=r!=null?m(y(r)):{},p(t||!r||!r.__esModule?l(e,"default",{value:r,enumerable:!0}):e,r)),O=r=>p(l({},"__esModule",{value:!0}),r),d={};B(d,{Context:()=>a,InputRecorder:()=>h}),module.exports=O(d);const i=b(require("fs")),f=b(require("path")),A=require("playwright-core/lib/utilsBundle"),I=require("playwright-core"),w=require("../log"),u=require("./tab"),j=require("./config"),q=b(require("./codegen")),E=require("./tools/utils"),x=(0,A.debug)("pw:mcp:test");class a{constructor(t){this._tabs=[],this._abortController=new AbortController,this.config=t.config,this.sessionLog=t.sessionLog,this.options=t,this._browserContextFactory=t.browserContextFactory,this._clientInfo=t.clientInfo,x("create context"),a._allContexts.add(this)}static{this._allContexts=new Set}static async disposeAll(){await Promise.all([...a._allContexts].map(t=>t.dispose()))}tabs(){return this._tabs}currentTab(){return this._currentTab}currentTabOrDie(){if(!this._currentTab)throw new Error('No open pages available. Use the "browser_navigate" tool to navigate to a page first.');return this._currentTab}async newTab(){const{browserContext:t}=await this._ensureBrowserContext(),e=await t.newPage();return this._currentTab=this._tabs.find(o=>o.page===e),await this._currentTab.initializedPromise,this._currentTab}async selectTab(t){const e=this._tabs[t];if(!e)throw new Error(`Tab ${t} not found`);return await e.page.bringToFront(),this._currentTab=e,e}async ensureTab(){const{browserContext:t}=await this._ensureBrowserContext();return this._currentTab||await t.newPage(),this._currentTab}async closeTab(t){const e=t===void 0?this._currentTab:this._tabs[t];if(!e)throw new Error(`Tab ${t} not found`);const o=e.page.url();return await e.page.close(),o}async outputFile(t,e){return(0,j.outputFile)(this.config,this._clientInfo,t,e)}_onPageCreated(t){const e=new u.Tab(this,t,o=>this._onPageClosed(o));this._tabs.push(e),this._currentTab||(this._currentTab=e)}_onPageClosed(t){const e=this._tabs.indexOf(t);e!==-1&&(this._tabs.splice(e,1),this._currentTab===t&&(this._currentTab=this._tabs[Math.min(e,this._tabs.length-1)]),this._tabs.length||this.closeBrowserContext())}async closeBrowserContext(){this._closeBrowserContextPromise||(this._closeBrowserContextPromise=this._closeBrowserContextImpl().catch(w.logUnhandledError)),await this._closeBrowserContextPromise,this._closeBrowserContextPromise=void 0}isRunningTool(){return this._runningToolName!==void 0}setRunningTool(t){this._runningToolName=t}async _closeBrowserContextImpl(){if(!this._browserContextPromise)return;x("close context");const t=this._browserContextPromise;this._browserContextPromise=void 0,await t.then(async({browserContext:e,close:o})=>{this.config.saveTrace&&await e.tracing.stop();const s=this.config.saveVideo?e.pages().map(n=>n.video()).filter(n=>!!n):[];await o(async()=>{for(const n of s){const _=await this.outputFile((0,E.dateAsFileName)("webm"),{origin:"code",reason:"Saving video"});await i.default.promises.mkdir(f.default.dirname(_),{recursive:!0});const c=await n.path();if(i.default.existsSync(c))try{await i.default.promises.rename(c,_)}catch(g){g.code!=="EXDEV"&&(0,w.logUnhandledError)(g);try{await i.default.promises.copyFile(c,_),await i.default.promises.unlink(c)}catch(C){(0,w.logUnhandledError)(C)}}}})})}async dispose(){this._abortController.abort("MCP context disposed"),await this.closeBrowserContext(),a._allContexts.delete(this)}async ensureBrowserContext(){const{browserContext:t}=await this._ensureBrowserContext();return t}_ensureBrowserContext(){return this._browserContextPromise||(this._browserContextPromise=this._setupBrowserContext(),this._browserContextPromise.catch(()=>{this._browserContextPromise=void 0})),this._browserContextPromise}async _setupBrowserContext(){if(this._closeBrowserContextPromise)throw new Error("Another browser context is being closed.");this.config.testIdAttribute&&I.selectors.setTestIdAttribute(this.config.testIdAttribute);const t=await this._browserContextFactory.createContext(this._clientInfo,this._abortController.signal,this._runningToolName),{browserContext:e}=t,o=f.default.join(__dirname,"..","..","src","stable-id-inject.js");i.default.existsSync(o)&&await e.addInitScript({path:o}),this.sessionLog&&await h.create(this,e);for(const s of e.pages())this._onPageCreated(s);return e.on("page",s=>this._onPageCreated(s)),this.config.saveTrace&&await e.tracing.start({name:"trace-"+Date.now(),screenshots:!0,snapshots:!0,_live:!0}),t}lookupSecret(t){return this.config.secrets?.[t]?{value:this.config.secrets[t],code:`process.env['${t}']`}:{value:t,code:q.quote(t)}}}class h{constructor(t,e){this._context=t,this._browserContext=e}static async create(t,e){const o=new h(t,e);return await o._initialize(),o}async _initialize(){const t=this._context.sessionLog;await this._browserContext._enableRecorder({mode:"recording",recorderMode:"api"},{actionAdded:(e,o,s)=>{if(this._context.isRunningTool())return;const n=u.Tab.forPage(e);n&&t.logUserAction(o.action,n,s,!1)},actionUpdated:(e,o,s)=>{if(this._context.isRunningTool())return;const n=u.Tab.forPage(e);n&&t.logUserAction(o.action,n,s,!0)},signalAdded:(e,o)=>{if(this._context.isRunningTool()||o.signal.name!=="navigation")return;const s=u.Tab.forPage(e),n={name:"navigate",url:o.signal.url,signals:[]};s&&t.logUserAction(n,s,`await page.goto('${o.signal.url}');`,!1)}})}}
@@ -0,0 +1 @@
1
+ "use strict";const c=Object.defineProperty,S=Object.getOwnPropertyDescriptor,y=Object.getOwnPropertyNames,M=Object.prototype.hasOwnProperty,b=(a,t)=>{for(const e in t)c(a,e,{get:t[e],enumerable:!0})},P=(a,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(const o of y(t))!M.call(a,o)&&o!==e&&c(a,o,{get:()=>t[o],enumerable:!(s=S(t,o))||s.enumerable});return a},T=a=>P(c({},"__esModule",{value:!0}),a),u={};b(u,{Tab:()=>d,TabEvents:()=>r,renderModalStates:()=>m}),module.exports=T(u);const C=require("events"),g=require("playwright-core/lib/utils"),n=require("./tools/utils"),h=require("../log"),v=require("./tools/dialogs"),q=require("./tools/files"),x=require("../../transform/transform"),r={modalState:"modalState"};class d extends C.EventEmitter{constructor(t,e,s){super(),this._lastTitle="about:blank",this._consoleMessages=[],this._recentConsoleMessages=[],this._requests=new Set,this._modalStates=[],this._downloads=[],this._needsFullSnapshot=!1,this.context=t,this.page=e,this._onPageClose=s,e.on("console",o=>this._handleConsoleMessage(p(o))),e.on("pageerror",o=>this._handleConsoleMessage(_(o))),e.on("request",o=>this._requests.add(o)),e.on("close",()=>this._onClose()),e.on("filechooser",o=>{this.setModalState({type:"fileChooser",description:"File chooser",fileChooser:o,clearedBy:q.uploadFile.schema.name})}),e.on("dialog",o=>this._dialogShown(o)),e.on("download",o=>{this._downloadStarted(o)}),e.setDefaultNavigationTimeout(this.context.config.timeouts.navigation),e.setDefaultTimeout(this.context.config.timeouts.action),e[w]=this,this.initializedPromise=this._initialize()}static forPage(t){return t[w]}static async collectConsoleMessages(t){const e=[],s=await t.consoleMessages().catch(()=>[]);for(const i of s)e.push(p(i));const o=await t.pageErrors().catch(()=>[]);for(const i of o)e.push(_(i));return e}async _initialize(){for(const e of await d.collectConsoleMessages(this.page))this._handleConsoleMessage(e);const t=await this.page.requests().catch(()=>[]);for(const e of t)this._requests.add(e);for(const e of this.context.config.browser.initPage||[])try{const{default:s}=await(0,x.requireOrImport)(e);await s({page:this.page})}catch(s){(0,h.logUnhandledError)(s)}}modalStates(){return this._modalStates}setModalState(t){this._modalStates.push(t),this.emit(r.modalState,t)}clearModalState(t){this._modalStates=this._modalStates.filter(e=>e!==t)}modalStatesMarkdown(){return m(this.context,this.modalStates())}_dialogShown(t){this.setModalState({type:"dialog",description:`"${t.type()}" dialog with message "${t.message()}"`,dialog:t,clearedBy:v.handleDialog.schema.name})}async _downloadStarted(t){const e={download:t,finished:!1,outputFile:await this.context.outputFile(t.suggestedFilename(),{origin:"web",reason:"Saving download"})};this._downloads.push(e),await t.saveAs(e.outputFile),e.finished=!0}_clearCollectedArtifacts(){this._consoleMessages.length=0,this._recentConsoleMessages.length=0,this._requests.clear()}_handleConsoleMessage(t){this._consoleMessages.push(t),this._recentConsoleMessages.push(t)}_onClose(){this._clearCollectedArtifacts(),this._onPageClose(this)}async updateTitle(){await this._raceAgainstModalStates(async()=>{this._lastTitle=await(0,n.callOnPageNoTrace)(this.page,t=>t.title())})}lastTitle(){return this._lastTitle}isCurrentTab(){return this===this.context.currentTab()}async waitForLoadState(t,e){await(0,n.callOnPageNoTrace)(this.page,s=>s.waitForLoadState(t,e).catch(h.logUnhandledError))}async navigate(t){this._clearCollectedArtifacts();const e=(0,n.callOnPageNoTrace)(this.page,s=>s.waitForEvent("download").catch(h.logUnhandledError));try{await this.page.goto(t,{waitUntil:"domcontentloaded"})}catch(s){const o=s;if(!(o.message.includes("net::ERR_ABORTED")||o.message.includes("Download is starting"))||!await Promise.race([e,new Promise(l=>setTimeout(l,3e3))]))throw o;await new Promise(l=>setTimeout(l,500));return}await this.waitForLoadState("load",{timeout:5e3})}async consoleMessages(t){return await this.initializedPromise,this._consoleMessages.filter(e=>t?e.type===t:!0)}async requests(){return await this.initializedPromise,this._requests}async captureSnapshot(){let t;const e=await this._raceAgainstModalStates(async()=>{const s=await this.page._snapshotForAI({track:"response"});t={url:this.page.url(),title:await this.page.title(),ariaSnapshot:s.full,ariaSnapshotDiff:this._needsFullSnapshot?void 0:s.incremental,modalStates:[],consoleMessages:[],downloads:this._downloads}});return t&&(t.consoleMessages=this._recentConsoleMessages,this._recentConsoleMessages=[]),this._needsFullSnapshot=!t,t??{url:this.page.url(),title:"",ariaSnapshot:"",modalStates:e,consoleMessages:[],downloads:[]}}_javaScriptBlocked(){return this._modalStates.some(t=>t.type==="dialog")}async _raceAgainstModalStates(t){if(this.modalStates().length)return this.modalStates();const e=new g.ManualPromise,s=o=>e.resolve([o]);return this.once(r.modalState,s),await Promise.race([t().then(()=>(this.off(r.modalState,s),[])),e])}async waitForCompletion(t){await this._raceAgainstModalStates(()=>(0,n.waitForCompletion)(this,t))}async refLocator(t){return(await this.refLocators([t]))[0]}async refLocators(t){return Promise.all(t.map(async e=>{try{const s=this.page.locator(`aria-ref=${e.ref}`).describe(e.element),{resolvedSelector:o}=await s._resolveSelector();let i=null;try{i=await s.getAttribute("data-zibby-id")}catch{}return{locator:s,resolved:(0,g.asLocator)("javascript",o),stableId:i}}catch{throw new Error(`Ref ${e.ref} not found in the current page snapshot. Try capturing new snapshot.`)}}))}async waitForTimeout(t){if(this._javaScriptBlocked()){await new Promise(e=>setTimeout(e,t));return}await(0,n.callOnPageNoTrace)(this.page,e=>e.evaluate(()=>new Promise(s=>setTimeout(s,1e3))))}}function p(a){return{type:a.type(),text:a.text(),toString:()=>`[${a.type().toUpperCase()}] ${a.text()} @ ${a.location().url}:${a.location().lineNumber}`}}function _(a){return a instanceof Error?{type:"error",text:a.message,toString:()=>a.stack||a.message}:{type:"error",text:String(a),toString:()=>String(a)}}function m(a,t){const e=["### Modal state"];t.length===0&&e.push("- There is no modal state present");for(const s of t)e.push(`- [${s.description}]: can be handled by the "${s.clearedBy}" tool`);return e}const w=Symbol("tabSymbol");
@@ -0,0 +1 @@
1
+ "use strict";const r=Object.defineProperty,w=Object.getOwnPropertyDescriptor,p=Object.getOwnPropertyNames,d=Object.prototype.hasOwnProperty,b=(t,e)=>{for(const o in e)r(t,o,{get:e[o],enumerable:!0})},l=(t,e,o,c)=>{if(e&&typeof e=="object"||typeof e=="function")for(const i of p(e))!d.call(t,i)&&i!==o&&r(t,i,{get:()=>e[i],enumerable:!(c=w(e,i))||c.enumerable});return t},h=t=>l(r({},"__esModule",{value:!0}),t),s={};b(s,{default:()=>y}),module.exports=h(s);const n=require("../../sdk/bundle"),a=require("./tool"),_=(0,a.defineTool)({capability:"core",schema:{name:"browser_close",title:"Close browser",description:"Close the page",inputSchema:n.z.object({}),type:"action"},handle:async(t,e,o)=>{typeof global.__zibbyRecordEvent=="function"&&global.__zibbyRecordEvent("close",{}),await t.closeBrowserContext(),o.setIncludeTabs(),o.addCode("await page.close()")}}),u=(0,a.defineTabTool)({capability:"core",schema:{name:"browser_resize",title:"Resize browser window",description:"Resize the browser window",inputSchema:n.z.object({width:n.z.number().describe("Width of the browser window"),height:n.z.number().describe("Height of the browser window")}),type:"action"},handle:async(t,e,o)=>{o.addCode(`await page.setViewportSize({ width: ${e.width}, height: ${e.height} });`),await t.waitForCompletion(async()=>{await t.page.setViewportSize({width:e.width,height:e.height})})}});var y=[_,u];
@@ -0,0 +1 @@
1
+ "use strict";const s=Object.defineProperty,d=Object.getOwnPropertyDescriptor,y=Object.getOwnPropertyNames,b=Object.prototype.hasOwnProperty,u=(t,e)=>{for(const o in e)s(t,o,{get:e[o],enumerable:!0})},_=(t,e,o,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(const a of y(e))!b.call(t,a)&&a!==o&&s(t,a,{get:()=>e[a],enumerable:!(n=d(e,a))||n.enumerable});return t},f=t=>_(s({},"__esModule",{value:!0}),t),c={};u(c,{default:()=>k}),module.exports=f(c);const r=require("../../sdk/bundle"),l=require("./tool"),h=require("./snapshot"),w=(0,l.defineTabTool)({capability:"core",schema:{name:"browser_press_key",title:"Press a key",description:"Press a key on the keyboard",inputSchema:r.z.object({key:r.z.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")}),type:"input"},handle:async(t,e,o)=>{o.setIncludeSnapshot(),o.addCode(`// Press ${e.key}`),o.addCode(`await page.keyboard.press('${e.key}');`),await t.waitForCompletion(async()=>{await t.page.keyboard.press(e.key)})}}),g=h.elementSchema.extend({text:r.z.string().describe("Text to type into the element"),submit:r.z.boolean().optional().describe("Whether to submit entered text (press Enter after)"),slowly:r.z.boolean().optional().describe("Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.")}),m=(0,l.defineTabTool)({capability:"core",schema:{name:"browser_type",title:"Type text",description:"Type text into editable element",inputSchema:g,type:"input"},handle:async(t,e,o)=>{const{locator:n,resolved:a,stableId:p}=await t.refLocator(e),i=t.context.lookupSecret(e.text);await t.waitForCompletion(async()=>{e.slowly?(o.setIncludeSnapshot(),o.addCode(`await page.${a}.pressSequentially(${i.code});`),await n.pressSequentially(i.value)):(o.addCode(`await page.${a}.fill(${i.code});`),await n.fill(i.value)),e.submit&&(o.setIncludeSnapshot(),o.addCode(`await page.${a}.press('Enter');`),await n.press("Enter"))}),typeof global.__zibbyRecordEvent=="function"&&global.__zibbyRecordEvent("type",{element:e.element,ref:e.ref,text:e.text,stableId:p})}});var k=[w,m];
@@ -0,0 +1 @@
1
+ "use strict";const c=Object.defineProperty,p=Object.getOwnPropertyDescriptor,b=Object.getOwnPropertyNames,l=Object.prototype.hasOwnProperty,g=(t,e)=>{for(const a in e)c(t,a,{get:e[a],enumerable:!0})},u=(t,e,a,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(const o of b(e))!l.call(t,o)&&o!==a&&c(t,o,{get:()=>e[o],enumerable:!(n=p(e,o))||n.enumerable});return t},_=t=>u(c({},"__esModule",{value:!0}),t),r={};g(r,{default:()=>v}),module.exports=_(r);const i=require("../../sdk/bundle"),s=require("./tool"),d=(0,s.defineTool)({capability:"core",schema:{name:"browser_navigate",title:"Navigate to a URL",description:"Navigate to a URL",inputSchema:i.z.object({url:i.z.string().describe("The URL to navigate to")}),type:"action"},handle:async(t,e,a)=>{await(await t.ensureTab()).navigate(e.url),a.setIncludeSnapshot(),a.addCode(`await page.goto('${e.url}');`),typeof global.__zibbyRecordEvent=="function"&&global.__zibbyRecordEvent("navigate",{url:e.url})}}),y=(0,s.defineTabTool)({capability:"core",schema:{name:"browser_navigate_back",title:"Go back",description:"Go back to the previous page",inputSchema:i.z.object({}),type:"action"},handle:async(t,e,a)=>{await t.page.goBack(),a.setIncludeSnapshot(),a.addCode("await page.goBack();")}});var v=[d,y];
@@ -0,0 +1 @@
1
+ "use strict";const m=Object.create,s=Object.defineProperty,_=Object.getOwnPropertyDescriptor,y=Object.getOwnPropertyNames,g=Object.getPrototypeOf,w=Object.prototype.hasOwnProperty,v=(t,e)=>{for(const o in e)s(t,o,{get:e[o],enumerable:!0})},u=(t,e,o,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(const n of y(e))!w.call(t,n)&&n!==o&&s(t,n,{get:()=>e[n],enumerable:!(i=_(e,n))||i.enumerable});return t},z=(t,e,o)=>(o=t!=null?m(g(t)):{},u(e||!t||!t.__esModule?s(o,"default",{value:t,enumerable:!0}):o,t)),O=t=>u(s({},"__esModule",{value:!0}),t),f={};v(f,{default:()=>x,elementSchema:()=>l}),module.exports=O(f);const a=require("../../sdk/bundle"),c=require("./tool"),h=z(require("../codegen")),S=(0,c.defineTool)({capability:"core",schema:{name:"browser_snapshot",title:"Page snapshot",description:"Capture accessibility snapshot of the current page, this is better than screenshot",inputSchema:a.z.object({}),type:"readOnly"},handle:async(t,e,o)=>{await t.ensureTab(),o.setIncludeSnapshot("full"),typeof global.__zibbyRecordEvent=="function"&&global.__zibbyRecordEvent("snapshot",{})}}),l=a.z.object({element:a.z.string().describe("Human-readable element description used to obtain permission to interact with the element"),ref:a.z.string().describe("Exact target element reference from the page snapshot")}),k=l.extend({doubleClick:a.z.boolean().optional().describe("Whether to perform a double click instead of a single click"),button:a.z.enum(["left","right","middle"]).optional().describe("Button to click, defaults to left"),modifiers:a.z.array(a.z.enum(["Alt","Control","ControlOrMeta","Meta","Shift"])).optional().describe("Modifier keys to press")}),C=(0,c.defineTabTool)({capability:"core",schema:{name:"browser_click",title:"Click",description:"Perform click on a web page",inputSchema:k,type:"input"},handle:async(t,e,o)=>{o.setIncludeSnapshot();const{locator:i,resolved:n,stableId:r}=await t.refLocator(e),d={button:e.button,modifiers:e.modifiers},p=h.formatObject(d," ","oneline"),b=p!=="{}"?p:"";e.doubleClick?o.addCode(`await page.${n}.dblclick(${b});`):o.addCode(`await page.${n}.click(${b});`),await t.waitForCompletion(async()=>{e.doubleClick?await i.dblclick(d):await i.click(d)}),typeof global.__zibbyRecordEvent=="function"&&global.__zibbyRecordEvent("click",{element:e.element,ref:e.ref,stableId:r})}}),E=(0,c.defineTabTool)({capability:"core",schema:{name:"browser_drag",title:"Drag mouse",description:"Perform drag and drop between two elements",inputSchema:a.z.object({startElement:a.z.string().describe("Human-readable source element description used to obtain the permission to interact with the element"),startRef:a.z.string().describe("Exact source element reference from the page snapshot"),endElement:a.z.string().describe("Human-readable target element description used to obtain the permission to interact with the element"),endRef:a.z.string().describe("Exact target element reference from the page snapshot")}),type:"input"},handle:async(t,e,o)=>{o.setIncludeSnapshot();const[i,n]=await t.refLocators([{ref:e.startRef,element:e.startElement},{ref:e.endRef,element:e.endElement}]);await t.waitForCompletion(async()=>{await i.locator.dragTo(n.locator)}),o.addCode(`await page.${i.resolved}.dragTo(page.${n.resolved});`)}}),T=(0,c.defineTabTool)({capability:"core",schema:{name:"browser_hover",title:"Hover mouse",description:"Hover over element on page",inputSchema:l,type:"input"},handle:async(t,e,o)=>{o.setIncludeSnapshot();const{locator:i,resolved:n,stableId:r}=await t.refLocator(e);o.addCode(`await page.${n}.hover();`),await t.waitForCompletion(async()=>{await i.hover()}),typeof global.__zibbyRecordEvent=="function"&&global.__zibbyRecordEvent("hover",{element:e.element,ref:e.ref,stableId:r})}}),P=l.extend({values:a.z.array(a.z.string()).describe("Array of values to select in the dropdown. This can be a single value or multiple values.")}),j=(0,c.defineTabTool)({capability:"core",schema:{name:"browser_select_option",title:"Select option",description:"Select an option in a dropdown",inputSchema:P,type:"input"},handle:async(t,e,o)=>{o.setIncludeSnapshot();const{locator:i,resolved:n,stableId:r}=await t.refLocator(e);o.addCode(`await page.${n}.selectOption(${h.formatObject(e.values)});`),await t.waitForCompletion(async()=>{await i.selectOption(e.values)}),typeof global.__zibbyRecordEvent=="function"&&global.__zibbyRecordEvent("select",{element:e.element,ref:e.ref,values:e.values,stableId:r})}}),R=(0,c.defineTabTool)({capability:"testing",schema:{name:"browser_generate_locator",title:"Create locator for element",description:"Generate locator for the given element to use in tests",inputSchema:l,type:"readOnly"},handle:async(t,e,o)=>{const{resolved:i}=await t.refLocator(e);o.addResult(i)}});var x=[S,C,E,T,j,R];
@@ -0,0 +1 @@
1
+ "use strict";var l=Object.create;var c=Object.defineProperty;var T=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var u=Object.getPrototypeOf,m=Object.prototype.hasOwnProperty;var h=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of p(t))!m.call(e,s)&&s!==r&&c(e,s,{get:()=>t[s],enumerable:!(n=T(t,s))||n.enumerable});return e};var P=(e,t,r)=>(r=e!=null?l(u(e)):{},h(t||!e||!e.__esModule?c(r,"default",{value:e,enumerable:!0}):r,e));const _=Object.create,a=Object.defineProperty,b=Object.getOwnPropertyDescriptor,C=Object.getOwnPropertyNames,q=Object.getPrototypeOf,R=Object.prototype.hasOwnProperty,v=(e,t)=>{for(const r in t)a(e,r,{get:t[r],enumerable:!0})},S=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(const s of C(t))!R.call(e,s)&&s!==r&&a(e,s,{get:()=>t[s],enumerable:!(n=b(t,s))||n.enumerable});return e},d=(e,t,r)=>(r=e!=null?_(q(e)):{},S(t||!e||!e.__esModule?a(r,"default",{value:e,enumerable:!0}):r,e)),g=e=>S(a({},"__esModule",{value:!0}),e),i={};v(i,{CallToolRequestSchema:()=>x,Client:()=>E,ListRootsRequestSchema:()=>M,ListToolsRequestSchema:()=>k,PingRequestSchema:()=>B,ProgressNotificationSchema:()=>D,SSEClientTransport:()=>z,SSEServerTransport:()=>H,Server:()=>j,StdioClientTransport:()=>L,StdioServerTransport:()=>w,StreamableHTTPClientTransport:()=>J,StreamableHTTPServerTransport:()=>N,z:()=>I,zodToJsonSchema:()=>y}),module.exports=g(i);const O=require.resolve("playwright"),f=require("path").join(require("path").dirname(O),"lib","mcpBundleImpl"),o=d(require(f)),y=o.zodToJsonSchema,E=o.Client,j=o.Server,z=o.SSEClientTransport,H=o.SSEServerTransport,L=o.StdioClientTransport,w=o.StdioServerTransport,N=o.StreamableHTTPServerTransport,J=o.StreamableHTTPClientTransport,x=o.CallToolRequestSchema,M=o.ListRootsRequestSchema,D=o.ProgressNotificationSchema,k=o.ListToolsRequestSchema,B=o.PingRequestSchema,I=o.z;
@@ -0,0 +1 @@
1
+ (function(){if(window.__zibbyInitialized)return;window.__zibbyInitialized=!0;function y(t){if(t.getAttribute("aria-label"))return t.getAttribute("aria-label").trim();const e=t.getAttribute("aria-labelledby");if(e){const n=document.getElementById(e);if(n)return n.textContent.trim()}if(t.id){const n=document.querySelector(`label[for="${t.id}"]`);if(n)return n.textContent.trim()}const o=t.closest("label");if(o){const n=o.cloneNode(!0);n.querySelectorAll("input, select, textarea").forEach(c=>c.remove());const a=n.textContent.trim();if(a)return a}if(t.placeholder)return t.placeholder;const r=t.tagName.toLowerCase();return r==="button"||r==="a"||t.getAttribute("role")==="button"?(t.textContent||"").trim().slice(0,50):t.title?t.title:r==="input"&&(t.type==="submit"||t.type==="button")&&t.value||""}function p(t){const e=[],o=t.closest("form");if(o)if(o.id)e.push("form#"+o.id);else if(o.name)e.push("form[name="+o.name+"]");else if(o.action)try{const i=new URL(o.action,window.location.origin).pathname;e.push("form[action="+i+"]")}catch{e.push("form[action="+o.getAttribute("action")+"]")}else{const i=document.querySelectorAll("form"),s=Array.from(i).indexOf(o);e.push("form:nth("+s+")")}const r=t.closest('header, nav, main, footer, aside, [role="banner"], [role="navigation"], [role="main"], [role="contentinfo"]');if(r){const i=r.tagName.toLowerCase(),s=r.getAttribute("role");e.push(s||i)}const n=t.closest('section, article, [role="region"]');if(n){const i=n.querySelector("h1, h2, h3, h4, h5, h6");i&&e.push("section:"+i.textContent.trim().slice(0,30))}const a=t.closest("fieldset");if(a){const i=a.querySelector("legend");i&&e.push("fieldset:"+i.textContent.trim())}const c=t.closest('dialog, [role="dialog"], [role="alertdialog"]');if(c){const i=c.querySelector('[role="heading"], h1, h2, h3');i?e.push("dialog:"+i.textContent.trim().slice(0,30)):e.push("dialog")}return e.join("/")}function f(t){const e=t.tagName.toLowerCase(),o=t.id||"",r=t.name||"",n=t.type||"",a=t.getAttribute("role")||"";let c="";if(t.href)try{c=new URL(t.href,window.location.origin).pathname.slice(0,50)}catch{c=t.getAttribute("href")?.slice(0,50)||""}const i=y(t).slice(0,50).replace(/\s+/g," "),s=p(t),h=[e,o,r,n,a,c,i,s].join("|");let l=5381;for(let b=0;b<h.length;b++)l=(l<<5)+l^h.charCodeAt(b);return"zibby-"+(l>>>0).toString(36)}function m(){if(!document.body)return;const t=["button","a","input","select","textarea","label[for]",'[role="button"]','[role="link"]','[role="textbox"]','[role="checkbox"]','[role="radio"]','[role="combobox"]','[role="menuitem"]','[role="tab"]','[role="option"]','[role="switch"]','[role="slider"]',"[onclick]","[data-action]"].join(", "),e=new Map;try{document.querySelectorAll(t).forEach(o=>{const r=window.getComputedStyle(o);if(r.display==="none"||r.visibility==="hidden")return;let n=f(o);const a=n,c=e.get(a)||0;c>0&&(n=a+"-"+c),e.set(a,c+1),o.setAttribute("data-zibby-id",n)})}catch(o){console.error("[Zibby] Error injecting stable IDs:",o)}}function g(){if(!document.body&&document.readyState==="loading"){document.addEventListener("DOMContentLoaded",g);return}m();const t=new MutationObserver(()=>{requestAnimationFrame(m)});document.body&&t.observe(document.body,{childList:!0,subtree:!0})}window.__zibbyGetStableId=function(t){return t?.getAttribute("data-zibby-id")||null};function w(){const t=window.location.hostname.split(".");return t.length>=2?"."+t.slice(-2).join("."):window.location.hostname}Object.defineProperty(window,"__zibbyLastStableId",{get:function(){const t=document.cookie.match(/(?:^|; )__zibbyLastStableId=([^;]*)/);return t?decodeURIComponent(t[1]):null},set:function(t){if(t){const e=w();document.cookie=`__zibbyLastStableId=${encodeURIComponent(t)}; domain=${e}; path=/; max-age=3600; SameSite=Lax`}}});function d(t){const e=t.closest("[data-zibby-id]");if(e)return e;const o=t.closest("label");if(o){if(o.htmlFor){const a=document.getElementById(o.htmlFor);if(a?.getAttribute("data-zibby-id"))return a}const n=o.querySelector('input, select, textarea, [role="checkbox"], [role="radio"]');if(n?.getAttribute("data-zibby-id"))return n}const r=t.closest('[class*="select"], [class*="dropdown"], [class*="checkbox"], [class*="radio"]');if(r){const n=r.querySelector("[data-zibby-id]");if(n)return n}return null}function u(t){if(!t)return null;const e=t.getAttribute("data-zibby-id");if(e)return e;const o=f(t);return t.setAttribute("data-zibby-id",o),o}document.addEventListener("click",t=>{let e=d(t.target);!e&&t.target.matches('button, a, input, select, textarea, [role="button"], [onclick]')&&(e=t.target),e&&(window.__zibbyLastStableId=u(e))},!0),document.addEventListener("focusin",t=>{let e=d(t.target);!e&&t.target.matches('input, select, textarea, [role="textbox"], [role="combobox"]')&&(e=t.target),e&&(window.__zibbyLastStableId=u(e))},!0),document.addEventListener("change",t=>{let e=d(t.target);e||(e=t.target),e&&(window.__zibbyLastStableId=u(e))},!0),g()})();
package/package.json CHANGED
@@ -1,20 +1,19 @@
1
1
  {
2
2
  "name": "@zibby/mcp-browser",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Wrapper for @playwright/mcp with stable ID support and event recording",
5
5
  "type": "commonjs",
6
- "main": "bin/mcp-browser-zibby.js",
6
+ "main": "dist/bin/mcp-browser-zibby.js",
7
7
  "bin": {
8
- "mcp-browser-zibby": "bin/mcp-browser-zibby.js"
8
+ "mcp-browser-zibby": "dist/bin/mcp-browser-zibby.js"
9
9
  },
10
10
  "files": [
11
- "bin/",
12
- "lib/",
13
- "src/",
11
+ "dist/",
14
12
  "README.md",
15
13
  "LICENSE"
16
14
  ],
17
15
  "scripts": {
16
+ "build": "node ../../scripts/build.mjs --format cjs --extra-dirs bin lib",
18
17
  "start": "node bin/mcp-browser-zibby.js",
19
18
  "lint": "eslint .",
20
19
  "lint:fix": "eslint --fix ."
@@ -34,12 +33,15 @@
34
33
  "url": "https://github.com/ZibbyHQ/zibby-agent/issues"
35
34
  },
36
35
  "dependencies": {
37
- "@playwright/mcp": "^0.0.54"
36
+ "@playwright/mcp": "^0.0.70"
38
37
  },
39
38
  "engines": {
40
39
  "node": ">=18.0.0"
41
40
  },
42
41
  "license": "MIT",
43
42
  "author": "Zibby",
44
- "homepage": "https://zibby.app"
43
+ "homepage": "https://zibby.app",
44
+ "devDependencies": {
45
+ "esbuild": "^0.28.0"
46
+ }
45
47
  }