shiplightai 0.1.20 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-THVHM4KG.js +11 -0
- package/dist/cjs/debugger-pw.cjs +8 -4
- package/dist/cjs/index.cjs +1 -1
- package/dist/cli.js +496 -494
- package/dist/debugger-pw.js +1 -1
- package/dist/index.js +5 -13
- package/dist/testFlow-ZLC5L5GT.js +3 -0
- package/package.json +1 -1
- package/dist/testFlow-DPEUSVQO.js +0 -3
package/dist/debugger-pw.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "module";
|
|
2
2
|
const require = __createRequire(import.meta.url);
|
|
3
|
-
import"./chunk-CSINHOOD.js";import w from"express";import*as m from"path";import*as h from"fs/promises";import*as y from"os";var g=class{page;agent;sessionId="pw-"+Math.random().toString(36).slice(2,10);abortController=null;constructor(e,t){this.page=e,this.agent=t}async createSession(e){return{sessionId:this.sessionId}}async executeLogin(e){return{status:"success",details:"Handled by Playwright"}}async ensureBrowser(e){return{page:this.page,liveviewUrl:""}}async startDebug(e){return{sessionId:this.sessionId,liveviewUrl:"",browserWsUrl:""}}async executeAction(e,t,s,n){let o=t.action_data||t.action;if(!o)throw new Error("ActionEntity has no action_data");if(console.error(`[pw-sandbox] executeAction stepId=${JSON.stringify(s)} action_name=${JSON.stringify(o.action_name)} locator=${JSON.stringify(t.locator)} kwargs=${JSON.stringify(o.kwargs)}`),o.action_name==="js_code"&&s==="prelude"){if(this.page.url()==="about:blank"){let l=o.kwargs?.code?.match(/PLAYWRIGHT_STARTING_URL \|\| '([^']*)'/)?.[1];l&&await this.page.goto(l,{waitUntil:"domcontentloaded"})}return{status:"success"}}let r=t.action_description||o.action_name;return{status:"success",...await this.agent.step(this.page,async()=>{await this.agent.execAction(o.action_name,this.page,t)},r,s,n?.stmtUid,!!n?.withSelfHealing)}}async runStep(e,t,s,n,o){let r=[],p={},l=this.agent._actionHandler;this.abortController=new AbortController;try{let u=await this.agent.execute(this.page,t,s,!1);return{success:u?.success??!0,actions:r,details:u?.details}}finally{this.abortController=null}}stopRunStep(e){return this.abortController?(this.abortController.abort(),!0):!1}async evaluate(e,t,s){try{let n=await this.agent.evaluate(this.page,t);return{status:"success",conclusion:n?"true":"false",explanation:n?`Condition met: ${t}`:`Condition not met: ${t}`}}catch(n){return{status:"error",conclusion:"unknown",explanation:n.message}}}async generateAction(e,t,s,n){return this.agent.generate(this.page,t,s,n?.usePureVision)}async takeScreenshot(e){let t=await this.page.screenshot(),s=m.join(y.tmpdir(),`shiplight-screenshot-${Date.now()}.png`);return await h.writeFile(s,t),{screenshot:t.toString("base64"),screenshotPath:s}}async initializeCopilot(e,t){}async sendCopilotMessage(e,t,s,n){throw new Error("Copilot not supported in Playwright debug mode")}abortCopilot(e){return!1}getCopilotTestFlow(e){}async terminateSession(e){console.error("[debugger] Playwright session released")}async cleanupAll(){}};async function S(d){let{yamlFilePath:e,port:t,page:s,agent:n}=d,o=new g(s,n),r=w();r.use(w.json({limit:"10mb"})),r.use((c,i,a)=>{if(i.setHeader("Access-Control-Allow-Origin","*"),i.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, OPTIONS"),i.setHeader("Access-Control-Allow-Headers","Content-Type"),c.method==="OPTIONS")return i.sendStatus(204);a()});let{createTestFlowRouter:p}=await import("./testFlow-
|
|
3
|
+
import"./chunk-CSINHOOD.js";import w from"express";import*as m from"path";import*as h from"fs/promises";import*as y from"os";var g=class{page;agent;sessionId="pw-"+Math.random().toString(36).slice(2,10);abortController=null;constructor(e,t){this.page=e,this.agent=t}async createSession(e){return{sessionId:this.sessionId}}async executeLogin(e){return{status:"success",details:"Handled by Playwright"}}async ensureBrowser(e){return{page:this.page,liveviewUrl:""}}async startDebug(e){return{sessionId:this.sessionId,liveviewUrl:"",browserWsUrl:""}}async executeAction(e,t,s,n){let o=t.action_data||t.action;if(!o)throw new Error("ActionEntity has no action_data");if(console.error(`[pw-sandbox] executeAction stepId=${JSON.stringify(s)} action_name=${JSON.stringify(o.action_name)} locator=${JSON.stringify(t.locator)} kwargs=${JSON.stringify(o.kwargs)}`),o.action_name==="js_code"&&s==="prelude"){if(this.page.url()==="about:blank"){let l=o.kwargs?.code?.match(/PLAYWRIGHT_STARTING_URL \|\| '([^']*)'/)?.[1];l&&await this.page.goto(l,{waitUntil:"domcontentloaded"})}return{status:"success"}}let r=t.action_description||o.action_name;return{status:"success",...await this.agent.step(this.page,async()=>{await this.agent.execAction(o.action_name,this.page,t)},r,s,n?.stmtUid,!!n?.withSelfHealing)}}async runStep(e,t,s,n,o){let r=[],p={},l=this.agent._actionHandler;this.abortController=new AbortController;try{let u=await this.agent.execute(this.page,t,s,!1);return{success:u?.success??!0,actions:r,details:u?.details}}finally{this.abortController=null}}stopRunStep(e){return this.abortController?(this.abortController.abort(),!0):!1}async evaluate(e,t,s){try{let n=await this.agent.evaluate(this.page,t);return{status:"success",conclusion:n?"true":"false",explanation:n?`Condition met: ${t}`:`Condition not met: ${t}`}}catch(n){return{status:"error",conclusion:"unknown",explanation:n.message}}}async generateAction(e,t,s,n){return this.agent.generate(this.page,t,s,n?.usePureVision)}async takeScreenshot(e){let t=await this.page.screenshot(),s=m.join(y.tmpdir(),`shiplight-screenshot-${Date.now()}.png`);return await h.writeFile(s,t),{screenshot:t.toString("base64"),screenshotPath:s}}async initializeCopilot(e,t){}async sendCopilotMessage(e,t,s,n){throw new Error("Copilot not supported in Playwright debug mode")}abortCopilot(e){return!1}getCopilotTestFlow(e){}async terminateSession(e){console.error("[debugger] Playwright session released")}async cleanupAll(){}};async function S(d){let{yamlFilePath:e,port:t,page:s,agent:n}=d,o=new g(s,n),r=w();r.use(w.json({limit:"10mb"})),r.use((c,i,a)=>{if(i.setHeader("Access-Control-Allow-Origin","*"),i.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, OPTIONS"),i.setHeader("Access-Control-Allow-Headers","Content-Type"),c.method==="OPTIONS")return i.sendStatus(204);a()});let{createTestFlowRouter:p}=await import("./testFlow-ZLC5L5GT.js"),{createIntRunnerRouter:l}=await import("./intRunner-5A6M6JSJ.js"),{createCopilotRouter:u}=await import("./copilot-7JTVJQXP.js");r.use(p(e)),r.use(l(o)),r.use(u(o));let b=await new Promise((c,i)=>{let a=r.listen(t,"localhost",()=>c(a));a.on("error",i)});return{url:`http://localhost:${t}`,close:async()=>{await o.cleanupAll(),await new Promise((c,i)=>{b.close(a=>a?i(a):c())})}}}export{g as PlaywrightSandboxService,S as startPlaywrightDebugServer};
|
package/dist/index.js
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "module";
|
|
2
2
|
const require = __createRequire(import.meta.url);
|
|
3
|
-
import{r as
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
`);
|
|
7
|
-
`):["// Skipping js_action: missing code"]});function K(e){let t=e.functionName;if(!t)return null;let r=Array.isArray(e.args)?e.args.map(String):[];if(r.length===0)return`await ${t}()`;let n=["page","testContext","request","agentServices"],s=["undefined","null","true","false"],a=r.map(i=>n.includes(i)||s.includes(i)||/^-?\d+(\.\d+)?$/.test(i)?i:i.startsWith("$")?`agent.agentServices.readVariable('${i.substring(1)}')`:`"${i}"`);return`await ${t}(${a.join(", ")})`}function w(e,t,r,n="main"){let s=[];for(let a=0;a<e.length;a++){let i=e[a],c=`${n}.${a}`,u=Ne(i,t,c,r);u.length>0&&(s.push(...u),a<e.length-1&&s.push(""))}return s}function Ne(e,t,r,n){let s=" ".repeat(t);switch(e.type){case"DRAFT":return Oe(e,t,r,n);case"ACTION":return Ee(e,t,r,n);case"STEP":return Pe(e,t,r,n);case"IF_ELSE":return Le(e,t,r,n);case"WHILE_LOOP":return je(e,t,r,n);default:return[`${s}// Unknown statement type: ${e.type}`]}}function Oe(e,t,r,n){let s=" ".repeat(t),a=e.description?.trim()||"";if(!a)return[`${s}// ${r}: Skipping - no description`];if(n.noAgent)return[`${s}// ${r}: ${d(a)}`,`${s}// DRAFT: ${d(a)} (requires agent - skipped in hook)`];let i=JSON.stringify(a);return[`${s}// ${r}: ${d(a)}`,`${s}// \u26A0 DRAFT: AI-resolved at runtime (~5-10s). Add a locator to make this <1s.`,`${s}page = agent.agentServices.validatePage(page);`,`${s}await agent.run(page, ${i}, '${r}');`]}function Ee(e,t,r,n){let s=" ".repeat(t),a=e.description,i=e.uid,u=n.actionEntityStore?.entries[e.uid]?.action_entity??e.action_entity;if(!u){if(!a)return[`${s}// ${r}: Skipping - no description`];if(n.noAgent)return[`${s}// ${r}: ${d(a)}`,`${s}// DRAFT: ${d(a)} (requires agent - skipped in hook)`];let $=JSON.stringify(a),k=!!e.use_pure_vision;return[`${s}// ${r}: ${d(a)}`,`${s}// \u26A0 DRAFT: AI-resolved at runtime (~5-10s). Add a locator to make this <1s.`,`${s}page = agent.agentServices.validatePage(page);`,`${s}await agent.execute(page, ${$}, '${r}', ${k});`]}let o=e.locator?{...u,locator:e.locator}:u;a&&a!==o.action_description&&(o={...o,action_description:a});let l=o.action_data?.action_name||"",f=o.action_description||"",m=Te(l);if(!m)return[`${s}// ${r}: Unknown action: ${l}`];let g={imports:n.imports},_=m(o,r,g);if(n.noAgent){if(q(o))return[`${s}// ${r}: ${d(f)}`,`${s}// AI action: ${d(f)} (requires agent - skipped in hook)`];let $=Ie(o,l,s,r);return $||[`${s}// ${r}: ${d(f)}`,..._.map(k=>`${s}${k}`)]}if(q(o))return[`${s}// ${r}: ${d(f)}`,`${s}page = agent.agentServices.validatePage(page);`,..._.map($=>`${s}${$}`)];let b=JSON.stringify(f),S=_.map($=>`${s} ${$}`),E=be(o),L=i?`'${i}'`:"undefined";return[`${s}// ${r}: ${d(f)}`,`${s}page = agent.agentServices.validatePage(page);`,`${s}await agent.step(page, async () => {`,...S,`${s}}, ${b}, '${r}', ${L}, ${E});`]}function Pe(e,t,r,n){let s=" ".repeat(t),a=[];e.description&&e.description.trim()&&a.push(`${s}// Step: ${d(e.description)}`);let i=w(e.statements,t,n,r);return a.push(...i),a}function Le(e,t,r,n){let s=" ".repeat(t),a=[];if(a.push(`${s}// ${r}: Conditional check`),e.condition.type==="JS_CODE")a.push(`${s}if (${e.condition.expression}) {`);else{a.push(`${s}// AI Condition: ${d(e.condition.expression)}`);let c=JSON.stringify(e.condition.expression);a.push(`${s}if (await agent.evaluate(page, ${c}, "${r}")) {`)}let i=w(e.then,t+1,n,`${r}.then`);if(a.push(...i),e.else&&e.else.length>0){a.push(`${s}} else {`);let c=w(e.else,t+1,n,`${r}.else`);a.push(...c)}return a.push(`${s}}`),a}function je(e,t,r,n){let s=" ".repeat(t),a=[];a.push(`${s}// ${r}: Loop`);let i=e.timeout_ms??V,c=i/1e3,u=e.timeout_ms?`While loop exceeded timeout of ${c}s`:`While loop exceeded default timeout of ${c}s`,o=`loop_${r.replace(/\./g,"_")}`;if(a.push(`${s}const ${o}_start = Date.now();`),a.push(`${s}const ${o}_timeout = ${i};`),a.push(`${s}const ${o}_check = () => {`),a.push(`${s} if (Date.now() - ${o}_start > ${o}_timeout) {`),a.push(`${s} throw new Error('${u}');`),a.push(`${s} }`),a.push(`${s} return true;`),a.push(`${s}};`),e.condition.type==="JS_CODE")a.push(`${s}while (${o}_check() && (${e.condition.expression})) {`);else{a.push(`${s}// AI Loop Condition: ${d(e.condition.expression)}`);let f=JSON.stringify(e.condition.expression);a.push(`${s}while (${o}_check() && await agent.evaluate(page, ${f}, "${r}")) {`)}let l=w(e.body,t+1,n,`${r}.body`);return a.push(...l),a.push(`${s}}`),a}function Ie(e,t,r,n){let s=e.action_description||"",a=e.action_data?.kwargs||{};switch(t){case"go_to_url":case"open_tab":{let i=a.url||"";return[`${r}// ${n}: ${d(s)}`,`${r}await page.goto(${JSON.stringify(i)}, { waitUntil: 'domcontentloaded' });`]}case"go_back":return[`${r}// ${n}: ${d(s)}`,`${r}await page.goBack();`];case"go_forward":return[`${r}// ${n}: ${d(s)}`,`${r}await page.goForward();`];case"input_text":{let i=a.text||"",c=C(e);return c?[`${r}// ${n}: ${d(s)}`,`${r}await ${c}.fill(${JSON.stringify(i)}, { timeout: ${I} });`]:null}case"select_dropdown_option":{let i=a.text||a.label||"",c=C(e);return c?[`${r}// ${n}: ${d(s)}`,`${r}await ${c}.selectOption({ label: ${JSON.stringify(i)} }, { timeout: ${I} });`]:null}default:return null}}function Ce(e,t){let r=[],n=t?.version||"unknown";r.push(`// @generated by shiplightai v${n}`),r.push(...ee()),r.push(""),t?.use&&Object.keys(t.use).length>0&&(r.push(`test.use(${JSON.stringify(t.use,null,2)});`),r.push(""));let s=new Set,a={imports:s};t?.beforeEach&&t.beforeEach.length>0&&(r.push(...B("beforeEach",t.beforeEach,a)),r.push(""));let i=t?.timeout||t?.skip!==void 0||t?.fail!==void 0||t?.only||t?.slow?{timeout:t.timeout,skip:t.skip,fail:t.fail,only:t.only,slow:t.slow}:void 0;if(t?.parameters&&t.parameters.length>0){let c=t?.testName||e.goal||"Generated test",u=Y(t?.tags);for(let o of t.parameters){let l=Z(e,o.values);r.push(...J(l,`${u}${A(c)} [${A(o.name)}]`,a,0,i)),r.push("")}}else{let c=t?.testName||e.goal||"Generated test",u=Y(t?.tags);r.push(...J(e,`${u}${A(c)}`,a,0,i))}return t?.afterEach&&t.afterEach.length>0&&(r.push(""),r.push(...B("afterEach",t.afterEach,a))),te(r,s),r.join(`
|
|
8
|
-
`)}function Je(e,t){let r=[],n=t?.version||"unknown";r.push(`// @generated by shiplightai v${n}`),r.push(...ee()),r.push(""),t?.use&&Object.keys(t.use).length>0&&(r.push(`test.use(${JSON.stringify(t.use,null,2)});`),r.push(""));let s=new Set,a={imports:s},i=t?.testName||"Test Suite",c=Y(t?.tags),u=e.serial?"test.describe.serial":"test.describe";r.push(`${u}('${c}${A(i)}', () => {`),e.beforeAll&&e.beforeAll.length>0&&(r.push(...j("beforeAll",e.beforeAll,a,1)),r.push("")),e.beforeEach&&e.beforeEach.length>0&&(r.push(...j("beforeEach",e.beforeEach,a,1)),r.push(""));for(let o=0;o<e.tests.length;o++){let l=e.tests[o],f=l.timeout||l.skip!==void 0||l.fail!==void 0||l.only||l.slow?{timeout:l.timeout,skip:l.skip,fail:l.fail,only:l.only,slow:l.slow}:void 0;if(l.parameters&&l.parameters.length>0)for(let m of l.parameters){let g=Z(l.testFlow,m.values);r.push(...J(g,`${A(l.name)} [${A(m.name)}]`,a,1,f)),r.push("")}else r.push(...J(l.testFlow,A(l.name),a,1,f)),(o<e.tests.length-1||e.afterEach||e.afterAll)&&r.push("")}return e.afterEach&&e.afterEach.length>0&&(r.push(...j("afterEach",e.afterEach,a,1)),r.push("")),e.afterAll&&e.afterAll.length>0&&r.push(...j("afterAll",e.afterAll,a,1)),r.push("});"),te(r,s),r.join(`
|
|
9
|
-
`)}function Y(e){return e&&e.length>0?e.map(t=>`@${t}`).join(" ")+" ":""}function J(e,t,r,n=0,s){let a=" ".repeat(n),i=[],c=s?.only?"test.only":"test";i.push(`${a}${c}('${t}', async ({ page, agent }) => {`),s?.skip===!0?i.push(`${a} test.skip();`):typeof s?.skip=="string"&&i.push(`${a} test.skip(true, '${A(s.skip)}');`),s?.fail===!0?i.push(`${a} test.fail();`):typeof s?.fail=="string"&&i.push(`${a} test.fail(true, '${A(s.fail)}');`),s?.slow&&i.push(`${a} test.slow();`),s?.timeout&&i.push(`${a} test.setTimeout(${s.timeout});`);let u=e.teardown&&e.teardown.length>0,o=n+1;if(u){if(i.push(`${a} try {`),e.statements&&e.statements.length>0){i.push(`${a} // Test steps`);let f=w(e.statements,o+1,r);i.push(...f)}i.push(`${a} } finally {`),i.push(`${a} // Teardown`);let l=w(e.teardown,o+1,r,"teardown");i.push(...l),i.push(`${a} }`)}else if(e.statements&&e.statements.length>0){i.push(`${a} // Test steps`);let l=w(e.statements,o,r);i.push(...l)}return i.push(`${a}});`),i}function B(e,t,r){let n=[],s=Q(t);return n.push(`test.${e}(async ({ page, agent }) => {`),n.push(...w(s,1,r,e)),n.push("});"),n}function j(e,t,r,n){let s=" ".repeat(n),a=[],i=Q(t);if(e==="beforeAll"||e==="afterAll"){let u={...r,noAgent:!0};a.push(`${s}test.${e}(async ({ browser }, workerInfo) => {`),a.push(`${s} const page = await browser.newPage({ baseURL: workerInfo.project.use.baseURL });`),a.push(...w(i,n+1,u,e)),a.push(`${s} await page.close();`),a.push(`${s}});`)}else a.push(`${s}test.${e}(async ({ page, agent }) => {`),a.push(...w(i,n+1,r,e)),a.push(`${s}});`);return a}function Q(e){let r=Se({goal:"_hook",statements:e});return P(r).statements}function Z(e,t){let r=G(e);for(let[n,s]of Object.entries(t))r=r.split(`<<${n}>>`).join(String(s));return P(r)}function ee(){return["import { test, expect } from 'shiplightai/fixture';"]}function te(e,t){if(t.size>0){let r=0;for(let s=0;s<e.length;s++)e[s].startsWith("import ")&&(r=s+1);let n=Array.from(t);e.splice(r,0,...n)}}var X=5;function ne(e,t){let r={expandingPaths:new Set([se(t)]),depth:0,referencedPaths:new Set},n={...e};Array.isArray(n.statements)&&(n.statements=v(n.statements,t,r)),Array.isArray(n.teardown)&&(n.teardown=v(n.teardown,t,r));for(let s of["beforeAll","afterAll","beforeEach","afterEach"])Array.isArray(n[s])&&(n[s]=v(n[s],t,r));return{doc:n,referencedTemplatePaths:Array.from(r.referencedPaths)}}function v(e,t,r){let n=[];for(let s of e)if(We(s)){let a=Re(s,t,r);n.push(...a)}else n.push(De(s,t,r));return n}function We(e){return typeof e=="object"&&e!==null&&typeof e.template=="string"}function Re(e,t,r){if(r.depth>=X)throw new Error(`Template expansion exceeded maximum depth of ${X}. Check for deeply nested or circular template references.`);let n=se(Ye(t),e.template);if(r.expandingPaths.has(n))throw new Error(`Circular template reference detected: ${n} is already being expanded. Stack: ${Array.from(r.expandingPaths).join(" \u2192 ")} \u2192 ${n}`);r.referencedPaths.add(n);let s;try{s=He(n,"utf-8")}catch(l){throw new Error(`Failed to read template file: ${n} (referenced from ${t}): ${l.message}`)}let a=z(s);if(!a||typeof a!="object")throw new Error(`Invalid template file: ${n} \u2014 expected a YAML object`);let i=a.params||[],c=e.params||{};for(let l of i)if(!(l in c))throw new Error(`Template ${e.template} requires param "${l}" but it was not provided. Required params: [${i.join(", ")}]`);let u=a.statements;if(!Array.isArray(u))throw new Error(`Template ${e.template} must have a "statements" array`);if(Object.keys(c).length>0){let f=Me(u);for(let[m,g]of Object.entries(c))f=f.split(`<<${m}>>`).join(String(g));u=z(f)}let o={expandingPaths:new Set([...r.expandingPaths,n]),depth:r.depth+1,referencedPaths:r.referencedPaths};return v(u,n,o)}function De(e,t,r){if(typeof e!="object"||e===null)return e;let n={...e};return Array.isArray(n.statements)&&(n.statements=v(n.statements,t,r)),Array.isArray(n.THEN)&&(n.THEN=v(n.THEN,t,r)),Array.isArray(n.ELSE)&&(n.ELSE=v(n.ELSE,t,r)),Array.isArray(n.DO)&&(n.DO=v(n.DO,t,r)),n}function M(e,t){let r=Fe(e),n=r?.name,s=r?.tags,a=r?.use;if(r&&(r.name!==void 0||r.tags!==void 0||r.use!==void 0)&&(delete r.name,delete r.tags,delete r.use),r?.suite){if(r.goal||r.statements)throw new Error('YAML file cannot have both "suite" and top-level "goal"/"statements". Use either suite format or single-test format.');return Ue(r,n,s,a,t)}return Ge(r,n,s,a,t)}function Ge(e,t,r,n,s){let a=e?.beforeEach,i=e?.afterEach,c=ae(e?.parameters),u=e?.timeout,o=e?.skip,l=e?.fail,f=e?.only,m=e?.slow;e&&(delete e.beforeEach,delete e.afterEach,delete e.parameters,delete e.timeout,delete e.skip,delete e.fail,delete e.only,delete e.slow),e&&!e.goal&&t&&(e.goal=t);let g=[];if(s&&e&&typeof e=="object"){let S=ne(e,s);e=S.doc,g=S.referencedTemplatePaths}let _=re(e);return{testFlow:P(_),name:t,tags:r,use:n,beforeEach:a,afterEach:i,parameters:c,timeout:u,skip:o,fail:l,only:f,slow:m,referencedTemplatePaths:g}}function Ue(e,t,r,n,s){let a=e.suite;if(!Array.isArray(a.tests)||a.tests.length===0)throw new Error('Suite must have a non-empty "tests" array.');let i=a.serial,c=a.beforeAll,u=a.afterAll,o=a.beforeEach,l=a.afterEach,f=[],m=a.tests.map(g=>{if(!g.name)throw new Error('Each test in a suite must have a "name" field.');if(!Array.isArray(g.statements)||g.statements.length===0)throw new Error(`Suite test "${g.name}" must have a non-empty "statements" array.`);let _={goal:g.name,statements:g.statements};g.teardown&&(_.teardown=g.teardown);let b=[],S=_;if(s&&typeof _=="object"){let k=ne(_,s);S=k.doc,b=k.referencedTemplatePaths,f.push(...b)}let E=re(S),L=P(E),$=ae(g.parameters);return{testFlow:L,name:g.name,parameters:$,timeout:g.timeout,skip:g.skip,fail:g.fail,only:g.only,slow:g.slow}});return{suite:{serial:i,beforeAll:c,afterAll:u,beforeEach:o,afterEach:l,tests:m},name:t,tags:r,use:n,referencedTemplatePaths:f}}function ae(e){if(!(!Array.isArray(e)||e.length===0))return e.map((t,r)=>{if(!t.name)throw new Error(`Parameter set at index ${r} must have a "name" field.`);if(!t.values||typeof t.values!="object")throw new Error(`Parameter set "${t.name}" must have a "values" object.`);return{name:t.name,values:t.values}})}function ie(e,t,r){let n=/\btemplate:\s/.test(e),s=/^suite:/m.test(e),a=n||s?null:U(e);if(a&&!a.valid)return{valid:!1,errors:a.errors,warnings:[],stats:a.stats};let i,c,u=[];try{let o=M(e,t);u=o.referencedTemplatePaths;let l={version:r?.version};o.suite?i=Je(o.suite,{...l,testName:o.name,tags:o.tags,use:o.use}):i=Ce(o.testFlow,{...l,testName:o.name,tags:o.tags,use:o.use,beforeEach:o.beforeEach,afterEach:o.afterEach,parameters:o.parameters,timeout:o.timeout,skip:o.skip,fail:o.fail,only:o.only,slow:o.slow});let f=i.split(`
|
|
10
|
-
`).filter(m=>!m.startsWith("import ")).join(`
|
|
11
|
-
`);new Function(f),c=t.replace(/\.test\.yaml$/,".yaml.spec.ts"),qe(Ke(c),{recursive:!0}),Ve(c,i)}catch(o){let l=o.message.includes("Unexpected token")?" This usually means a YAML escaping issue \u2014 in double-quoted strings, use \\\\/ instead of \\/ for regex patterns, or use single quotes / block scalars.":" This may indicate a transpiler bug \u2014 please report it.";return{valid:!1,errors:[`Transpilation failed: ${o.message}.${l}`],warnings:[],stats:a?.stats??{total:0,withLocator:0,coverage:0},referencedTemplatePaths:u}}return{valid:!0,errors:[],warnings:a?.warnings??[],stats:a?.stats??{total:0,withLocator:0,coverage:0},specFile:c,referencedTemplatePaths:u}}var oe={name:"shiplightai",version:"0.1.20",type:"module",description:"Shiplight CLI for running and debugging .test.yaml files",main:"dist/index.js",types:"dist/index.d.ts",bin:{shiplight:"dist/cli.js"},exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.js",require:"./dist/cjs/index.cjs",default:"./dist/index.js"},"./fixture":{types:"./dist/fixture.d.ts",import:"./dist/fixture.js",require:"./dist/cjs/fixture.cjs",default:"./dist/fixture.js"},"./debugger-pw":{types:"./dist/debugger-pw.d.ts",import:"./dist/debugger-pw.js",require:"./dist/cjs/debugger-pw.cjs",default:"./dist/debugger-pw.js"}},files:["dist","README.md"],publishConfig:{registry:"https://registry.npmjs.org",access:"public"},scripts:{build:"tsup","build:cli":"tsup",clean:"rm -rf dist",dev:"tsup --watch",test:"playwright test",typecheck:"tsc --noEmit"},dependencies:{"@babel/plugin-transform-typescript":"^7.27.0","@babel/preset-env":"^7.26.9","@babel/preset-typescript":"^7.27.0","@anthropic-ai/claude-agent-sdk":"^0.1.72","@modelcontextprotocol/sdk":"^0.5.0",axios:"^1.6.0",dotenv:"^16.0.3",express:"^4.21.0",glob:"^13.0.0",open:"^10.1.0",sharp:"^0.34.5",uuid:"^11.1.0",yaml:"^2.8.0",zod:"^3.22.0","zod-to-json-schema":"^3.24.6"},devDependencies:{"@playwright/test":"1.58.2","@types/express":"^4.17.21","@types/node":"^24.0.0",copilot3:"workspace:*","mcp-tools":"workspace:*","sdk-core":"workspace:*","sdk-internal":"workspace:*","shiplight-tools":"workspace:*","shiplight-types":"workspace:*",tsup:"^8.3.5",typescript:"5.5.4","web-session":"workspace:*"},peerDependencies:{"@playwright/test":"1.58.2"},engines:{node:">=22.0.0"},keywords:["playwright","yaml","testing","automation","ai","shiplight","mcp"],author:"Shiplight",license:"MIT"};var W=oe.version;function ce(e){try{return ze(e).mtimeMs}catch{return 0}}var Qe=`// @generated by shiplightai v${W}`;function Ze(e,t){if(!pe(e)||le(e,"utf-8").split(`
|
|
12
|
-
`,1)[0]!==Qe)return!1;let n=ce(e);for(let s of t)if(ce(s)>n)return!1;return!0}function et(e){let t=process.argv.slice(2),r=[],n=R(e);for(let s of t){if(s.startsWith("-"))continue;let a=s.endsWith(".yaml.spec.ts")?s.replace(/\.yaml\.spec\.ts$/,".test.yaml"):s;if(!a.endsWith(".test.yaml"))continue;let i=R(e,a);pe(i)&&r.push(i.startsWith(n)?i.slice(n.length+1):a)}return r.length>0?r:null}function ue(e){let t=et(e.cwd),r=t??Xe("**/*.test.yaml",{cwd:e.cwd,ignore:["**/node_modules/**"]}),n=[];for(let s of r){let a=R(e.cwd,s),i=a.replace(/\.test\.yaml$/,".yaml.spec.ts"),c=le(a,"utf-8");try{let u=M(c,a);if(Ze(i,[a,...u.referencedTemplatePaths]))continue;let o=ie(c,a,{version:W});if(!o.valid)throw new Error(o.errors.join("; "))}catch(u){console.error(`[shiplight] Failed to transpile ${s}:`,u),n.push({file:s,error:u})}}if(n.length>0){let s=`[shiplight] Transpilation failed for ${n.length} file(s):
|
|
13
|
-
`+n.map(a=>` - ${a.file}`).join(`
|
|
14
|
-
`);if(t)throw new Error(s);console.warn(s+" (skipped)")}}import*as O from"path";import*as de from"fs";import{mkdir as rt}from"fs/promises";import*as H from"fs";import*as N from"path";function D(e){let t=null,r=process.env.SHIPLIGHT_LOGIN_EMAIL,n=process.env.SHIPLIGHT_LOGIN_PASSWORD;return r&&n&&(t={username:r,password:n}),t||(t=tt(e)),t?(process.env.SHIPLIGHT_LOGIN_URL&&(t.loginUrl=process.env.SHIPLIGHT_LOGIN_URL),process.env.SHIPLIGHT_LOGIN_TOTP_SECRET&&(t.totpSecret=process.env.SHIPLIGHT_LOGIN_TOTP_SECRET),t):null}function F(e){if(e.startsWith("$")){let t=e.slice(1);return process.env[t]||e}return e}function tt(e){let t=N.resolve(e),r=N.resolve(process.cwd());for(;;){for(let s of["shiplight.config.json","login.config.json"]){let a=N.join(t,s);if(H.existsSync(a))try{let i=JSON.parse(H.readFileSync(a,"utf-8"));if(i.username&&i.password)return{username:F(i.username),password:F(i.password),loginUrl:i.url?F(i.url):void 0,totpSecret:i.totp_secret?F(i.totp_secret):void 0}}catch{}}if(t===r)break;let n=N.dirname(t);if(n===t)break;t=n}return null}var me="auth.setup.ts",fe=".auth",ge="storage-state.json";async function st(e,t){let r=O.resolve(t),n=O.join(r,fe,ge);if(de.existsSync(n)){console.log("[INFO] Storage state exists, skipping login. Delete",n,"to force re-auth.");return}let s=D(r);if(!s)return;let{WebAgent:a,createAgentContext:i,configureSdk:c,VariableStore:u,LoginType:o,TwoFactorAuthType:l}=await import("./dist-CXOVVE77.js");c({env:{GOOGLE_API_KEY:process.env.GOOGLE_API_KEY??"",ANTHROPIC_API_KEY:process.env.ANTHROPIC_API_KEY??""}});let{resolveModelFromEnv:f}=await import("./dist-SRXGJZ7P.js"),m=f(process.env);if(!m)throw new Error("No AI model configured. Set WEB_AGENT_MODEL, ANTHROPIC_API_KEY, or GOOGLE_API_KEY.");let g=new a(i({model:m,variableStore:new u})),{username:_,password:b,loginUrl:S,totpSecret:E}=s,L=S||e.url()||"about:blank";if(!(await g.loginPage(e,{site_url:L,num_verification_exprs:0,account:{type:o.PASSWORD,username:_,password:b,...E&&{two_factor_auth_config:{type:l.TOTP,data:E}}}})).success)throw new Error("Login failed.");let k=O.join(r,fe);await rt(k,{recursive:!0}),await e.context().storageState({path:O.join(k,ge)})}function he(){return`// @generated by shiplightai \u2014 do not edit
|
|
3
|
+
import{r as Y,s as K}from"./chunk-JHSENQ4F.js";import"./chunk-32JFHFFG.js";import"./chunk-2F3YRAA7.js";import"./chunk-RTTIJBGI.js";import"./chunk-YU3XZJIJ.js";import"./chunk-USRSZQWN.js";import{l as W}from"./chunk-LPSNOKYP.js";import"./chunk-YHOTGR6H.js";import"./chunk-YDR4P3GA.js";import{a as U}from"./chunk-DJDHFWEV.js";import{a as I,b as E}from"./chunk-THVHM4KG.js";import"./chunk-JNRJXAJS.js";import"./chunk-CSINHOOD.js";import*as c from"fs";import*as a from"path";import tt from"dotenv";import{globSync as et}from"glob";import{readFileSync as P,statSync as V,existsSync as T}from"fs";import{resolve as S}from"path";import{globSync as q}from"glob";var L={name:"shiplightai",version:"0.1.21",type:"module",description:"Shiplight CLI for running and debugging .test.yaml files",main:"dist/index.js",types:"dist/index.d.ts",bin:{shiplight:"dist/cli.js"},exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.js",require:"./dist/cjs/index.cjs",default:"./dist/index.js"},"./fixture":{types:"./dist/fixture.d.ts",import:"./dist/fixture.js",require:"./dist/cjs/fixture.cjs",default:"./dist/fixture.js"},"./debugger-pw":{types:"./dist/debugger-pw.d.ts",import:"./dist/debugger-pw.js",require:"./dist/cjs/debugger-pw.cjs",default:"./dist/debugger-pw.js"}},files:["dist","README.md"],publishConfig:{registry:"https://registry.npmjs.org",access:"public"},scripts:{build:"tsup","build:cli":"tsup",clean:"rm -rf dist",dev:"tsup --watch",test:"playwright test",typecheck:"tsc --noEmit"},dependencies:{"@babel/plugin-transform-typescript":"^7.27.0","@babel/preset-env":"^7.26.9","@babel/preset-typescript":"^7.27.0","@anthropic-ai/claude-agent-sdk":"^0.1.72","@modelcontextprotocol/sdk":"^0.5.0",axios:"^1.6.0",dotenv:"^16.0.3",express:"^4.21.0",glob:"^13.0.0",open:"^10.1.0",sharp:"^0.34.5",uuid:"^11.1.0",yaml:"^2.8.0",zod:"^3.22.0","zod-to-json-schema":"^3.24.6"},devDependencies:{"@playwright/test":"1.58.2","@types/express":"^4.17.21","@types/node":"^24.0.0",copilot3:"workspace:*","mcp-tools":"workspace:*","sdk-core":"workspace:*","sdk-internal":"workspace:*","shiplight-tools":"workspace:*","shiplight-types":"workspace:*",tsup:"^8.3.5",typescript:"5.5.4","web-session":"workspace:*"},peerDependencies:{"@playwright/test":"1.58.2"},engines:{node:">=22.0.0"},keywords:["playwright","yaml","testing","automation","ai","shiplight","mcp"],author:"Shiplight",license:"MIT"};var m=L.version;function x(t){try{return V(t).mtimeMs}catch{return 0}}var z=`// @generated by shiplightai v${m}`;function J(t,e){if(!T(t)||P(t,"utf-8").split(`
|
|
4
|
+
`,1)[0]!==z)return!1;let r=x(t);for(let s of e)if(x(s)>r)return!1;return!0}function B(t){let e=process.argv.slice(2),i=[],r=S(t);for(let s of e){if(s.startsWith("-"))continue;let o=s.endsWith(".yaml.spec.ts")?s.replace(/\.yaml\.spec\.ts$/,".test.yaml"):s;if(!o.endsWith(".test.yaml"))continue;let n=S(t,o);T(n)&&i.push(n.startsWith(r)?n.slice(r.length+1):o)}return i.length>0?i:null}function A(t){let e=B(t.cwd),i=e??q("**/*.test.yaml",{cwd:t.cwd,ignore:["**/node_modules/**"]}),r=[];for(let s of i){let o=S(t.cwd,s),n=o.replace(/\.test\.yaml$/,".yaml.spec.ts"),p=P(o,"utf-8");try{let l=I(p,o);if(J(n,[o,...l.referencedTemplatePaths]))continue;let u=E(p,o,{version:m});if(!u.valid)throw new Error(u.errors.join("; "))}catch(l){console.error(`[shiplight] Failed to transpile ${s}:`,l),r.push({file:s,error:l})}}if(r.length>0){let s=`[shiplight] Transpilation failed for ${r.length} file(s):
|
|
5
|
+
`+r.map(o=>` - ${o.file}`).join(`
|
|
6
|
+
`);if(e)throw new Error(s);console.warn(s+" (skipped)")}}import*as g from"path";import*as j from"fs";import{mkdir as X}from"fs/promises";import*as h from"fs";import*as f from"path";function y(t){let e=null,i=process.env.SHIPLIGHT_LOGIN_EMAIL,r=process.env.SHIPLIGHT_LOGIN_PASSWORD;return i&&r&&(e={username:i,password:r}),e||(e=Q(t)),e?(process.env.SHIPLIGHT_LOGIN_URL&&(e.loginUrl=process.env.SHIPLIGHT_LOGIN_URL),process.env.SHIPLIGHT_LOGIN_TOTP_SECRET&&(e.totpSecret=process.env.SHIPLIGHT_LOGIN_TOTP_SECRET),e):null}function d(t){if(t.startsWith("$")){let e=t.slice(1);return process.env[e]||t}return t}function Q(t){let e=f.resolve(t),i=f.resolve(process.cwd());for(;;){for(let s of["shiplight.config.json","login.config.json"]){let o=f.join(e,s);if(h.existsSync(o))try{let n=JSON.parse(h.readFileSync(o,"utf-8"));if(n.username&&n.password)return{username:d(n.username),password:d(n.password),loginUrl:n.url?d(n.url):void 0,totpSecret:n.totp_secret?d(n.totp_secret):void 0}}catch{}}if(e===i)break;let r=f.dirname(e);if(r===e)break;e=r}return null}var k="auth.setup.ts",b=".auth",O="storage-state.json";async function Z(t,e){let i=g.resolve(e),r=g.join(i,b,O);if(j.existsSync(r)){console.log("[INFO] Storage state exists, skipping login. Delete",r,"to force re-auth.");return}let s=y(i);if(!s)return;let{WebAgent:o,createAgentContext:n,configureSdk:p,VariableStore:l,LoginType:u,TwoFactorAuthType:G}=await import("./dist-CXOVVE77.js");p({env:{GOOGLE_API_KEY:process.env.GOOGLE_API_KEY??"",ANTHROPIC_API_KEY:process.env.ANTHROPIC_API_KEY??""}});let{resolveModelFromEnv:H}=await import("./dist-SRXGJZ7P.js"),w=H(process.env);if(!w)throw new Error("No AI model configured. Set WEB_AGENT_MODEL, ANTHROPIC_API_KEY, or GOOGLE_API_KEY.");let N=new o(n({model:w,variableStore:new l})),{username:R,password:D,loginUrl:F,totpSecret:v}=s,M=F||t.url()||"about:blank";if(!(await N.loginPage(t,{site_url:M,num_verification_exprs:0,account:{type:u.PASSWORD,username:R,password:D,...v&&{two_factor_auth_config:{type:G.TOTP,data:v}}}})).success)throw new Error("Login failed.");let _=g.join(i,b);await X(_,{recursive:!0}),await t.context().storageState({path:g.join(_,O)})}function C(){return`// @generated by shiplightai \u2014 do not edit
|
|
15
7
|
import { test as setup } from '@playwright/test';
|
|
16
8
|
import { authSetup } from 'shiplightai';
|
|
17
9
|
|
|
18
10
|
setup('authenticate', async ({ page }) => {
|
|
19
11
|
await authSetup(page, import.meta.dirname);
|
|
20
12
|
});
|
|
21
|
-
`}function
|
|
13
|
+
`}function st(t={}){t.dotenv!==!1&&rt(t.scanDir||process.cwd());let e=t.scanDir||process.cwd();return A({cwd:e}),it(e),t.apiKey&&(process.env.__SHIPLIGHT_API_KEY=t.apiKey),{}}function rt(t){let e=[],i=a.resolve(t),r=a.resolve(process.cwd());for(;;){let s=a.join(i,".env");if(c.existsSync(s)&&e.push(s),i===r)break;let o=a.dirname(i);if(o===i)break;i=o}for(let s of e)tt.config({path:s})}function it(t){let e=et("**/shiplight.config.json",{cwd:t,ignore:["**/node_modules/**"]});for(let i of e){let r=a.resolve(t,i),s=a.dirname(r),o=a.join(s,k),n=!1;try{let p=JSON.parse(c.readFileSync(r,"utf-8"));n=!!(p.username&&p.password)}catch{}if(!n&&process.env.SHIPLIGHT_LOGIN_EMAIL&&process.env.SHIPLIGHT_LOGIN_PASSWORD&&(n=!0),!!n){try{let p=c.statSync(r).mtimeMs;if(c.statSync(o).mtimeMs>p)continue}catch{}c.writeFileSync(o,C())}}}export{W as VariableStore,Y as WebAgent,Z as authSetup,U as configureSdk,K as createAgentContext,y as resolveLoginConfig,st as shiplightConfig};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "module";
|
|
2
|
+
const require = __createRequire(import.meta.url);
|
|
3
|
+
import{a as d}from"./chunk-THVHM4KG.js";import{ca as c,fa as w,ga as p}from"./chunk-JNRJXAJS.js";import"./chunk-CSINHOOD.js";import{Router as g}from"express";import*as r from"fs/promises";import*as u from"path";import{stringify as b}from"yaml";function m(t){if(!t||t.length===0)return[];let n=b({goal:"_hook",statements:t});return p(n).statements}function y(t){let n=g();return n.get("/api/test-flow",async(l,a)=>{try{let e=await r.readFile(t,"utf-8"),f=w(e),s=d(e,t),i=await r.stat(t);s.suite?a.json({isSuite:!0,suite:{beforeAll:m(s.suite.beforeAll),afterAll:m(s.suite.afterAll),beforeEach:m(s.suite.beforeEach),afterEach:m(s.suite.afterEach),tests:s.suite.tests.map(o=>({name:o.name,testFlow:o.testFlow,skip:o.skip,timeout:o.timeout,fail:o.fail,only:o.only,slow:o.slow}))},metadata:f,name:s.name,tags:s.tags,use:s.use,filePath:t,fileName:u.basename(t),lastModified:i.mtimeMs}):a.json({testFlow:s.testFlow,metadata:f,filePath:t,fileName:u.basename(t),lastModified:i.mtimeMs})}catch(e){if(e.code==="ENOENT")return a.status(404).json({error:`File not found: ${t}`});console.error("[debugger] Error loading test flow:",e),a.status(500).json({error:e.message})}}),n.put("/api/test-flow",async(l,a)=>{try{let{testFlow:e,metadata:f}=l.body;if(!e)return a.status(400).json({error:"testFlow is required"});let s=c(e,f),i=t+".tmp";await r.writeFile(i,s,"utf-8"),await r.rename(i,t);let o=await r.stat(t);a.json({success:!0,lastModified:o.mtimeMs})}catch(e){console.error("[debugger] Error saving test flow:",e),a.status(500).json({error:e.message})}}),n}export{y as createTestFlowRouter};
|
package/package.json
CHANGED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { createRequire as __createRequire } from "module";
|
|
2
|
-
const require = __createRequire(import.meta.url);
|
|
3
|
-
import{ca as c,fa as u,ga as m}from"./chunk-JNRJXAJS.js";import"./chunk-CSINHOOD.js";import{Router as g}from"express";import*as o from"fs/promises";import*as w from"path";function j(r){let a=g();return a.get("/api/test-flow",async(f,s)=>{try{let t=await o.readFile(r,"utf-8"),n=m(t),i=u(t),e=await o.stat(r);s.json({testFlow:n,metadata:i,filePath:r,fileName:w.basename(r),lastModified:e.mtimeMs})}catch(t){if(t.code==="ENOENT")return s.status(404).json({error:`File not found: ${r}`});console.error("[debugger] Error loading test flow:",t),s.status(500).json({error:t.message})}}),a.put("/api/test-flow",async(f,s)=>{try{let{testFlow:t,metadata:n}=f.body;if(!t)return s.status(400).json({error:"testFlow is required"});let i=c(t,n),e=r+".tmp";await o.writeFile(e,i,"utf-8"),await o.rename(e,r);let d=await o.stat(r);s.json({success:!0,lastModified:d.mtimeMs})}catch(t){console.error("[debugger] Error saving test flow:",t),s.status(500).json({error:t.message})}}),a}export{j as createTestFlowRouter};
|