"use strict";var k=Object.create;var T=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var J=Object.getPrototypeOf,Y=Object.prototype.hasOwnProperty;var W=(r,s)=>{for(var e in s)T(r,e,{get:s[e],enumerable:!0})},H=(r,s,e,t)=>{if(s&&typeof s=="object"||typeof s=="function")for(let o of G(s))!Y.call(r,o)&&o!==e&&T(r,o,{get:()=>s[o],enumerable:!(t=F(s,o))||t.enumerable});return r};var P=(r,s,e)=>(e=r!=null?k(J(r)):{},H(s||!r||!r.__esModule?T(e,"default",{value:r,enumerable:!0}):e,r)),z=r=>H(T({},"__esModule",{value:!0}),r);var X={};W(X,{DebuggerManager:()=>_});module.exports=z(X);var O=P(require("http"),1),L=P(require("net"),1),y=P(require("path"),1),U=P(require("fs"),1),M=require("crypto");var w=P(require("fs"),1),m=P(require("path"),1),C=require("child_process"),N=require("yaml");function V(r){let s=!1;return()=>{if(!s){s=!0;try{w.unlinkSync(r)}catch{}}}}function E(r){let s=["playwright.config.ts","playwright.config.js","playwright.config.mjs"],e=m.resolve(r);for(;;){for(let o of s){let n=m.join(e,o);if(w.existsSync(n))return n}let t=m.dirname(e);if(t===e)break;e=t}return null}function K(r,s,e,t){let o={...e},n=o.launchOptions?.args??[];return o.launchOptions={...o.launchOptions??{},args:[...n,"--remote-debugging-port=0"]},`// @generated by shiplightai \u2014 temporary debug test
import { test } from 'shiplightai/fixture';
${`
test.use(${JSON.stringify(o)});
`}
test('__shiplight_debug__', async ({ page, agent }) => {
  // Disable timeout \u2014 debugger runs until user stops it
  test.setTimeout(0);

  const { startPlaywrightDebugServer } = await import('shiplightai/debugger-pw');

  const server = await startPlaywrightDebugServer({
    yamlFilePath: ${JSON.stringify(r)},
    port: ${s},
    page,
    agent,
    projectRoot: ${JSON.stringify(t??null)},
  });

  console.error('[debugger] Playwright sandbox ready on internal port ${s}');

  // Keep alive until the server is closed externally (Ctrl+C kills the process)
  await new Promise(() => {});
});
`}async function Q(r){let{createServer:s}=await import("net");for(let e=r;e<r+20;e++)if(await new Promise(o=>{let n=s();n.once("error",()=>o(!1)),n.once("listening",()=>{n.close(()=>o(!0))}),n.listen(e,"127.0.0.1")}))return e;throw new Error(`No available port found in range ${r}-${r+19}`)}async function j(r){let{yamlFilePath:s,configPath:e,tempSuffix:t="",headed:o}=r,n=m.dirname(e),a=await Q(16174),i;if(!w.existsSync(s))throw new Error(`Please select a test file before starting the debug session. File not found: ${s}`);try{let d=(0,N.parse)(w.readFileSync(s,"utf-8"));d?.use&&typeof d.use=="object"&&!Array.isArray(d.use)&&(i=d.use),d?.base_url&&!i?.baseURL&&(i={...i,baseURL:d.base_url}),d?.settings?.auto_dismiss_modal!==void 0&&(i={...i,autoDismissModal:!!d.settings.auto_dismiss_modal}),d?.settings?.browser_timezone!=null&&(i={...i,timezoneId:String(d.settings.browser_timezone)}),d?.settings?.browser_language!=null&&(i={...i,locale:String(d.settings.browser_language)}),d?.settings?.extra_http_headers!=null&&typeof d.settings.extra_http_headers=="object"&&(i={...i,extraHTTPHeaders:d.settings.extra_http_headers})}catch(d){console.error("[debugger] Could not parse YAML for `use` block:",d)}let u=m.dirname(m.resolve(s)),l=t?`-${t}`:"",g=m.join(u,`.__shiplight_debug__${l}.yaml.spec.ts`),p=K(s,a,i,n);w.writeFileSync(g,p);let h=V(g),x=["playwright","test",g,...o?["--headed"]:[]],c=(0,C.spawn)("npx",x,{stdio:["ignore","pipe","pipe"],shell:!0,cwd:n,env:{...process.env,PWDEBUG:"console",SHIPLIGHT_REGISTRY_URL:""}});c.stdout?.on("data",d=>{process.stderr.write(d)}),c.stderr?.on("data",d=>{process.stderr.write(d)});let f=()=>{c.killed||c.kill("SIGTERM")};process.on("SIGTERM",f),process.on("SIGINT",f),process.on("exit",f),c.on("close",d=>{process.removeListener("SIGTERM",f),process.removeListener("SIGINT",f),process.removeListener("exit",f),h(),d!==0&&d!==null&&console.error(`[debugger] Playwright process exited with code ${d}`)}),console.error("[debugger] Waiting for Playwright sandbox to start...");let $=["127.0.0.1","::1"];async function v(d){try{let b=d.includes(":")?`[${d}]`:d,R=await fetch(`http://${b}:${a}/api/test-flow`);if(R.ok){try{await R.text()}catch{}return!0}}catch{}return!1}let S=null;for(let d=0;d<180;d++){if(c.exitCode!==null)throw h(),new Error(`Playwright process exited with code ${c.exitCode} before sandbox was ready`);for(let b of $)if(await v(b)){S=b;break}if(S){console.error(`[debugger] Playwright sandbox ready on ${S}:${a}`);break}if(d===179)throw f(),h(),new Error("Timed out waiting for Playwright sandbox to start (180s)");await new Promise(b=>setTimeout(b,1e3))}if(!S)throw f(),h(),new Error("Sandbox poll finished without a reachable host");return{port:a,host:S,pid:c.pid??0,cleanup:async()=>{f(),h()}}}var D=1e4,_=class{sessions=new Map;byYamlPath=new Map;options;spawner;headed;constructor(s={}){this.options=s,this.spawner=s.spawner??j,this.headed=s.headed??!1}log(s){this.options.onLog?this.options.onLog(s):console.error(s)}notifyStateChange(s){try{this.options.onSessionStateChange?.({...s})}catch(e){this.log(`[manager] onSessionStateChange listener threw: ${e.message}`)}}openSession(s){let e=y.resolve(s),t=this.byYamlPath.get(e);if(t){let l=this.sessions.get(t);if(l&&l.session.status!=="ended")return{...l.session};this.byYamlPath.delete(e)}if(!U.existsSync(e))throw new Error(`YAML file not found: ${e}`);if(!E(y.dirname(e)))throw new Error(`No Playwright config found for ${e} (searched parents for playwright.config.{ts,js,mjs}).`);let n=`dbg-${(0,M.randomUUID)().slice(0,8)}`,a=new Date().toISOString(),i={sessionId:n,yamlPath:e,innerPort:0,innerHost:"127.0.0.1",pid:0,startedAt:a,status:"idle",exitInfo:null},u={session:i,cleanup:async()=>{},readyPromise:Promise.resolve()};return this.sessions.set(n,u),this.byYamlPath.set(e,n),this.notifyStateChange(i),this.log(`[manager] session ${n} created (idle) for ${y.basename(e)}`),{...i}}async startSandbox(s){let e=this.sessions.get(s);if(!e)throw new Error(`No session ${s} to start sandbox for`);if(e.session.status==="running"||e.session.status==="starting"){e.readyPromise&&await e.readyPromise;return}if(e.session.status==="ended")throw new Error(`Session ${s} has ended`);let t=e.session.yamlPath,o=E(y.dirname(t));if(!o)throw new Error(`No Playwright config found for ${t} (searched parents for playwright.config.{ts,js,mjs}).`);e.session.status="starting",this.notifyStateChange(e.session);let n=(async()=>{let a=D+18e4,i,u=new Promise((p,h)=>{i=setTimeout(()=>h(new Error(`Timed out after ${a/1e3}s waiting for inner playwright to register on a port.`)),a)}),l;try{l=await Promise.race([this.spawner({yamlFilePath:t,configPath:o,tempSuffix:s,headed:this.headed}),u])}finally{i&&clearTimeout(i)}e.session.innerPort=l.port,e.session.innerHost=l.host,e.session.pid=l.pid,e.session.status="running",e.cleanup=l.cleanup;let g=p=>{e.session.status!=="ended"&&(e.session.status="ended",e.session.exitInfo=p,this.notifyStateChange(e.session))};e.livenessTimer=this.startLivenessProbe(s,g),this.notifyStateChange(e.session),this.log(`[manager] session ${s} running on ${e.session.innerHost}:${e.session.innerPort} (yaml=${y.basename(t)})`)})();e.readyPromise=n;try{await n}catch(a){throw e.session.status="ended",e.session.exitInfo=a.message,this.notifyStateChange(e.session),a}}async stopSandbox(s){let e=this.sessions.get(s);if(e&&e.session.status!=="idle"&&e.session.status!=="ended"){this.log(`[manager] stopSandbox ${s}`),e.livenessTimer&&(clearInterval(e.livenessTimer),e.livenessTimer=void 0);try{await e.cleanup()}catch(t){this.log(`[manager] stopSandbox ${s} cleanup error: ${t.message}`)}e.session.innerPort=0,e.session.innerHost="127.0.0.1",e.session.pid=0,e.session.status="idle",e.session.exitInfo=null,e.cleanup=async()=>{},e.readyPromise=Promise.resolve(),this.notifyStateChange(e.session)}}startLivenessProbe(s,e){let n=0,a=setInterval(()=>{let i=this.sessions.get(s);if(!i||i.session.status==="ended"){clearInterval(a);return}let u=!1;try{process.kill(i.session.pid,0),u=!0}catch(l){l?.code!=="ESRCH"&&(u=!0)}u?n=0:(n+=1,n>=3&&(clearInterval(a),e("process-exited")))},3e3);return a.unref(),a}async closeSession(s){let e=this.sessions.get(s);if(e){this.log(`[manager] closeSession ${s} (status=${e.session.status})`),this.sessions.delete(s),this.byYamlPath.get(e.session.yamlPath)===s&&this.byYamlPath.delete(e.session.yamlPath),e.livenessTimer&&(clearInterval(e.livenessTimer),e.livenessTimer=void 0),e.session.status!=="ended"&&(e.session.status="ended",e.session.exitInfo="SIGTERM",this.notifyStateChange(e.session));try{await e.cleanup()}catch(t){this.log(`[manager] closeSession ${s} cleanup error: ${t.message}`)}}}async restartInner(s){let e=this.sessions.get(s);if(!e)throw new Error(`No session ${s} to restart`);if(e.restartInProgress)throw new Error(`Restart already in progress for session ${s}`);let t=e.session.yamlPath,o=E(y.dirname(t));if(!o)throw new Error(`No Playwright config found for ${t} (searched parents for playwright.config.{ts,js,mjs}).`);e.restartInProgress=!0;try{e.livenessTimer&&(clearInterval(e.livenessTimer),e.livenessTimer=void 0);try{await e.cleanup()}catch(l){this.log(`[manager] restartInner ${s} old cleanup error: ${l.message}`)}e.session.status="starting",e.session.innerPort=0,e.session.pid=0,this.notifyStateChange(e.session);let n=D+18e4,a,i=new Promise((l,g)=>{a=setTimeout(()=>g(new Error(`Timed out after ${n/1e3}s waiting for inner playwright to respawn.`)),n)}),u;try{u=await Promise.race([this.spawner({yamlFilePath:t,configPath:o,tempSuffix:s,headed:this.headed}),i])}catch(l){throw e.session.status="ended",e.session.exitInfo=l.message,this.notifyStateChange(e.session),l}finally{a&&clearTimeout(a)}e.session.innerPort=u.port,e.session.innerHost=u.host,e.session.pid=u.pid,e.session.status="running",e.cleanup=u.cleanup,e.livenessTimer=this.startLivenessProbe(s,l=>{e.session.status!=="ended"&&(e.session.status="ended",e.session.exitInfo=l,this.notifyStateChange(e.session))}),this.notifyStateChange(e.session),this.log(`[manager] session ${s} restarted on ${e.session.innerHost}:${e.session.innerPort} (yaml=${y.basename(t)})`)}finally{e.restartInProgress=!1}}listSessions(){return Array.from(this.sessions.values()).map(s=>({...s.session}))}getSession(s){let e=this.sessions.get(s);return e?{...e.session}:void 0}httpProxyFor(s,e={}){let{liveviewUrlBuilder:t}=e;return async(o,n,a)=>{let i=this.sessions.get(s);if(!i){n.status(404).json({status:"error",message:"Session not found"});return}if(i.session.status==="ended"){n.status(410).json({status:"error",message:"Session has ended",exitInfo:i.session.exitInfo});return}if(i.session.status==="idle"){n.status(503).json({status:"error",message:"Sandbox not started"});return}let u=`${o.method} ${o.path}`;if(u==="POST /api/int-runner/create-session"){let g=await A(o),p={};if(g.length)try{p=JSON.parse(g.toString("utf-8"))}catch{n.status(400).json({status:"error",message:"Invalid JSON body"});return}p.testFilePath=i.session.yamlPath,await B(o,n,i.session.innerHost,i.session.innerPort,Buffer.from(JSON.stringify(p),"utf-8"),"application/json");return}if(u==="POST /api/int-runner/terminate-session"){try{await this.stopSandbox(s),n.json({status:"success",details:"Sandbox stopped"})}catch(g){n.status(500).json({status:"error",message:g.message})}return}if(u==="POST /api/int-runner/liveview-url"){let g=t?.(o)??"";n.json({liveviewUrl:g,browserWsUrl:""});return}let l=await A(o);await B(o,n,i.session.innerHost,i.session.innerPort,l,o.headers["content-type"])}}wsUpgradeFor(s){return async(e,t,o,n)=>{let a=this.sessions.get(s);if(!a){I(t,`HTTP/1.1 404 Not Found\r
\r
`);return}if(a.session.status==="ended"){I(t,`HTTP/1.1 410 Gone\r
\r
`);return}try{let i=a.session.innerHost.includes(":")?`[${a.session.innerHost}]`:a.session.innerHost,u=await fetch(`http://${i}:${a.session.innerPort}/api/browser-cdp`);if(!u.ok)throw new Error(`Inner /api/browser-cdp returned ${u.status}`);let{cdpUrl:l}=await u.json(),g=new URL(l.replace(/^ws/,"http")),p=parseInt(g.port||"80",10),h=g.hostname,x=g.pathname;n&&n.startsWith("/page/")&&(x=`/devtools${n}`);let c=L.createConnection(p,h);c.on("connect",()=>{let f=`GET ${x} HTTP/1.1\r
Host: ${h}:${p}\r
`;for(let[$,v]of Object.entries(e.headers)){let S=$.toLowerCase();S!=="host"&&S!=="origin"&&(f+=`${$}: ${Array.isArray(v)?v.join(", "):v}\r
`)}f+=`\r
`,c.write(f),o.length&&c.write(o),c.pipe(t),t.pipe(c)}),c.on("error",()=>{(!("destroyed"in t)||!t.destroyed)&&t.destroy()}),t.on("error",()=>{c.destroyed||c.destroy()}),t.on("close",()=>{c.destroyed||c.destroy()})}catch(i){this.log(`[manager] WS upgrade for ${s} failed: ${i.message}`),I(t,`HTTP/1.1 502 Bad Gateway\r
\r
`)}}}async shutdown(){let s=Array.from(this.sessions.keys());await Promise.allSettled(s.map(e=>this.closeSession(e)))}};function A(r){return new Promise((s,e)=>{if(r.body&&typeof r.body=="object")try{s(Buffer.from(JSON.stringify(r.body),"utf-8"));return}catch{}if(r.readableEnded){s(Buffer.alloc(0));return}let t=[];r.on("data",o=>t.push(o)),r.on("end",()=>s(Buffer.concat(t))),r.on("error",e)})}function B(r,s,e,t,o,n){return new Promise(a=>{let i=Array.isArray(n)?n[0]:n,u=O.request({hostname:e,port:t,path:r.originalUrl,method:r.method,headers:{"content-type":i||"application/json","content-length":String(o.length)},timeout:3e5},l=>{s.writeHead(l.statusCode??502,l.headers),l.on("data",g=>s.write(g)),l.on("end",()=>{s.end(),a()}),l.on("error",()=>{s.writableEnded||s.end(),a()})});u.on("error",l=>{s.headersSent?s.writableEnded||s.end():s.status(502).json({status:"error",message:"Inner server unavailable: "+l.message}),a()}),u.end(o)})}function I(r,s){try{(!("destroyed"in r)||!r.destroyed)&&(r.write(s),r.destroy())}catch{}}0&&(module.exports={DebuggerManager});
