"use strict";var F=Object.create;var $=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var J=Object.getPrototypeOf,Y=Object.prototype.hasOwnProperty;var W=(r,s)=>{for(var e in s)$(r,e,{get:s[e],enumerable:!0})},_=(r,s,e,t)=>{if(s&&typeof s=="object"||typeof s=="function")for(let o of G(s))!Y.call(r,o)&&o!==e&&$(r,o,{get:()=>s[o],enumerable:!(t=k(s,o))||t.enumerable});return r};var S=(r,s,e)=>(e=r!=null?F(J(r)):{},_(s||!r||!r.__esModule?$(e,"default",{value:r,enumerable:!0}):e,r)),V=r=>_($({},"__esModule",{value:!0}),r);var X={};W(X,{DebuggerManager:()=>I});module.exports=V(X);var O=S(require("http"),1),L=S(require("net"),1),m=S(require("path"),1),U=S(require("fs"),1),M=require("crypto");var y=S(require("fs"),1),h=S(require("path"),1),N=require("child_process"),C=require("yaml");function K(r){let s=!1;return()=>{if(!s){s=!0;try{y.unlinkSync(r)}catch{}}}}function E(r){let s=["playwright.config.ts","playwright.config.js","playwright.config.mjs"],e=h.resolve(r);for(;;){for(let o of s){let n=h.join(e,o);if(y.existsSync(n))return n}let t=h.dirname(e);if(t===e)break;e=t}return null}function z(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=h.dirname(e),a=await Q(16174),i;if(!y.existsSync(s))throw new Error(`Please select a test file before starting the debug session. File not found: ${s}`);try{let u=(0,C.parse)(y.readFileSync(s,"utf-8"));u?.use&&typeof u.use=="object"&&!Array.isArray(u.use)&&(i=u.use),u?.base_url&&!i?.baseURL&&(i={...i,baseURL:u.base_url})}catch(u){console.error("[debugger] Could not parse YAML for `use` block:",u)}let d=h.dirname(h.resolve(s)),l=t?`-${t}`:"",c=h.join(d,`.__shiplight_debug__${l}.yaml.spec.ts`),p=z(s,a,i,n);y.writeFileSync(c,p);let w=K(c),T=["playwright","test",c,...o?["--headed"]:[]],g=(0,N.spawn)("npx",T,{stdio:["ignore","pipe","pipe"],shell:!0,cwd:n,env:{...process.env,PWDEBUG:"console",SHIPLIGHT_REGISTRY_URL:""}});g.stdout?.on("data",u=>{process.stderr.write(u)}),g.stderr?.on("data",u=>{process.stderr.write(u)});let f=()=>{g.killed||g.kill("SIGTERM")};process.on("SIGTERM",f),process.on("SIGINT",f),process.on("exit",f),g.on("close",u=>{process.removeListener("SIGTERM",f),process.removeListener("SIGINT",f),process.removeListener("exit",f),w(),u!==0&&u!==null&&console.error(`[debugger] Playwright process exited with code ${u}`)}),console.error("[debugger] Waiting for Playwright sandbox to start...");let x=["127.0.0.1","::1"];async function v(u){try{let b=u.includes(":")?`[${u}]`:u,H=await fetch(`http://${b}:${a}/api/test-flow`);if(H.ok){try{await H.text()}catch{}return!0}}catch{}return!1}let P=null;for(let u=0;u<180;u++){if(g.exitCode!==null)throw w(),new Error(`Playwright process exited with code ${g.exitCode} before sandbox was ready`);for(let b of x)if(await v(b)){P=b;break}if(P){console.error(`[debugger] Playwright sandbox ready on ${P}:${a}`);break}if(u===179)throw f(),w(),new Error("Timed out waiting for Playwright sandbox to start (180s)");await new Promise(b=>setTimeout(b,1e3))}if(!P)throw f(),w(),new Error("Sandbox poll finished without a reachable host");return{port:a,host:P,pid:g.pid??0,cleanup:async()=>{f(),w()}}}var D=1e4,I=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)}resetToIdle(s,e){s.session.innerPort=0,s.session.innerHost="127.0.0.1",s.session.pid=0,s.session.status="idle",s.session.exitInfo=e,s.cleanup=async()=>{},s.readyPromise=Promise.resolve(),this.notifyStateChange(s.session)}notifyStateChange(s){try{this.options.onSessionStateChange?.({...s})}catch(e){this.log(`[manager] onSessionStateChange listener threw: ${e.message}`)}}openSession(s){let e=m.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(m.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},d={session:i,cleanup:async()=>{},readyPromise:Promise.resolve()};return this.sessions.set(n,d),this.byYamlPath.set(e,n),this.notifyStateChange(i),this.log(`[manager] session ${n} created (idle) for ${m.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(m.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,d=new Promise((c,p)=>{i=setTimeout(()=>p(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}),d])}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,e.livenessTimer=this.startLivenessProbe(s,c=>{(e.session.status==="running"||e.session.status==="starting")&&this.resetToIdle(e,c)}),this.notifyStateChange(e.session),this.log(`[manager] session ${s} running on ${e.session.innerHost}:${e.session.innerPort} (yaml=${m.basename(t)})`)})();e.readyPromise=n;try{await n}catch(a){throw this.resetToIdle(e,a.message),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}`)}this.resetToIdle(e,null)}}startLivenessProbe(s,e){let n=0,a=setInterval(()=>{let i=this.sessions.get(s);if(!i||i.session.status==="ended"||i.session.status==="idle"){clearInterval(a);return}let d=!1;try{process.kill(i.session.pid,0),d=!0}catch(l){l?.code!=="ESRCH"&&(d=!0)}d?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(m.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,c)=>{a=setTimeout(()=>c(new Error(`Timed out after ${n/1e3}s waiting for inner playwright to respawn.`)),n)}),d;try{d=await Promise.race([this.spawner({yamlFilePath:t,configPath:o,tempSuffix:s,headed:this.headed}),i])}catch(l){throw this.resetToIdle(e,l.message),l}finally{a&&clearTimeout(a)}e.session.innerPort=d.port,e.session.innerHost=d.host,e.session.pid=d.pid,e.session.status="running",e.cleanup=d.cleanup,e.livenessTimer=this.startLivenessProbe(s,l=>{(e.session.status==="running"||e.session.status==="starting")&&this.resetToIdle(e,l)}),this.notifyStateChange(e.session),this.log(`[manager] session ${s} restarted on ${e.session.innerHost}:${e.session.innerPort} (yaml=${m.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 d=`${o.method} ${o.path}`;if(d==="POST /api/int-runner/create-session"){let c=await A(o),p={};if(c.length)try{p=JSON.parse(c.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(d==="POST /api/int-runner/terminate-session"){try{await this.stopSandbox(s),n.json({status:"success",details:"Sandbox stopped"})}catch(c){n.status(500).json({status:"error",message:c.message})}return}if(d==="POST /api/int-runner/liveview-url"){let c=t?.(o)??"";n.json({liveviewUrl:c,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){R(t,`HTTP/1.1 404 Not Found\r
\r
`);return}if(a.session.status==="ended"){R(t,`HTTP/1.1 410 Gone\r
\r
`);return}try{let i=a.session.innerHost.includes(":")?`[${a.session.innerHost}]`:a.session.innerHost,d=await fetch(`http://${i}:${a.session.innerPort}/api/browser-cdp`);if(!d.ok)throw new Error(`Inner /api/browser-cdp returned ${d.status}`);let{cdpUrl:l}=await d.json(),c=new URL(l.replace(/^ws/,"http")),p=parseInt(c.port||"80",10),w=c.hostname,T=c.pathname;n&&n.startsWith("/page/")&&(T=`/devtools${n}`);let g=L.createConnection(p,w);g.on("connect",()=>{let f=`GET ${T} HTTP/1.1\r
Host: ${w}:${p}\r
`;for(let[x,v]of Object.entries(e.headers)){let P=x.toLowerCase();P!=="host"&&P!=="origin"&&(f+=`${x}: ${Array.isArray(v)?v.join(", "):v}\r
`)}f+=`\r
`,g.write(f),o.length&&g.write(o),g.pipe(t),t.pipe(g)}),g.on("error",()=>{(!("destroyed"in t)||!t.destroyed)&&t.destroy()}),t.on("error",()=>{g.destroyed||g.destroy()}),t.on("close",()=>{g.destroyed||g.destroy()})}catch(i){this.log(`[manager] WS upgrade for ${s} failed: ${i.message}`),R(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,d=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",c=>s.write(c)),l.on("end",()=>{s.end(),a()}),l.on("error",()=>{s.writableEnded||s.end(),a()})});d.on("error",l=>{s.headersSent?s.writableEnded||s.end():s.status(502).json({status:"error",message:"Inner server unavailable: "+l.message}),a()}),d.end(o)})}function R(r,s){try{(!("destroyed"in r)||!r.destroyed)&&(r.write(s),r.destroy())}catch{}}0&&(module.exports={DebuggerManager});
