@shiplightai/mcp 0.1.23 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +4 -4
  2. package/package.json +16 -16
package/dist/index.js CHANGED
@@ -5427,7 +5427,7 @@ ${e}`,i?e=`${e}
5427
5427
  ${f}`)),m.push(l),m.push(`Interactive elements from top layer of the current page inside the viewport:
5428
5428
  ${e}`),m.join(`
5429
5429
 
5430
- `)}async getLocator(e,t){let r=this.sessions.get(e);if(!r)throw new Error(`Session ${e} not found`);if(!r.domState)throw new Error(`No DOM state cached for session ${e}. Call inspectPage first.`);let n=r.domState.selectorMap.get(t);if(!n)throw new Error(`Element index ${t} not found in DOM state. Available indices: ${Array.from(r.domState.selectorMap.keys()).join(", ")}`);let i=this.getPageForSession(r),s=await Bl(i,n),o=n.getAllTextTillNextClickableElement(2);return{element_index:t,locator:s.locator||null,xpath:s.xpath||n.xpath,frame_path:s.frame_path||[],tag_name:n.tagName,text:o.trim()}}getConsoleLogs(e,t){let r=this.sessions.get(e);if(!r)throw new Error(`Session ${e} not found`);let n=r.consoleLogs;return t?.sinceTimestamp!==void 0&&(n=n.filter(i=>i.timestamp>=t.sinceTimestamp)),t?.logTypes&&t.logTypes.length>0&&(n=n.filter(i=>t.logTypes.includes(i.type))),n}getNetworkLogs(e,t){let r=this.sessions.get(e);if(!r)throw new Error(`Session ${e} not found`);let n=r.networkLogs;return t?.sinceTimestamp!==void 0&&(n=n.filter(i=>i.timestamp>=t.sinceTimestamp)),t?.statusFilter&&(t.statusFilter==="errors"?n=n.filter(i=>i.status&&i.status>=400):t.statusFilter==="success"&&(n=n.filter(i=>i.status&&i.status>=200&&i.status<300))),n}clearLogs(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);t.consoleLogs.length=0,t.networkLogs.length=0}async runTask(e,t,r){let n=this.sessions.get(e);if(!n)throw new Error(`Session ${e} not found`);n.abortController.signal.aborted&&(n.abortController=new AbortController);let i=r?.onEvent||this.createDefaultEventLogger();try{let s=this.getPageForSession(n),a=await(await Promise.resolve().then(()=>(eE(),PEe))).runTask(t,s,n.webAgent.agentServices,i,{abortSignal:r?.abortSignal||n.abortController.signal,maxSteps:r?.maxSteps,executionHistory:n.agentContext.executionHistory,variables:n.variableStore.getAll?.()||{},sensitiveKeys:n.variableStore.getAllSensitiveKeys?.()||[]});return{success:a.status==="success"&&a.completed,actions:a.actionEntities||[],details:a.explanation||a.error,executionHistory:n.agentContext.executionHistory}}catch(s){return{success:!1,details:s instanceof Error?s.message:String(s)}}}createDefaultEventLogger(){return e=>{console.log("[run_task event]",JSON.stringify(e,null,2))}}async stopTask(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.abortController.signal.aborted?!1:(t.abortController.abort(),!0)}async act(e,t,r){let n=this.sessions.get(e);if(!n)throw new Error(`Session ${e} not found`);if(!n.domState)throw new Error(`No DOM state cached for session ${e}. Call inspectPage first.`);let i=await Promise.resolve().then(()=>(eE(),PEe)),{toolRegistry:s,DomService:o}=i,a=r?.stopOnError??!0,u=this.getPageForSession(n),l=[],c=!0;for(let d of t){let p=Object.keys(d)[0],f=d[p]||{};if(UEe(p)&&!qEe()){if(l.push({success:!1,error:HEe(p)}),c=!1,a)break;continue}try{let m={page:u,agentServices:n.webAgent.agentServices,domService:new o,domState:n.domState,actionDescription:f.description},C=await s.execute(p,f,m);if(l.push({success:C.success,action_entity:C.actionEntity,message:C.message,error:C.error}),!C.success&&(c=!1,a))break}catch(m){let C=m instanceof Error?m.message:String(m);if(l.push({success:!1,error:C}),c=!1,a)break}}return{success:c,results:l}}updateVariables(e,t,r){let n=this.sessions.get(e);if(!n)throw new Error(`Session ${e} not found`);let i=new Set(r||[]);for(let[s,o]of Object.entries(t)){let a=i.has(s);n.variableStore.set(s,o,a)}}getVariables(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.variableStore.getAll()}getVariableNames(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return Object.keys(t.variableStore.getAll())}clearExecutionHistory(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);t.agentContext.executionHistory=[]}addExecutionHistory(e,t,r){let n=this.sessions.get(e);if(!n)throw new Error(`Session ${e} not found`);n.agentContext.executionHistory?.push([t,r])}getExecutionHistory(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.agentContext.executionHistory||[]}getRemainingActions(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);let r=t.maxActions??1/0;return Math.max(0,r-t.totalActions)}addActions(e,t){let r=this.sessions.get(e);if(!r)throw new Error(`Session ${e} not found`);r.totalActions+=t}isMaxActionsReached(e){return this.getRemainingActions(e)<=0}getTotalActions(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.totalActions}createAbortController(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);let r=new AbortController;return t.abortController.signal.aborted?r.abort():t.abortController.signal.addEventListener("abort",()=>{r.abort()},{once:!0}),r}abortTask(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.abortController.signal.aborted?!1:(t.abortController.abort(),!0)}resetAbortController(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);t.abortController=new AbortController}isAborted(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.abortController.signal.aborted}getAbortSignal(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.abortController.signal}getAbortController(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.abortController}async getLocalArtifact(e){if(!Nf.existsSync(e))throw new Error(`Artifact not found: ${e}`);let t=Nf.statSync(e),r=vg.extname(e).toLowerCase();return[".png",".jpg",".jpeg",".gif",".webp"].includes(r)?{data:Nf.readFileSync(e).toString("base64"),format:"base64",size:t.size}:{data:Nf.readFileSync(e,"utf-8"),format:"text",size:t.size}}async saveStorageState(e,t){let r=this.sessions.get(e);if(!r)throw new Error(`Session ${e} not found`);let n=vg.dirname(t);Nf.mkdirSync(n,{recursive:!0}),await this.getPageForSession(r).context().storageState({path:t,indexedDB:!0})}getSessionDir(e){return vg.join(this.runDir,e)}getRunDir(){return this.runDir}},WEe=class{http;getApiToken;maxRetries;initialDelayMs;maxDelayMs;constructor(e){this.getApiToken=e.getApiToken,this.maxRetries=e.maxRetries??3,this.initialDelayMs=e.initialDelayMs??500,this.maxDelayMs=e.maxDelayMs??5e3,this.http=Dxr.create({baseURL:e.apiBaseUrl})}async getAuthHeaders(){let t={Authorization:`Bearer ${await this.getApiToken()}`},r=process.env.ORGANIZATION_ID||process.env.LOGGIA_ORGANIZATION_ID||process.env.SHIPLIGHT_ORGANIZATION_ID;return r&&(t["organization-id"]=r),t}async retryRequest(e){let t,r=this.initialDelayMs;for(let n=0;n<=this.maxRetries;n++)try{return await e()}catch(i){if(t=i,n<this.maxRetries){let s=i;if(s.code==="ECONNRESET"||s.code==="ETIMEDOUT"||s.response&&s.response.status>=500){await new Promise(o=>setTimeout(o,r)),r=Math.min(r*2,this.maxDelayMs);continue}}throw i}throw t}async getTestAccount(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.get(`/v1/test-accounts/${e}`,{headers:t}))).data}async listTestAccounts(e,t){let r=await this.getAuthHeaders(),n=e;if(!n&&t){let a=(await this.listEnvironments()).find(u=>u.url===t);if(!a)throw new Error(`No environment found with URL: ${t}`);n=a.id}let i="/v1/test-accounts";return n!==void 0&&(i+=`?environmentId=${n}`),(await this.retryRequest(()=>this.http.get(i,{headers:r}))).data}async createTestAccount(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.post("/v1/test-accounts",{username:e.username,password:e.password,name:e.name,environmentId:e.environmentId,loginConfig:e.loginConfig},{headers:t}))).data}async getEnvironment(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.get(`/v1/environments/${e}`,{headers:t}))).data}async listEnvironments(){let e=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.get("/v1/environments",{headers:e}))).data}async getFolder(e){let t=await this.getAuthHeaders(),r=await this.retryRequest(()=>this.http.get(`/v1/test-folders/${e}`,{headers:t}));return r.data.data||r.data}async listFolders(e,t){let r=await this.getAuthHeaders(),n,i=new URLSearchParams;e!==void 0?(n="/v1/test-folders",i.append("parentId",e===null?"null":String(e))):n="/v1/test-folders/all",t&&i.append("search",t);let s=i.toString();s&&(n+=`?${s}`);let o=await this.retryRequest(()=>this.http.get(n,{headers:r}));return o.data.data||o.data}async createFolder(e){let t=await this.getAuthHeaders(),r=await this.retryRequest(()=>this.http.post("/v1/test-folders",{name:e.name,description:e.description,parentId:e.parentId},{headers:t}));return r.data.data||r.data}async getTestCase(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.get(`/v1/test-cases/${e}`,{headers:t}))).data}async createTestCase(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.post("/v1/test-cases",{title:e.title,test_flow:e.testFlow,folder_id:e.folderId,environment_configs:e.environmentConfigs},{headers:t}))).data}async updateTestCase(e,t){let r=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.put(`/v1/test-cases/${e}`,{title:t.title,test_flow:t.testFlow},{headers:r}))).data}async generateTest(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.post("/v1/test-batch-gen-tasks/create",{title:e.title,startingUrl:e.startingUrl,goal:e.goal,environmentId:e.environmentId,testAccountGroup:e.testAccountGroup,folderId:e.folderId,creation_mode:"SINGLE"},{headers:t}))).data.testCase}async listTestRuns(e){let t=await this.getAuthHeaders(),r=new URLSearchParams;e?.testPlanId!==void 0&&r.append("testPlanId",String(e.testPlanId)),e?.trigger&&r.append("trigger",e.trigger),e?.result&&r.append("result",e.result),e?.limit!==void 0&&r.append("limit",String(e.limit));let n=r.toString(),i=n?`/v1/test-runs?${n}`:"/v1/test-runs";return(await this.retryRequest(()=>this.http.get(i,{headers:t}))).data}async getTestRunDetails(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.get(`/run-results/${e}`,{headers:t}))).data}async getTestCaseResult(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.get(`/test-case-results/${e}`,{headers:t}))).data}async runTestCase(e,t){let r=await this.getAuthHeaders(),n={trigger:"API"};return t&&(n.environment={id:t}),(await this.retryRequest(()=>this.http.post(`/v1/test-run/test-case/${e}`,n,{headers:r}))).data}async getS3FileContents(e){try{let t=await this.getAuthHeaders(),r=await this.retryRequest(()=>this.http.get("/v1/s3/file",{headers:t,params:{uri:e}}));return typeof r.data=="string"?r.data:JSON.stringify(r.data)}catch(t){return console.error("Failed to get S3 file contents:",t),null}}};var YEe=class{tools=new Map;registerAll(e,t){for(let r of e.toolDefinitions){let n=this.findMethodForTool(t,r.name);n&&this.tools.set(r.name,{definition:r,handler:t[n].bind(t)})}return this}registerTools(e,t,r){for(let n of r){let i=this.findDefinitionForMethod(e.toolDefinitions,n);i&&this.tools.set(i.name,{definition:i,handler:t[n].bind(t)})}return this}registerTool(e,t){return this.tools.set(e.name,{definition:e,handler:t}),this}build(){return{tools:Array.from(this.tools.values()).map(r=>r.definition),handleToolCall:async(r,n,i)=>{let s=this.tools.get(r);if(!s)throw new Error(`Unknown tool: ${r}`);return s.handler(n,i)}}}getRegisteredToolNames(){return Array.from(this.tools.keys())}hasToolRegistered(e){return this.tools.has(e)}findMethodForTool(e,t){let n={new_session:"newSession",save_storage_state:"saveStorageState",close_session:"closeSession",close_all:"closeAllSessions",get_session_state:"getCurrentState",navigate:"navigate",get_page_info:"getPageInfo",get_dom:"getDom",take_screenshot:"takeScreenshot",act:"act",get_locator:"getLocator",update_variables:"updateVariables",clear_execution_history:"clearExecutionHistory",get_browser_console_logs:"getConsoleLogs",get_browser_network_logs:"getNetworkLogs",clear_logs:"clearLogs",get_local_artifact:"getLocalArtifact",list_environments:"listEnvironments",list_test_accounts:"listTestAccounts",get_test_account:"getTestAccount",create_test_account:"createTestAccount",list_folders:"listFolders",create_folder:"createFolder",get_folder:"getFolder",get_test_case:"getTestCase",run_test_case:"runTestCase",save_test_case:"saveTestCase",run_test_flow:"runTestFlow",list_test_runs:"listTestRuns",get_test_run_details:"getTestRunDetails",get_test_case_result:"getTestCaseResult",get_test_case_result_steps:"getTestCaseResultSteps",get_step_artifacts:"getStepArtifacts",init_local_project:"initLocalProject",export_yaml_to_test:"exportYamlToTest"}[t];return n&&typeof e[n]=="function"?n:null}findDefinitionForMethod(e,t){let n={newSession:["new_session"],saveStorageState:["save_storage_state"],closeSession:["close_session"],closeAllSessions:["close_all"],getCurrentState:["get_session_state"],navigate:["navigate"],getPageInfo:["get_page_info"],getDom:["get_dom"],takeScreenshot:["take_screenshot"],act:["act"],getLocator:["get_locator"],updateVariables:["update_variables"],clearExecutionHistory:["clear_execution_history"],getConsoleLogs:["get_browser_console_logs"],getNetworkLogs:["get_browser_network_logs"],clearLogs:["clear_logs"],getLocalArtifact:["get_local_artifact"],listEnvironments:["list_environments"],listTestAccounts:["list_test_accounts"],getTestAccount:["get_test_account"],createTestAccount:["create_test_account"],listFolders:["list_folders"],createFolder:["create_folder"],getFolder:["get_folder"],getTestCase:["get_test_case"],runTestCase:["run_test_case"],saveTestCase:["save_test_case"],runTestFlow:["run_test_flow"],listTestRuns:["list_test_runs"],getTestRunDetails:["get_test_run_details"],getTestCaseResult:["get_test_case_result"],getTestCaseResultSteps:["get_test_case_result_steps"],getStepArtifacts:["get_step_artifacts"],initLocalProject:["init_local_project"],exportYamlToTest:["export_yaml_to_test"]}[t];if(!n)return null;for(let i of n){let s=e.find(o=>o.name===i);if(s)return s}return null}};var JEe={"browser-session-basics":{name:"browser-session-basics",description:"Basic browser session management for UI verification",content:`# Browser Session Basics
5430
+ `)}async getLocator(e,t){let r=this.sessions.get(e);if(!r)throw new Error(`Session ${e} not found`);if(!r.domState)throw new Error(`No DOM state cached for session ${e}. Call inspectPage first.`);let n=r.domState.selectorMap.get(t);if(!n)throw new Error(`Element index ${t} not found in DOM state. Available indices: ${Array.from(r.domState.selectorMap.keys()).join(", ")}`);let i=this.getPageForSession(r),s=await Bl(i,n),o=n.getAllTextTillNextClickableElement(2);return{element_index:t,locator:s.locator||null,xpath:s.xpath||n.xpath,frame_path:s.frame_path||[],tag_name:n.tagName,text:o.trim()}}getConsoleLogs(e,t){let r=this.sessions.get(e);if(!r)throw new Error(`Session ${e} not found`);let n=r.consoleLogs;return t?.sinceTimestamp!==void 0&&(n=n.filter(i=>i.timestamp>=t.sinceTimestamp)),t?.logTypes&&t.logTypes.length>0&&(n=n.filter(i=>t.logTypes.includes(i.type))),n}getNetworkLogs(e,t){let r=this.sessions.get(e);if(!r)throw new Error(`Session ${e} not found`);let n=r.networkLogs;return t?.sinceTimestamp!==void 0&&(n=n.filter(i=>i.timestamp>=t.sinceTimestamp)),t?.statusFilter&&(t.statusFilter==="errors"?n=n.filter(i=>i.status&&i.status>=400):t.statusFilter==="success"&&(n=n.filter(i=>i.status&&i.status>=200&&i.status<300))),n}clearLogs(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);t.consoleLogs.length=0,t.networkLogs.length=0}async runTask(e,t,r){let n=this.sessions.get(e);if(!n)throw new Error(`Session ${e} not found`);n.abortController.signal.aborted&&(n.abortController=new AbortController);let i=r?.onEvent||this.createDefaultEventLogger();try{let s=this.getPageForSession(n),a=await(await Promise.resolve().then(()=>(eE(),PEe))).runTask(t,s,n.webAgent.agentServices,i,{abortSignal:r?.abortSignal||n.abortController.signal,maxSteps:r?.maxSteps,executionHistory:n.agentContext.executionHistory,variables:n.variableStore.getAll?.()||{},sensitiveKeys:n.variableStore.getAllSensitiveKeys?.()||[]});return{success:a.status==="success"&&a.completed,actions:a.actionEntities||[],details:a.explanation||a.error,executionHistory:n.agentContext.executionHistory}}catch(s){return{success:!1,details:s instanceof Error?s.message:String(s)}}}createDefaultEventLogger(){return e=>{console.log("[run_task event]",JSON.stringify(e,null,2))}}async stopTask(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.abortController.signal.aborted?!1:(t.abortController.abort(),!0)}async act(e,t,r){let n=this.sessions.get(e);if(!n)throw new Error(`Session ${e} not found`);if(!n.domState)throw new Error(`No DOM state cached for session ${e}. Call inspectPage first.`);let i=await Promise.resolve().then(()=>(eE(),PEe)),{toolRegistry:s,DomService:o}=i,a=r?.stopOnError??!0,u=this.getPageForSession(n),l=[],c=!0;for(let d of t){let p=Object.keys(d)[0],f=d[p]||{};if(UEe(p)&&!qEe()){if(l.push({success:!1,error:HEe(p)}),c=!1,a)break;continue}try{let m={page:u,agentServices:n.webAgent.agentServices,domService:new o,domState:n.domState,actionDescription:f.description},C=await s.execute(p,f,m);if(l.push({success:C.success,action_entity:C.actionEntity,message:C.message,error:C.error}),!C.success&&(c=!1,a))break}catch(m){let C=m instanceof Error?m.message:String(m);if(l.push({success:!1,error:C}),c=!1,a)break}}return{success:c,results:l}}updateVariables(e,t,r){let n=this.sessions.get(e);if(!n)throw new Error(`Session ${e} not found`);let i=new Set(r||[]);for(let[s,o]of Object.entries(t)){let a=i.has(s);n.variableStore.set(s,o,a)}}getVariables(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.variableStore.getAll()}getVariableNames(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return Object.keys(t.variableStore.getAll())}clearExecutionHistory(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);t.agentContext.executionHistory=[]}addExecutionHistory(e,t,r){let n=this.sessions.get(e);if(!n)throw new Error(`Session ${e} not found`);n.agentContext.executionHistory?.push([t,r])}getExecutionHistory(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.agentContext.executionHistory||[]}getRemainingActions(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);let r=t.maxActions??1/0;return Math.max(0,r-t.totalActions)}addActions(e,t){let r=this.sessions.get(e);if(!r)throw new Error(`Session ${e} not found`);r.totalActions+=t}isMaxActionsReached(e){return this.getRemainingActions(e)<=0}getTotalActions(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.totalActions}createAbortController(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);let r=new AbortController;return t.abortController.signal.aborted?r.abort():t.abortController.signal.addEventListener("abort",()=>{r.abort()},{once:!0}),r}abortTask(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.abortController.signal.aborted?!1:(t.abortController.abort(),!0)}resetAbortController(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);t.abortController=new AbortController}isAborted(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.abortController.signal.aborted}getAbortSignal(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.abortController.signal}getAbortController(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);return t.abortController}async getLocalArtifact(e){if(!Nf.existsSync(e))throw new Error(`Artifact not found: ${e}`);let t=Nf.statSync(e),r=vg.extname(e).toLowerCase();return[".png",".jpg",".jpeg",".gif",".webp"].includes(r)?{data:Nf.readFileSync(e).toString("base64"),format:"base64",size:t.size}:{data:Nf.readFileSync(e,"utf-8"),format:"text",size:t.size}}async saveStorageState(e,t){let r=this.sessions.get(e);if(!r)throw new Error(`Session ${e} not found`);let n=vg.dirname(t);Nf.mkdirSync(n,{recursive:!0}),await this.getPageForSession(r).context().storageState({path:t,indexedDB:!0})}getSessionDir(e){return vg.join(this.runDir,e)}getRunDir(){return this.runDir}},WEe=class{http;getApiToken;maxRetries;initialDelayMs;maxDelayMs;constructor(e){this.getApiToken=e.getApiToken,this.maxRetries=e.maxRetries??3,this.initialDelayMs=e.initialDelayMs??500,this.maxDelayMs=e.maxDelayMs??5e3,this.http=Dxr.create({baseURL:e.apiBaseUrl})}async getAuthHeaders(){let t={Authorization:`Bearer ${await this.getApiToken()}`},r=process.env.ORGANIZATION_ID||process.env.LOGGIA_ORGANIZATION_ID||process.env.SHIPLIGHT_ORGANIZATION_ID;return r&&(t["organization-id"]=r),t}async retryRequest(e){let t,r=this.initialDelayMs;for(let n=0;n<=this.maxRetries;n++)try{return await e()}catch(i){if(t=i,n<this.maxRetries){let s=i;if(s.code==="ECONNRESET"||s.code==="ETIMEDOUT"||s.response&&s.response.status>=500){await new Promise(o=>setTimeout(o,r)),r=Math.min(r*2,this.maxDelayMs);continue}}throw i}throw t}async getTestAccount(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.get(`/v1/test-accounts/${e}`,{headers:t}))).data}async listTestAccounts(e,t){let r=await this.getAuthHeaders(),n=e;if(!n&&t){let a=(await this.listEnvironments()).find(u=>u.url===t);if(!a)throw new Error(`No environment found with URL: ${t}`);n=a.id}let i="/v1/test-accounts";return n!==void 0&&(i+=`?environmentId=${n}`),(await this.retryRequest(()=>this.http.get(i,{headers:r}))).data}async createTestAccount(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.post("/v1/test-accounts",{username:e.username,password:e.password,name:e.name,environmentId:e.environmentId,loginConfig:e.loginConfig},{headers:t}))).data}async getEnvironment(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.get(`/v1/environments/${e}`,{headers:t}))).data}async listEnvironments(){let e=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.get("/v1/environments",{headers:e}))).data}async getFolder(e){let t=await this.getAuthHeaders(),r=await this.retryRequest(()=>this.http.get(`/v1/test-folders/${e}`,{headers:t}));return r.data.data||r.data}async listFolders(e,t){let r=await this.getAuthHeaders(),n,i=new URLSearchParams;e!==void 0?(n="/v1/test-folders",i.append("parentId",e===null?"null":String(e))):n="/v1/test-folders/all",t&&i.append("search",t);let s=i.toString();s&&(n+=`?${s}`);let o=await this.retryRequest(()=>this.http.get(n,{headers:r}));return o.data.data||o.data}async createFolder(e){let t=await this.getAuthHeaders(),r=await this.retryRequest(()=>this.http.post("/v1/test-folders",{name:e.name,description:e.description,parentId:e.parentId},{headers:t}));return r.data.data||r.data}async getTestCase(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.get(`/v1/test-cases/${e}`,{headers:t}))).data}async createTestCase(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.post("/v1/test-cases",{title:e.title,test_flow:e.testFlow,folder_id:e.folderId,environment_configs:e.environmentConfigs},{headers:t}))).data}async updateTestCase(e,t){let r=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.put(`/v1/test-cases/${e}`,{title:t.title,test_flow:t.testFlow},{headers:r}))).data}async generateTest(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.post("/v1/test-batch-gen-tasks/create",{title:e.title,startingUrl:e.startingUrl,goal:e.goal,environmentId:e.environmentId,testAccountGroup:e.testAccountGroup,folderId:e.folderId,creation_mode:"SINGLE"},{headers:t}))).data.testCase}async listTestRuns(e){let t=await this.getAuthHeaders(),r=new URLSearchParams;e?.testPlanId!==void 0&&r.append("testPlanId",String(e.testPlanId)),e?.trigger&&r.append("trigger",e.trigger),e?.result&&r.append("result",e.result),e?.limit!==void 0&&r.append("limit",String(e.limit));let n=r.toString(),i=n?`/v1/test-runs?${n}`:"/v1/test-runs";return(await this.retryRequest(()=>this.http.get(i,{headers:t}))).data}async getTestRunDetails(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.get(`/run-results/${e}`,{headers:t}))).data}async getTestCaseResult(e){let t=await this.getAuthHeaders();return(await this.retryRequest(()=>this.http.get(`/test-case-results/${e}`,{headers:t}))).data}async runTestCase(e,t){let r=await this.getAuthHeaders(),n={trigger:"API"};return t&&(n.environment={id:t}),(await this.retryRequest(()=>this.http.post(`/v1/test-run/test-case/${e}`,n,{headers:r}))).data}async getS3FileContents(e){try{let t=await this.getAuthHeaders(),r=await this.retryRequest(()=>this.http.get("/v1/s3/file",{headers:t,params:{uri:e}}));return typeof r.data=="string"?r.data:JSON.stringify(r.data)}catch(t){return console.error("Failed to get S3 file contents:",t),null}}};var YEe={"browser-session-basics":{name:"browser-session-basics",description:"Basic browser session management for UI verification",content:`# Browser Session Basics
5431
5431
 
5432
5432
  1. Create: new_session(starting_url) - Launch browser session
5433
5433
  2. Get DOM: get_dom(session_id) - Get DOM tree with element indices
@@ -5440,7 +5440,7 @@ Workflow: get_dom \u2192 act \u2192 get_dom \u2192 act \u2192 ... until done
5440
5440
  act returns: { success, actions: [{ action_entity, success, error? }] }
5441
5441
 
5442
5442
  **Action parameters:** See resource \`shiplight://schemas/action-entity\`
5443
- `}};function zEe(e){return JEe[e]?.content}function KEe(){return Object.values(JEe).map(e=>({name:e.name,description:e.description}))}var SD=["click","double_click","right_click","hover","input_text","clear_input","press","send_keys_on_element","select_dropdown_option","get_dropdown_options","set_date_for_native_date_picker","scroll","scroll_to_text","scroll_on_element","go_to_url","go_back","reload_page","switch_tab","close_tab","wait","wait_for_page_ready","wait_for_download_complete","upload_file","save_variable","verify","ai_extract","ai_wait_until","generate_2fa_code"];eE();import{zodToJsonSchema as xxr}from"zod-to-json-schema";function Fxr(e,t){let r=[];r.push(`## Supported Actions
5443
+ `}};function JEe(e){return YEe[e]?.content}function zEe(){return Object.values(YEe).map(e=>({name:e.name,description:e.description}))}var KEe=class{tools=new Map;registerAll(e,t){for(let r of e.toolDefinitions){let n=this.findMethodForTool(t,r.name);n&&this.tools.set(r.name,{definition:r,handler:t[n].bind(t)})}return this}registerTools(e,t,r){for(let n of r){let i=this.findDefinitionForMethod(e.toolDefinitions,n);i&&this.tools.set(i.name,{definition:i,handler:t[n].bind(t)})}return this}registerTool(e,t){return this.tools.set(e.name,{definition:e,handler:t}),this}build(){return{tools:Array.from(this.tools.values()).map(r=>r.definition),handleToolCall:async(r,n,i)=>{let s=this.tools.get(r);if(!s)throw new Error(`Unknown tool: ${r}`);return s.handler(n,i)}}}getRegisteredToolNames(){return Array.from(this.tools.keys())}hasToolRegistered(e){return this.tools.has(e)}findMethodForTool(e,t){let n={new_session:"newSession",save_storage_state:"saveStorageState",close_session:"closeSession",close_all:"closeAllSessions",get_session_state:"getCurrentState",navigate:"navigate",get_page_info:"getPageInfo",get_dom:"getDom",take_screenshot:"takeScreenshot",act:"act",get_locator:"getLocator",update_variables:"updateVariables",clear_execution_history:"clearExecutionHistory",get_browser_console_logs:"getConsoleLogs",get_browser_network_logs:"getNetworkLogs",clear_logs:"clearLogs",get_local_artifact:"getLocalArtifact",list_environments:"listEnvironments",list_test_accounts:"listTestAccounts",get_test_account:"getTestAccount",create_test_account:"createTestAccount",list_folders:"listFolders",create_folder:"createFolder",get_folder:"getFolder",get_test_case:"getTestCase",run_test_case:"runTestCase",save_test_case:"saveTestCase",run_test_flow:"runTestFlow",list_test_runs:"listTestRuns",get_test_run_details:"getTestRunDetails",get_test_case_result:"getTestCaseResult",get_test_case_result_steps:"getTestCaseResultSteps",get_step_artifacts:"getStepArtifacts",init_local_project:"initLocalProject",export_yaml_to_test:"exportYamlToTest"}[t];return n&&typeof e[n]=="function"?n:null}findDefinitionForMethod(e,t){let n={newSession:["new_session"],saveStorageState:["save_storage_state"],closeSession:["close_session"],closeAllSessions:["close_all"],getCurrentState:["get_session_state"],navigate:["navigate"],getPageInfo:["get_page_info"],getDom:["get_dom"],takeScreenshot:["take_screenshot"],act:["act"],getLocator:["get_locator"],updateVariables:["update_variables"],clearExecutionHistory:["clear_execution_history"],getConsoleLogs:["get_browser_console_logs"],getNetworkLogs:["get_browser_network_logs"],clearLogs:["clear_logs"],getLocalArtifact:["get_local_artifact"],listEnvironments:["list_environments"],listTestAccounts:["list_test_accounts"],getTestAccount:["get_test_account"],createTestAccount:["create_test_account"],listFolders:["list_folders"],createFolder:["create_folder"],getFolder:["get_folder"],getTestCase:["get_test_case"],runTestCase:["run_test_case"],saveTestCase:["save_test_case"],runTestFlow:["run_test_flow"],listTestRuns:["list_test_runs"],getTestRunDetails:["get_test_run_details"],getTestCaseResult:["get_test_case_result"],getTestCaseResultSteps:["get_test_case_result_steps"],getStepArtifacts:["get_step_artifacts"],initLocalProject:["init_local_project"],exportYamlToTest:["export_yaml_to_test"]}[t];if(!n)return null;for(let i of n){let s=e.find(o=>o.name===i);if(s)return s}return null}};var SD=["click","double_click","right_click","hover","input_text","clear_input","press","send_keys_on_element","select_dropdown_option","get_dropdown_options","set_date_for_native_date_picker","scroll","scroll_to_text","scroll_on_element","go_to_url","go_back","reload_page","switch_tab","close_tab","wait","wait_for_page_ready","wait_for_download_complete","upload_file","save_variable","verify","ai_extract","ai_wait_until","generate_2fa_code"];eE();import{zodToJsonSchema as xxr}from"zod-to-json-schema";function Fxr(e,t){let r=[];r.push(`## Supported Actions
5444
5444
  `);for(let n of t){let i=e.get(n);if(!i)continue;let s=xxr(i.schema,{$refStrategy:"none"});r.push(`#### ${i.name}`),r.push(i.description),r.push("```json"),r.push(JSON.stringify(s,null,2)),r.push("```\n")}return r.join(`
5445
5445
  `)}var vrt=[{uri:"shiplight://schemas/testflow-json-v1.2.0",name:"TestFlow JSON Schema v1.2.0",description:"JSON schema for TestFlow format used by cloud API tools (save_test_case, get_test_case)",mimeType:"text/markdown"},{uri:"shiplight://schemas/testflow-yaml-v1.2.0",name:"TestFlow YAML Format v1.2.0",description:"YAML format for local .test.yaml files used by shiplightai \u2014 includes templates, variables, use block, and Playwright integration",mimeType:"text/markdown"},{uri:"shiplight://schemas/action-entity",name:"ActionEntity Schema & Action Parameters",description:"Detailed parameter documentation for supported browser actions",mimeType:"text/markdown"}];function Drt(){return`# TestFlow JSON Schema v1.2.0
5446
5446
 
@@ -8023,7 +8023,7 @@ ${t}`)}error(e,t){this.log(`ERROR: ${e}`),t&&(this.log(`Error details: ${t.messa
8023
8023
  - 66: ENTER
8024
8024
  - 82: MENU
8025
8025
  - 187: APP_SWITCH`)});var _ss=ov.object({});var Sss=ov.object({});var kss=Ul.object({seconds:Ul.number().min(0).max(30).describe("Number of seconds to wait (max 30)")});var Rss=Ul.object({locator:Ul.string().describe("Element locator to wait for"),timeout_seconds:Ul.number().optional().describe("Maximum wait time in seconds (default: 10)")});var Nss=Ul.object({locator:Ul.string().describe("Element locator to wait for disappearance"),timeout_seconds:Ul.number().optional().describe("Maximum wait time in seconds (default: 10)")});var Qss=Ul.object({locator:Ul.string().describe("Element locator to assert on"),condition:Ul.enum(["exists","not_exists","displayed","not_displayed","enabled","disabled","text_equals","text_contains","checked","unchecked"]).describe("Condition to assert"),expected_value:Ul.string().optional().describe("Expected value (for text_equals, text_contains)")});var Mss=Ul.object({success:Ul.boolean().describe("Whether the task was completed successfully"),message:Ul.string().describe("Summary of what was accomplished")});var Lss=Ul.object({variable_name:Ul.string().describe("Variable name (without $ prefix)"),value:Ul.string().describe("Value to store")});var Oss=Ul.object({statement:Ul.string().describe('The assertion statement to verify (e.g., "The login button is visible", "User is on the home screen")')});var GPe={DEBUG:0,INFO:1,WARN:2,ERROR:3,SILENT:4};function Epi(){let e=(process.env.LOG_LEVEL||"INFO").toUpperCase();return GPe[e]!==void 0?e:"INFO"}function Oce(e){let t=Epi();return GPe[e]>=GPe[t]}var Pce={debug:(e,t)=>{Oce("DEBUG")&&console.log(`[${e}] ${t}`)},info:(e,t)=>{Oce("INFO")&&console.log(`[${e}] ${t}`)},warn:(e,t)=>{Oce("WARN")&&console.warn(`[${e}] ${t}`)},error:(e,t)=>{Oce("ERROR")&&console.error(`[${e}] ${t}`)}};function $Pe(e){return{debug:t=>Pce.debug(e,t),info:t=>Pce.info(e,t),warn:t=>Pce.warn(e,t),error:t=>Pce.error(e,t)}}var Uss=$Pe("TextVisionExecutor");var Hss=WA.object({locator:WA.string().describe("Element locator to get text from")});var Gss=WA.object({locator:WA.string().describe("Element locator"),attribute:WA.string().describe('Attribute name to get (e.g., "text", "enabled", "checked")')});var $ss=WA.object({locator:WA.string().describe("Element locator to check visibility")});var Vss=WA.object({locator:WA.string().describe("Element locator to check if enabled")});var jss=WA.object({locator:WA.string().describe("Checkbox/switch locator to check state")});var Wss=WA.object({locator:WA.string().describe("Element locator to check existence")});var Jss=L6.object({package:L6.string().describe('App package name (e.g., "com.android.settings")')});var zss=L6.object({package:L6.string().describe("App package name to close")});var Kss=L6.object({path:L6.string().describe("Path to APK file")});var Xss=L6.object({package:L6.string().describe("App package name to uninstall")});var Zss=L6.object({package:L6.string().describe("App package name to check")});var tos=tb.object({text:tb.string().describe("Text to copy to clipboard")});var ros=tb.object({});var nos=tb.object({});var ios=tb.object({});var sos=tb.object({orientation:tb.enum(["portrait","landscape"]).describe("Device orientation")});var oos=tb.object({});var uos=UT.object({});var los=UT.object({});var cos=UT.object({});var dos=UT.object({x:UT.number().describe("X coordinate to tap"),y:UT.number().describe("Y coordinate to tap")});var fos=$Pe("MobileAgentServices");var Aos=mpi(Api),mos=$Pe("TextVisionAgent");var qhr={agentType:"text-vision",provider:"gemini",appiumHost:"localhost",appiumPort:4723,noReset:!0,maxStepsPerTask:30,retryConfig:{maxRetries:1,retryDelayMs:500},debug:!1,debugDir:"./logs"},ypi=class{config;constructor(){this.config={...qhr}}getConfig(){return{...this.config}}updateConfig(e){this.config={...this.config,...e}}resetConfig(){this.config={...qhr}}get(e){return this.config[e]}set(e,t){this.config[e]=t}},bpi=new ypi;function HPe(){return bpi.getConfig()}function rb(e,t,r,n="main"){let i=[];for(let s=0;s<e.length;s++){let o=e[s],a=`${n}.${s}`,u=Fpi(o,t,a,r);u.length>0&&(i.push(...u),s<e.length-1&&i.push(""))}return i}function Fpi(e,t,r,n){let i=" ".repeat(t);switch(e.type){case"DRAFT":return Ipi(e,t,r);case"ACTION":return _pi(e,t,r,n);case"STEP":return Spi(e,t,r,n);case"IF_ELSE":return Tpi(e,t,r,n);case"WHILE_LOOP":return kpi(e,t,r,n);default:return[`${i}// Unknown statement type: ${e.type}`]}}function Ipi(e,t,r){let n=" ".repeat(t),i=e.description?.trim()||"";return new R5().transpileUncachedAction(i,r,!1,!0).map(a=>`${n}${a}`)}function _pi(e,t,r,n){let i=" ".repeat(t),s=e.description,o=e.uid,u=n.actionEntityStore?.entries[e.uid]?.action_entity??e.action_entity;if(!u)return new R5().transpileUncachedAction(s||"",r,!!e.use_pure_vision).map(m=>`${i}${m}`);let l=e.locator?{...u,locator:e.locator}:u;return s&&s!==l.action_description&&(l={...l,action_description:s}),new R5().transpile(l,r,o).map(p=>`${i}${p}`)}function Spi(e,t,r,n){let i=" ".repeat(t),s=[];if(e.reference_id){let o=n.reusableGroups.get(e.reference_id);if(o&&o.statements){e.description&&e.description.trim()&&s.push(`${i}// Step: ${Uce(e.description)}`);let a=rb(o.statements,t,n,r);s.push(...a)}}else{e.description&&e.description.trim()&&s.push(`${i}// Step: ${Uce(e.description)}`);let o=rb(e.statements,t,n,r);s.push(...o)}return s}function Tpi(e,t,r,n){let i=" ".repeat(t),s=[];if(s.push(`${i}// ${r}: Conditional check`),e.condition.type==="JS_CODE")s.push(`${i}if (${e.condition.expression}) {`);else{s.push(`${i}// AI Condition: ${Uce(e.condition.expression)}`);let a=JSON.stringify(e.condition.expression);s.push(`${i}if (await agent.evaluate(page, ${a}, "${r}")) {`)}let o=rb(e.then,t+1,n,`${r}.then`);if(s.push(...o),e.else&&e.else.length>0){s.push(`${i}} else {`);let a=rb(e.else,t+1,n,`${r}.else`);s.push(...a)}return s.push(`${i}}`),s}function kpi(e,t,r,n){let i=" ".repeat(t),s=[];s.push(`${i}// ${r}: Loop`);let o=e.timeout_ms??_D,a=o/1e3,u=e.timeout_ms?`While loop exceeded timeout of ${a}s`:`While loop exceeded default timeout of ${a}s`,l=`loop_${r.replace(/\./g,"_")}`;if(s.push(`${i}const ${l}_start = Date.now();`),s.push(`${i}const ${l}_timeout = ${o};`),s.push(`${i}const ${l}_check = () => {`),s.push(`${i} if (Date.now() - ${l}_start > ${l}_timeout) {`),s.push(`${i} throw new Error('${u}');`),s.push(`${i} }`),s.push(`${i} return true;`),s.push(`${i}};`),e.condition.type==="JS_CODE")s.push(`${i}while (${l}_check() && (${e.condition.expression})) {`);else{s.push(`${i}// AI Loop Condition: ${Uce(e.condition.expression)}`);let d=JSON.stringify(e.condition.expression);s.push(`${i}while (${l}_check() && await agent.evaluate(page, ${d}, "${r}")) {`)}let c=rb(e.body,t+1,n,`${r}.body`);return s.push(...c),s.push(`${i}}`),s}function Hhr(e){return e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t")}function Uce(e){return e.replace(/\r\n/g," ").replace(/\n/g," ").replace(/\r/g," ").trim()}var Mpi=Qpi(import.meta.url),Nos=VPe.dirname(Mpi);function Ghr(e,t){let r=AQ(e);return Lpi(r,t)}function Lpi(e,t){let r=[];r.push(...Opi()),r.push("");let n=t?.testName||e.goal||"Generated test";r.push(`test('${Hhr(n)}', async ({ page }) => {`),r.push(" // Configure SDK with API keys from environment"),r.push(" configureSdk({"),r.push(" env: {"),r.push(" GOOGLE_API_KEY: process.env.GOOGLE_API_KEY,"),r.push(" ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,"),r.push(" },"),r.push(" });"),r.push(""),r.push(" // Create agent"),r.push(" const variableStore = new VariableStore();"),r.push(" const agent = new WebAgent(createAgentContext({"),r.push(" model: process.env.SHIPLIGHT_MODEL || 'gemini-2.5-pro',"),r.push(" variableStore,"),r.push(" }));"),r.push("");let i=t?.startingUrl||e.url;i&&(r.push(" // Navigate to the specified URL"),r.push(` const targetUrl = process.env.PLAYWRIGHT_STARTING_URL || '${Hhr(i)}';`),r.push(" if (targetUrl) {"),r.push(" await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });"),r.push(" }"),r.push(""));let s={testName:t?.testName,reusableGroups:new Map,startingUrl:t?.startingUrl};if(e.teardown&&e.teardown.length>0){if(r.push(" try {"),e.statements&&e.statements.length>0){r.push(" // Test steps");let u=rb(e.statements,2,s);r.push(...u)}r.push(" } finally {"),r.push(" // Teardown");let a=rb(e.teardown,2,s,"teardown");r.push(...a),r.push(" }")}else if(e.statements&&e.statements.length>0){r.push(" // Test steps");let a=rb(e.statements,1,s);r.push(...a)}return r.push("});"),r.join(`
8026
- `)}function Opi(){return["import { test, expect } from '@playwright/test';","import { WebAgent, createAgentContext, configureSdk } from 'shiplightai';","import { VariableStore } from 'shiplightai';"]}import*as O6 from"fs";import*as P8 from"path";var Jce=class qT{constructor(t,r,n){this.backend=t,this.apiClient=r,this.webAgentModel=n}static newSessionTool={name:"new_session",description:"Create a new browser session with optional device emulation and auto-login. Returns a session_id for subsequent operations. Use storage_state_path to restore a previously saved session (cookies, localStorage, IndexedDB). After creating a session, IMMEDIATELY read resource 'shiplight://schemas/action-entity' for action parameter formats before calling any other tools.",inputSchema:iG(Nn.object({starting_url:Nn.string().optional().describe("Starting URL (defaults to about:blank)"),test_account_id:Nn.number().optional().describe("Test account ID for auto-login. If provided, will navigate to the environment URL and login."),storage_state_path:Nn.string().optional().describe("Path to a storage state file to restore cookies, localStorage, and IndexedDB into the session."),browser_options:Nn.object({device_type:Nn.enum(["browser","android"]).optional().describe("Device type. Default: browser"),device_name:Nn.string().optional().describe("Device name for emulation (e.g., 'iPhone 14 Pro')"),disable_security:Nn.boolean().optional().describe("Disable web security (CORS, CSP). Default: false"),record_video:Nn.boolean().optional().describe("Enable video recording"),enable_camera:Nn.boolean().optional().describe("Enable camera permission (Chromium-only)"),enable_microphone:Nn.boolean().optional().describe("Enable microphone permission (Chromium-only)"),headless:Nn.boolean().optional().describe("Run browser in headless mode"),locale:Nn.string().optional().describe("Browser locale (e.g., 'en-US')"),proxy:Nn.object({server:Nn.string(),username:Nn.string().optional(),password:Nn.string().optional()}).optional().describe("Proxy configuration")}).optional().describe("Browser launch options")}),{$refStrategy:"none"})};async newSession(t){console.log("[SessionTools.newSession] Starting with args:",JSON.stringify(t));let{starting_url:r,test_account_id:n,storage_state_path:i,browser_options:s}=Nn.object({starting_url:Nn.string().optional(),test_account_id:Nn.number().optional(),storage_state_path:Nn.string().optional(),browser_options:Nn.object({device_type:Nn.enum(["browser","android"]).optional(),device_name:Nn.string().optional(),disable_security:Nn.boolean().optional(),record_video:Nn.boolean().optional(),enable_camera:Nn.boolean().optional(),enable_microphone:Nn.boolean().optional(),headless:Nn.boolean().optional(),locale:Nn.string().optional(),proxy:Nn.object({server:Nn.string(),username:Nn.string().optional(),password:Nn.string().optional()}).optional()}).optional()}).parse(t);if(n&&i)throw new Error("test_account_id and storage_state_path are mutually exclusive. Use one or the other.");let o=null,a=r||"about:blank";if(n){if(!this.apiClient)throw new Error("test_account_id requires a Shiplight API token. Set API_TOKEN in your MCP server configuration, or use storage_state_path instead.");console.log("[SessionTools.newSession] Fetching test account:",n);try{let d=await this.apiClient.getTestAccount(n);console.log("[SessionTools.newSession] Got test account:",d.id,d.username);let p=d.environmentId||d.environment_id,f=r;if(p){console.log("[SessionTools.newSession] Fetching environment:",p);let y=await this.apiClient.getEnvironment(p);console.log("[SessionTools.newSession] Got environment URL:",y.url),f=y.url,r||(a=y.url)}let m=d.loginConfig||d.login_config||{},C={...m,site_url:f||a,account:{...m.account,username:d.username,password:d.password}};o={id:d.id,username:d.username,password:d.password||"",environmentId:p,loginConfig:C},console.log("[SessionTools.newSession] Built testAccount with loginConfig.site_url:",C.site_url)}catch(d){throw console.error("[SessionTools.newSession] Error fetching test account or environment:",d),d}}console.log("[SessionTools.newSession] Creating session with URL:",a);let u;try{u=(await this.backend.createSession({startingUrl:a,testAccount:o,browserOptions:{deviceType:s?.device_type||"browser",deviceName:s?.device_name||"Desktop Chrome Medium Resolution",disableSecurity:s?.disable_security??!1,recordVideo:s?.record_video,enableCamera:s?.enable_camera,enableMicrophone:s?.enable_microphone,headless:s?.headless??!1,locale:s?.locale,proxy:s?.proxy,storageStatePath:i},webAgentConfig:{model:this.webAgentModel}})).sessionId,console.log("[SessionTools.newSession] Session created:",u)}catch(d){throw console.error("[SessionTools.newSession] Error creating session:",d),d}let l=!1;if(o&&o.username&&o.password){console.log("[SessionTools.newSession] Performing login for user:",o.username);try{l=await this.backend.loginWithTestAccount(u,o),console.log("[SessionTools.newSession] Login result:",l)}catch(d){console.error("[SessionTools.newSession] Auto-login failed:",d)}}console.log("[SessionTools.newSession] Getting current URL");let c=await this.backend.getCurrentUrl(u);return console.log("[SessionTools.newSession] Current URL:",c),JSON.stringify({session_id:u,current_url:c,login_performed:l,storage_state_loaded:!!i,message:l?`Session created and logged in at ${c}`:i?`Session created with restored storage state at ${c}`:`Session created at ${c}`,do_this_now:"You MUST read resource 'shiplight://schemas/action-entity' before calling any other shiplight tools."})}static saveStorageStateTool={name:"save_storage_state",description:"Save the browser session's storage state (cookies, localStorage, IndexedDB) to a file. Use this after logging in to cache the session for fast restores via new_session's storage_state_path.",inputSchema:iG(Nn.object({session_id:Nn.string().describe("Session ID"),path:Nn.string().describe("File path to save the storage state to (e.g., ~/.shiplight/storage-states/my-app.json)")}),{$refStrategy:"none"})};async saveStorageState(t){let{session_id:r,path:n}=Nn.object({session_id:Nn.string(),path:Nn.string()}).parse(t);return await this.backend.saveStorageState(r,n),JSON.stringify({path:n,message:`Storage state saved to ${n}`})}static closeSessionTool={name:"close_session",description:"Close a browser session",inputSchema:iG(Nn.object({session_id:Nn.string().describe("Session ID to close")}),{$refStrategy:"none"})};async closeSession(t){let{session_id:r}=Nn.object({session_id:Nn.string()}).parse(t);return await this.backend.closeSession(r),JSON.stringify({message:`Session ${r} closed successfully`})}static closeAllSessionsTool={name:"close_all",description:"Close all browser sessions",inputSchema:iG(Nn.object({}),{$refStrategy:"none"})};async closeAllSessions(){let t=this.backend.getSessionCount();return await this.backend.closeAllSessions(),JSON.stringify({message:`Closed ${t} session(s)`})}static getCurrentStateTool={name:"get_session_state",description:"Get current state of a session (URL, session type)",inputSchema:iG(Nn.object({session_id:Nn.string().describe("Session ID")}),{$refStrategy:"none"})};async getCurrentState(t){let{session_id:r}=Nn.object({session_id:Nn.string()}).parse(t),n=this.backend.getSession(r);if(!n)throw new Error(`Session ${r} not found`);let i=await this.backend.getCurrentUrl(r);return JSON.stringify({session_id:r,current_url:i,session_type:n.sessionType})}static toolDefinitions=[qT.newSessionTool,qT.saveStorageStateTool,qT.closeSessionTool,qT.closeAllSessionsTool,qT.getCurrentStateTool]},jPe=Yo.object({session_id:Yo.string().describe("Session ID from new_session"),url:Yo.string().optional().describe("URL to navigate to (optional, stays at current URL if not provided)")}),Vhr={name:"navigate",description:`Navigate to a URL in an existing browser session.
8026
+ `)}function Opi(){return["import { test, expect } from '@playwright/test';","import { WebAgent, createAgentContext, configureSdk } from 'shiplightai';","import { VariableStore } from 'shiplightai';"]}import*as O6 from"fs";import*as P8 from"path";var Jce=class qT{constructor(t,r,n){this.backend=t,this.apiClient=r,this.webAgentModel=n}static newSessionTool={name:"new_session",description:"Create a new browser session with optional device emulation and auto-login. Returns a session_id for subsequent operations. Use storage_state_path to restore a previously saved session (cookies, localStorage, IndexedDB). After creating a session, IMMEDIATELY read resource 'shiplight://schemas/action-entity' for action parameter formats before calling any other tools.",inputSchema:iG(Nn.object({starting_url:Nn.string().optional().describe("Starting URL (defaults to about:blank)"),test_account_id:Nn.number().optional().describe("Test account ID for auto-login. If provided, will navigate to the environment URL and login."),storage_state_path:Nn.string().optional().describe("Path to a storage state file to restore cookies, localStorage, and IndexedDB into the session."),browser_options:Nn.object({device_type:Nn.enum(["browser","android"]).optional().describe("Device type. Default: browser"),device_name:Nn.string().optional().describe("Device name for emulation (e.g., 'iPhone 14 Pro')"),disable_security:Nn.boolean().optional().describe("Disable web security (CORS, CSP). Default: false"),record_video:Nn.boolean().optional().describe("Enable video recording"),enable_camera:Nn.boolean().optional().describe("Enable camera permission (Chromium-only)"),enable_microphone:Nn.boolean().optional().describe("Enable microphone permission (Chromium-only)"),headless:Nn.boolean().optional().describe("Run browser in headless mode"),locale:Nn.string().optional().describe("Browser locale (e.g., 'en-US')"),proxy:Nn.object({server:Nn.string(),username:Nn.string().optional(),password:Nn.string().optional()}).optional().describe("Proxy configuration")}).optional().describe("Browser launch options. See https://docs.shiplight.ai/reference/browser-options.md for full reference.")}),{$refStrategy:"none"})};async newSession(t){console.log("[SessionTools.newSession] Starting with args:",JSON.stringify(t));let{starting_url:r,test_account_id:n,storage_state_path:i,browser_options:s}=Nn.object({starting_url:Nn.string().optional(),test_account_id:Nn.number().optional(),storage_state_path:Nn.string().optional(),browser_options:Nn.object({device_type:Nn.enum(["browser","android"]).optional(),device_name:Nn.string().optional(),disable_security:Nn.boolean().optional(),record_video:Nn.boolean().optional(),enable_camera:Nn.boolean().optional(),enable_microphone:Nn.boolean().optional(),headless:Nn.boolean().optional(),locale:Nn.string().optional(),proxy:Nn.object({server:Nn.string(),username:Nn.string().optional(),password:Nn.string().optional()}).optional()}).optional()}).parse(t);if(n&&i)throw new Error("test_account_id and storage_state_path are mutually exclusive. Use one or the other.");let o=null,a=r||"about:blank";if(n){if(!this.apiClient)throw new Error("test_account_id requires a Shiplight API token. Set API_TOKEN in your MCP server configuration, or use storage_state_path instead.");console.log("[SessionTools.newSession] Fetching test account:",n);try{let d=await this.apiClient.getTestAccount(n);console.log("[SessionTools.newSession] Got test account:",d.id,d.username);let p=d.environmentId||d.environment_id,f=r;if(p){console.log("[SessionTools.newSession] Fetching environment:",p);let y=await this.apiClient.getEnvironment(p);console.log("[SessionTools.newSession] Got environment URL:",y.url),f=y.url,r||(a=y.url)}let m=d.loginConfig||d.login_config||{},C={...m,site_url:f||a,account:{...m.account,username:d.username,password:d.password}};o={id:d.id,username:d.username,password:d.password||"",environmentId:p,loginConfig:C},console.log("[SessionTools.newSession] Built testAccount with loginConfig.site_url:",C.site_url)}catch(d){throw console.error("[SessionTools.newSession] Error fetching test account or environment:",d),d}}console.log("[SessionTools.newSession] Creating session with URL:",a);let u;try{u=(await this.backend.createSession({startingUrl:a,testAccount:o,browserOptions:{deviceType:s?.device_type||"browser",deviceName:s?.device_name||"Desktop Chrome Medium Resolution",disableSecurity:s?.disable_security??!1,recordVideo:s?.record_video,enableCamera:s?.enable_camera,enableMicrophone:s?.enable_microphone,headless:s?.headless??!1,locale:s?.locale,proxy:s?.proxy,storageStatePath:i},webAgentConfig:{model:this.webAgentModel}})).sessionId,console.log("[SessionTools.newSession] Session created:",u)}catch(d){throw console.error("[SessionTools.newSession] Error creating session:",d),d}let l=!1;if(o&&o.username&&o.password){console.log("[SessionTools.newSession] Performing login for user:",o.username);try{l=await this.backend.loginWithTestAccount(u,o),console.log("[SessionTools.newSession] Login result:",l)}catch(d){console.error("[SessionTools.newSession] Auto-login failed:",d)}}console.log("[SessionTools.newSession] Getting current URL");let c=await this.backend.getCurrentUrl(u);return console.log("[SessionTools.newSession] Current URL:",c),JSON.stringify({session_id:u,current_url:c,login_performed:l,storage_state_loaded:!!i,message:l?`Session created and logged in at ${c}`:i?`Session created with restored storage state at ${c}`:`Session created at ${c}`,do_this_now:"You MUST read resource 'shiplight://schemas/action-entity' before calling any other shiplight tools."})}static saveStorageStateTool={name:"save_storage_state",description:"Save the browser session's storage state (cookies, localStorage, IndexedDB) to a file. Use this after logging in to cache the session for fast restores via new_session's storage_state_path.",inputSchema:iG(Nn.object({session_id:Nn.string().describe("Session ID"),path:Nn.string().describe("File path to save the storage state to (e.g., ~/.shiplight/storage-states/my-app.json)")}),{$refStrategy:"none"})};async saveStorageState(t){let{session_id:r,path:n}=Nn.object({session_id:Nn.string(),path:Nn.string()}).parse(t);return await this.backend.saveStorageState(r,n),JSON.stringify({path:n,message:`Storage state saved to ${n}`})}static closeSessionTool={name:"close_session",description:"Close a browser session",inputSchema:iG(Nn.object({session_id:Nn.string().describe("Session ID to close")}),{$refStrategy:"none"})};async closeSession(t){let{session_id:r}=Nn.object({session_id:Nn.string()}).parse(t);return await this.backend.closeSession(r),JSON.stringify({message:`Session ${r} closed successfully`})}static closeAllSessionsTool={name:"close_all",description:"Close all browser sessions",inputSchema:iG(Nn.object({}),{$refStrategy:"none"})};async closeAllSessions(){let t=this.backend.getSessionCount();return await this.backend.closeAllSessions(),JSON.stringify({message:`Closed ${t} session(s)`})}static getCurrentStateTool={name:"get_session_state",description:"Get current state of a session (URL, session type)",inputSchema:iG(Nn.object({session_id:Nn.string().describe("Session ID")}),{$refStrategy:"none"})};async getCurrentState(t){let{session_id:r}=Nn.object({session_id:Nn.string()}).parse(t),n=this.backend.getSession(r);if(!n)throw new Error(`Session ${r} not found`);let i=await this.backend.getCurrentUrl(r);return JSON.stringify({session_id:r,current_url:i,session_type:n.sessionType})}static toolDefinitions=[qT.newSessionTool,qT.saveStorageStateTool,qT.closeSessionTool,qT.closeAllSessionsTool,qT.getCurrentStateTool]},jPe=Yo.object({session_id:Yo.string().describe("Session ID from new_session"),url:Yo.string().optional().describe("URL to navigate to (optional, stays at current URL if not provided)")}),Vhr={name:"navigate",description:`Navigate to a URL in an existing browser session.
8027
8027
 
8028
8028
  Examples:
8029
8029
  - Navigate to a specific page: url="https://example.com/login"
@@ -8118,7 +8118,7 @@ The flow is executed statement by statement:
8118
8118
  - IF_ELSE: evaluates condition, then executes the matching branch
8119
8119
  - WHILE_LOOP: loops while condition is true (respects timeout_ms)
8120
8120
 
8121
- IMPORTANT: Read 'shiplight://schemas/testflow-json-v1.2.0' resource for the flow format.`,inputSchema:$pi(cv.object({session_id:cv.string().describe("Session ID from new_session"),flow:cv.any().describe("Test flow JSON object (must match TestFlow schema)")}),{$refStrategy:"none"})};async runTestFlow(t){let{session_id:r,flow:n}=cv.object({session_id:cv.string(),flow:cv.any()}).parse(t);if(!this.backend.getSession(r))throw new Error(`Session ${r} not found`);let s;try{s=$5.parse(n)}catch(p){throw p instanceof cv.ZodError?new Error(`Invalid TestFlow schema: ${JSON.stringify(p.errors)}`):p}let o=this.backend.getPage(r),a=this.backend.getWebAgent(r);s.url&&await o.goto(s.url,{waitUntil:"domcontentloaded"});let u=[],l=await this.executeStatements(s.statements,o,a,u),c=this.countResults(u),d={success:l,url:o.url(),results:u,statements_passed:c.passed,statements_failed:c.failed,statements_total:c.total};return JSON.stringify(d)}async executeStatements(t,r,n,i){let s=!0;for(let o of t){let a=await this.executeStatement(o,r,n);if(i.push(a),!a.success){s=!1;break}}return s}async executeStatement(t,r,n){let i=this.getStatementDescription(t),s={uid:t.uid,type:t.type,description:i,success:!1};try{switch(t.type){case H0.DRAFT:return await this.executeDraft(t,r,n,s);case H0.ACTION:return await this.executeAction(t,r,n,s);case H0.STEP:return await this.executeStep(t,r,n,s);case H0.IF_ELSE:return await this.executeIfElse(t,r,n,s);case H0.WHILE_LOOP:return await this.executeWhileLoop(t,r,n,s);default:return{...s,error:`Unknown statement type: ${t.type}`}}}catch(o){return{...s,error:o instanceof Error?o.message:String(o)}}}async executeDraft(t,r,n,i){let s=await n.run(r,t.description);return{...i,success:s.success,error:s.success?void 0:s.details||"Draft execution failed"}}async executeAction(t,r,n,i){if(t.action_entity){let s=t.action_entity.action_data?.action_name;return s?(await n.execAction(s,r,t.action_entity),{...i,success:!0}):{...i,error:"action_entity missing action_data.action_name"}}else{let s=await n.execute(r,t.description);return{...i,success:s.success,error:s.success?void 0:s.details||"Action execution failed"}}}async executeStep(t,r,n,i){let s=wj(t),o=[],a=!0;for(let u of s)if(!await this.executeStatements(u.statements,r,n,o)){a=!1;break}return{...i,success:a,children:o,error:a?void 0:"One or more child statements failed"}}async executeIfElse(t,r,n,i){let s=t.condition.expression,o=await n.evaluate(r,s),a=[],u=!0;return o&&t.then&&t.then.length>0?u=await this.executeStatements(t.then,r,n,a):!o&&t.else&&t.else.length>0&&(u=await this.executeStatements(t.else,r,n,a)),{...i,success:u,children:a,description:`${i.description} (condition=${o})`}}async executeWhileLoop(t,r,n,i){let o=t.condition.expression,a=t.timeout_ms??_D,u=Date.now(),l=[],c=0;for(;;){if(Date.now()-u>a)return{...i,success:!1,children:l,error:`While loop timed out after ${a}ms (${c} iterations)`};if(c>=100)return{...i,success:!1,children:l,error:"While loop exceeded max iterations (100)"};if(!await n.evaluate(r,o))break;if(t.body&&t.body.length>0){let p=[],f=await this.executeStatements(t.body,r,n,p);if(l.push(...p),!f)return{...i,success:!1,children:l,error:`While loop body failed on iteration ${c+1}`}}c++}return{...i,success:!0,children:l,description:`${i.description} (${c} iterations)`}}getStatementDescription(t){switch(t.type){case H0.DRAFT:case H0.ACTION:case H0.STEP:return t.description||t.type;case H0.IF_ELSE:return`IF ${t.condition.expression}`;case H0.WHILE_LOOP:return`WHILE ${t.condition.expression}`;default:let r=t;return"Unknown statement"}}countResults(t){let r=0,n=0,i=0;for(let s of t)if(i++,s.success?r++:n++,s.children){let o=this.countResults(s.children);r+=o.passed,n+=o.failed,i+=o.total}return{passed:r,failed:n,total:i}}static toolDefinitions=[dpr.runTestFlowTool]};eE();var ZPe=class e{static _instance;_logLevel;constructor(){switch(process.env.LOG_LEVEL?.toUpperCase()){case"ERROR":this._logLevel=1;break;case"WARN":this._logLevel=2;break;case"INFO":this._logLevel=3;break;case"DEBUG":this._logLevel=4;break;default:this._logLevel=3}}static getInstance(){return e._instance||(e._instance=new e),e._instance}setLevel(t){this._logLevel=t}debug(...t){this._logLevel>=4&&console.error("[DEBUG]",...t)}info(...t){this._logLevel>=3&&console.error("[INFO]",...t)}warn(...t){this._logLevel>=2&&console.error("[WARN]",...t)}error(...t){this._logLevel>=1&&console.error("[ERROR]",...t)}log(...t){this.info(...t)}},Vpi=ZPe.getInstance(),rde=Vpi;import{createHash as jpi}from"node:crypto";import{hostname as Wpi,userInfo as Ypi,platform as ppr,arch as Jpi}from"node:os";import{PostHog as zpi}from"posthog-node";var Kpi="phc_5Va2dHamGwJWX9qZqyRka5Lann5ATsg3LL4uyU63qin",Xpi="https://us.i.posthog.com",Zpi=process.env.SHIPLIGHT_TELEMETRY==="0"||process.env.DO_NOT_TRACK==="1",eUe=null,hpr=!1;function efi(){return Zpi?null:(eUe||(eUe=new zpi(Kpi,{host:Xpi,flushAt:1,flushInterval:3e4})),eUe)}function tfi(){let e=`${Wpi()}:${Ypi().username}:${ppr()}:${Jpi()}`;return jpi("sha256").update(e).digest("hex")}function fpr(e,t){let r=efi();if(r){hpr||(rde.info("Anonymous usage telemetry enabled. Set SHIPLIGHT_TELEMETRY=0 to disable."),hpr=!0);try{r.capture({distinctId:tfi(),event:"mcp_new_session",properties:{product:"mcp-server",version:e,platform:ppr(),nodeVersion:process.version,cloudMode:t,$geoip_disable:!0,$ip:null}}),r.flush().catch(()=>{})}catch{}}}process.env.PWDEBUG="console";var mpr="shiplight://environments",tUe=class{constructor(t=!1){this.cloudMode=t;let r=process.env.API_BASE_URL||"https://api.shiplight.ai",n=VEe(process.env),i=process.env.TERMINATION_TIMEOUT?parseInt(process.env.TERMINATION_TIMEOUT,10):null;this.server=new rfi({name:"shiplight-mcp",version:"1.0.0"},{capabilities:{tools:{},resources:{},prompts:{}}});let s={};process.env.GOOGLE_API_KEY&&(s.GOOGLE_API_KEY=process.env.GOOGLE_API_KEY),process.env.ANTHROPIC_API_KEY&&(s.ANTHROPIC_API_KEY=process.env.ANTHROPIC_API_KEY),Object.keys(s).length>0&&dG({env:s}),this.sessionBackend=new jEe({runDir:void 0,terminationTimeout:i}),this.apiClient=this.cloudMode&&process.env.API_TOKEN?new WEe({apiBaseUrl:r,getApiToken:()=>process.env.API_TOKEN}):null,this.sessionTools=new Jce(this.sessionBackend,this.apiClient,n),this.browserTools=new zce(this.sessionBackend),this.debugTools=new Kce(this.sessionBackend),this.localTestTools=new tde,this.registry=new YEe,this.registry.registerAll(Jce,this.sessionTools).registerAll(zce,this.browserTools).registerAll(Kce,this.debugTools).registerAll(tde,this.localTestTools),this.cloudMode&&(this.testCaseTools=new Zce(this.apiClient),this.testResultTools=new ede(this.apiClient),this.dataTools=new Xce(this.apiClient),this.registry.registerAll(Zce,this.testCaseTools).registerAll(ede,this.testResultTools).registerAll(Xce,this.dataTools)),this.setupHandlers()}server;registry;sessionBackend;apiClient;sessionTools;browserTools;debugTools;testCaseTools=null;testResultTools=null;dataTools=null;localTestTools;setupHandlers(){let{tools:t,handleToolCall:r}=this.registry.build();this.server.setRequestHandler(sfi,async()=>({tools:t})),this.server.setRequestHandler(ifi,async n=>{try{let{name:i,arguments:s}=n.params,o=await r(i,s);return i==="new_session"&&fpr("0.1.23",this.cloudMode),{content:[{type:"text",text:o}]}}catch(i){let s=i instanceof Error?i.message:String(i);throw Apr.isAxiosError(i)&&i.response?.status===401&&(s="Authentication required. Contact info@shiplight.ai to get access to cloud services and advanced features."),new uG(aG.InternalError,s)}}),this.server.setRequestHandler(ofi,async()=>{let n=new Set(["shiplight://schemas/testflow-json-v1.2.0"]),i=this.cloudMode?vj():vj().filter(s=>!n.has(s.uri));return this.cloudMode&&i.push({uri:mpr,name:"Environments",description:"List testing environments (same data as the list_environments tool).",mimeType:"application/json"}),{resources:i}}),this.server.setRequestHandler(afi,async n=>{let{uri:i}=n.params;if(i===mpr){if(!this.apiClient)throw new uG(aG.InternalError,"The environments resource requires a Shiplight API token. Set API_TOKEN in your MCP server configuration.");try{let o=await this.apiClient.listEnvironments();return{contents:[{uri:i,mimeType:"application/json",text:JSON.stringify({environments:o.map(a=>({id:a.id,name:a.name,url:a.url}))},null,2)}]}}catch(o){let a=o instanceof Error?o.message:String(o);throw Apr.isAxiosError(o)&&o.response?.status===401&&(a="Authentication required. Provide API_TOKEN in the MCP server configuration to access cloud resources."),new uG(aG.InternalError,a)}}let s=await XEe(i);if(!s)throw new uG(aG.InvalidRequest,`Unknown resource: ${i}`);return{contents:[{uri:i,mimeType:"text/markdown",text:s}]}}),this.server.setRequestHandler(ufi,async()=>({prompts:KEe().map(i=>({name:i.name,description:i.description}))})),this.server.setRequestHandler(lfi,async n=>{let{name:i}=n.params,s=zEe(i);if(!s)throw new uG(aG.InvalidRequest,`Unknown prompt: ${i}`);return{messages:[{role:"user",content:{type:"text",text:s}}]}})}async run(){let t=new nfi;await this.server.connect(t),rde.info("Shiplight MCP Server running on stdio")}},cfi=process.argv.includes("--cloud"),dfi=new tUe(cfi);await dfi.run();
8121
+ IMPORTANT: Read 'shiplight://schemas/testflow-json-v1.2.0' resource for the flow format.`,inputSchema:$pi(cv.object({session_id:cv.string().describe("Session ID from new_session"),flow:cv.any().describe("Test flow JSON object (must match TestFlow schema)")}),{$refStrategy:"none"})};async runTestFlow(t){let{session_id:r,flow:n}=cv.object({session_id:cv.string(),flow:cv.any()}).parse(t);if(!this.backend.getSession(r))throw new Error(`Session ${r} not found`);let s;try{s=$5.parse(n)}catch(p){throw p instanceof cv.ZodError?new Error(`Invalid TestFlow schema: ${JSON.stringify(p.errors)}`):p}let o=this.backend.getPage(r),a=this.backend.getWebAgent(r);s.url&&await o.goto(s.url,{waitUntil:"domcontentloaded"});let u=[],l=await this.executeStatements(s.statements,o,a,u),c=this.countResults(u),d={success:l,url:o.url(),results:u,statements_passed:c.passed,statements_failed:c.failed,statements_total:c.total};return JSON.stringify(d)}async executeStatements(t,r,n,i){let s=!0;for(let o of t){let a=await this.executeStatement(o,r,n);if(i.push(a),!a.success){s=!1;break}}return s}async executeStatement(t,r,n){let i=this.getStatementDescription(t),s={uid:t.uid,type:t.type,description:i,success:!1};try{switch(t.type){case H0.DRAFT:return await this.executeDraft(t,r,n,s);case H0.ACTION:return await this.executeAction(t,r,n,s);case H0.STEP:return await this.executeStep(t,r,n,s);case H0.IF_ELSE:return await this.executeIfElse(t,r,n,s);case H0.WHILE_LOOP:return await this.executeWhileLoop(t,r,n,s);default:return{...s,error:`Unknown statement type: ${t.type}`}}}catch(o){return{...s,error:o instanceof Error?o.message:String(o)}}}async executeDraft(t,r,n,i){let s=await n.run(r,t.description);return{...i,success:s.success,error:s.success?void 0:s.details||"Draft execution failed"}}async executeAction(t,r,n,i){if(t.action_entity){let s=t.action_entity.action_data?.action_name;return s?(await n.execAction(s,r,t.action_entity),{...i,success:!0}):{...i,error:"action_entity missing action_data.action_name"}}else{let s=await n.execute(r,t.description);return{...i,success:s.success,error:s.success?void 0:s.details||"Action execution failed"}}}async executeStep(t,r,n,i){let s=wj(t),o=[],a=!0;for(let u of s)if(!await this.executeStatements(u.statements,r,n,o)){a=!1;break}return{...i,success:a,children:o,error:a?void 0:"One or more child statements failed"}}async executeIfElse(t,r,n,i){let s=t.condition.expression,o=await n.evaluate(r,s),a=[],u=!0;return o&&t.then&&t.then.length>0?u=await this.executeStatements(t.then,r,n,a):!o&&t.else&&t.else.length>0&&(u=await this.executeStatements(t.else,r,n,a)),{...i,success:u,children:a,description:`${i.description} (condition=${o})`}}async executeWhileLoop(t,r,n,i){let o=t.condition.expression,a=t.timeout_ms??_D,u=Date.now(),l=[],c=0;for(;;){if(Date.now()-u>a)return{...i,success:!1,children:l,error:`While loop timed out after ${a}ms (${c} iterations)`};if(c>=100)return{...i,success:!1,children:l,error:"While loop exceeded max iterations (100)"};if(!await n.evaluate(r,o))break;if(t.body&&t.body.length>0){let p=[],f=await this.executeStatements(t.body,r,n,p);if(l.push(...p),!f)return{...i,success:!1,children:l,error:`While loop body failed on iteration ${c+1}`}}c++}return{...i,success:!0,children:l,description:`${i.description} (${c} iterations)`}}getStatementDescription(t){switch(t.type){case H0.DRAFT:case H0.ACTION:case H0.STEP:return t.description||t.type;case H0.IF_ELSE:return`IF ${t.condition.expression}`;case H0.WHILE_LOOP:return`WHILE ${t.condition.expression}`;default:let r=t;return"Unknown statement"}}countResults(t){let r=0,n=0,i=0;for(let s of t)if(i++,s.success?r++:n++,s.children){let o=this.countResults(s.children);r+=o.passed,n+=o.failed,i+=o.total}return{passed:r,failed:n,total:i}}static toolDefinitions=[dpr.runTestFlowTool]};eE();var ZPe=class e{static _instance;_logLevel;constructor(){switch(process.env.LOG_LEVEL?.toUpperCase()){case"ERROR":this._logLevel=1;break;case"WARN":this._logLevel=2;break;case"INFO":this._logLevel=3;break;case"DEBUG":this._logLevel=4;break;default:this._logLevel=3}}static getInstance(){return e._instance||(e._instance=new e),e._instance}setLevel(t){this._logLevel=t}debug(...t){this._logLevel>=4&&console.error("[DEBUG]",...t)}info(...t){this._logLevel>=3&&console.error("[INFO]",...t)}warn(...t){this._logLevel>=2&&console.error("[WARN]",...t)}error(...t){this._logLevel>=1&&console.error("[ERROR]",...t)}log(...t){this.info(...t)}},Vpi=ZPe.getInstance(),rde=Vpi;import{createHash as jpi}from"node:crypto";import{hostname as Wpi,userInfo as Ypi,platform as ppr,arch as Jpi}from"node:os";import{PostHog as zpi}from"posthog-node";var Kpi="phc_5Va2dHamGwJWX9qZqyRka5Lann5ATsg3LL4uyU63qin",Xpi="https://us.i.posthog.com",Zpi=process.env.SHIPLIGHT_TELEMETRY==="0"||process.env.DO_NOT_TRACK==="1",eUe=null,hpr=!1;function efi(){return Zpi?null:(eUe||(eUe=new zpi(Kpi,{host:Xpi,flushAt:1,flushInterval:3e4})),eUe)}function tfi(){let e=`${Wpi()}:${Ypi().username}:${ppr()}:${Jpi()}`;return jpi("sha256").update(e).digest("hex")}function fpr(e,t){let r=efi();if(r){hpr||(rde.info("Anonymous usage telemetry enabled. Set SHIPLIGHT_TELEMETRY=0 to disable."),hpr=!0);try{r.capture({distinctId:tfi(),event:"mcp_new_session",properties:{product:"mcp-server",version:e,platform:ppr(),nodeVersion:process.version,cloudMode:t,$geoip_disable:!0,$ip:null}}),r.flush().catch(()=>{})}catch{}}}process.env.PWDEBUG="console";var mpr="shiplight://environments",tUe=class{constructor(t=!1){this.cloudMode=t;let r=process.env.API_BASE_URL||"https://api.shiplight.ai",n=VEe(process.env),i=process.env.TERMINATION_TIMEOUT?parseInt(process.env.TERMINATION_TIMEOUT,10):null;this.server=new rfi({name:"shiplight-mcp",version:"1.0.0"},{capabilities:{tools:{},resources:{},prompts:{}}});let s={};process.env.GOOGLE_API_KEY&&(s.GOOGLE_API_KEY=process.env.GOOGLE_API_KEY),process.env.ANTHROPIC_API_KEY&&(s.ANTHROPIC_API_KEY=process.env.ANTHROPIC_API_KEY),Object.keys(s).length>0&&dG({env:s}),this.sessionBackend=new jEe({runDir:void 0,terminationTimeout:i}),this.apiClient=this.cloudMode&&process.env.API_TOKEN?new WEe({apiBaseUrl:r,getApiToken:()=>process.env.API_TOKEN}):null,this.sessionTools=new Jce(this.sessionBackend,this.apiClient,n),this.browserTools=new zce(this.sessionBackend),this.debugTools=new Kce(this.sessionBackend),this.localTestTools=new tde,this.registry=new KEe,this.registry.registerAll(Jce,this.sessionTools).registerAll(zce,this.browserTools).registerAll(Kce,this.debugTools).registerAll(tde,this.localTestTools),this.cloudMode&&(this.testCaseTools=new Zce(this.apiClient),this.testResultTools=new ede(this.apiClient),this.dataTools=new Xce(this.apiClient),this.registry.registerAll(Zce,this.testCaseTools).registerAll(ede,this.testResultTools).registerAll(Xce,this.dataTools)),this.setupHandlers()}server;registry;sessionBackend;apiClient;sessionTools;browserTools;debugTools;testCaseTools=null;testResultTools=null;dataTools=null;localTestTools;setupHandlers(){let{tools:t,handleToolCall:r}=this.registry.build();this.server.setRequestHandler(sfi,async()=>({tools:t})),this.server.setRequestHandler(ifi,async n=>{try{let{name:i,arguments:s}=n.params,o=await r(i,s);return i==="new_session"&&fpr("0.1.24",this.cloudMode),{content:[{type:"text",text:o}]}}catch(i){let s=i instanceof Error?i.message:String(i);throw Apr.isAxiosError(i)&&i.response?.status===401&&(s="Authentication required. Contact info@shiplight.ai to get access to cloud services and advanced features."),new uG(aG.InternalError,s)}}),this.server.setRequestHandler(ofi,async()=>{let n=new Set(["shiplight://schemas/testflow-json-v1.2.0"]),i=this.cloudMode?vj():vj().filter(s=>!n.has(s.uri));return this.cloudMode&&i.push({uri:mpr,name:"Environments",description:"List testing environments (same data as the list_environments tool).",mimeType:"application/json"}),{resources:i}}),this.server.setRequestHandler(afi,async n=>{let{uri:i}=n.params;if(i===mpr){if(!this.apiClient)throw new uG(aG.InternalError,"The environments resource requires a Shiplight API token. Set API_TOKEN in your MCP server configuration.");try{let o=await this.apiClient.listEnvironments();return{contents:[{uri:i,mimeType:"application/json",text:JSON.stringify({environments:o.map(a=>({id:a.id,name:a.name,url:a.url}))},null,2)}]}}catch(o){let a=o instanceof Error?o.message:String(o);throw Apr.isAxiosError(o)&&o.response?.status===401&&(a="Authentication required. Provide API_TOKEN in the MCP server configuration to access cloud resources."),new uG(aG.InternalError,a)}}let s=await XEe(i);if(!s)throw new uG(aG.InvalidRequest,`Unknown resource: ${i}`);return{contents:[{uri:i,mimeType:"text/markdown",text:s}]}}),this.server.setRequestHandler(ufi,async()=>({prompts:zEe().map(i=>({name:i.name,description:i.description}))})),this.server.setRequestHandler(lfi,async n=>{let{name:i}=n.params,s=JEe(i);if(!s)throw new uG(aG.InvalidRequest,`Unknown prompt: ${i}`);return{messages:[{role:"user",content:{type:"text",text:s}}]}})}async run(){let t=new nfi;await this.server.connect(t),rde.info("Shiplight MCP Server running on stdio")}},cfi=process.argv.includes("--cloud"),dfi=new tUe(cfi);await dfi.run();
8122
8122
  /*! Bundled license information:
8123
8123
 
8124
8124
  node-domexception/index.js:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shiplightai/mcp",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "type": "module",
5
5
  "description": "Shiplight MCP server for AI-powered test automation",
6
6
  "main": "dist/index.js",
@@ -15,6 +15,14 @@
15
15
  "registry": "https://registry.npmjs.org",
16
16
  "access": "public"
17
17
  },
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "build:deps": "turbo run build --filter=@shiplightai/mcp",
21
+ "clean": "rm -rf dist",
22
+ "dev": "tsup --watch",
23
+ "start": "node dist/index.js",
24
+ "typecheck": "tsc --noEmit"
25
+ },
18
26
  "dependencies": {
19
27
  "@modelcontextprotocol/sdk": "^0.5.0",
20
28
  "@playwright/test": "^1.55.0",
@@ -30,23 +38,15 @@
30
38
  },
31
39
  "devDependencies": {
32
40
  "@types/node": "^24.0.0",
41
+ "mcp-tools": "workspace:*",
42
+ "sdk-core": "workspace:*",
43
+ "sdk-internal": "workspace:*",
44
+ "shiplight-types": "workspace:*",
45
+ "shiplight-tools": "workspace:*",
33
46
  "tsup": "^8.3.5",
34
- "typescript": "5.5.4",
35
- "mcp-tools": "1.0.0",
36
- "sdk-internal": "0.1.1",
37
- "shiplight-types": "0.1.0",
38
- "shiplight-tools": "1.0.0",
39
- "sdk-core": "0.1.0"
47
+ "typescript": "5.5.4"
40
48
  },
41
49
  "engines": {
42
50
  "node": ">=22.0.0"
43
- },
44
- "scripts": {
45
- "build": "tsup",
46
- "build:deps": "turbo run build --filter=@shiplightai/mcp",
47
- "clean": "rm -rf dist",
48
- "dev": "tsup --watch",
49
- "start": "node dist/index.js",
50
- "typecheck": "tsc --noEmit"
51
51
  }
52
- }
52
+ }