shiplightai 0.1.63 → 0.1.65

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.
@@ -50,6 +50,19 @@ interface ServerRoutesOptions {
50
50
  * `ws://host/ws/debugger/:sessionId/cdp-browser`.
51
51
  */
52
52
  liveviewUrlBuilder?: (sessionId: string, req: Request) => string;
53
+ /**
54
+ * Fallback router for idle sessions (no sandbox process). When the SPA
55
+ * makes file-related API calls (`/api/test-flow`, `/api/files`, etc.)
56
+ * through the session proxy but the sandbox isn't running, the proxy
57
+ * delegates to this router instead of forwarding to the inner process.
58
+ */
59
+ fallbackRouter?: Router;
60
+ /**
61
+ * Absolute path to the artifacts directory (screenshots, videos).
62
+ * When set, `/api/report-assets/*` requests inside a session are served
63
+ * from this directory instead of being proxied to the inner process.
64
+ */
65
+ artifactsDir?: string;
53
66
  }
54
67
  declare function createDebuggerHttpRoutes(opts: ServerRoutesOptions): Router;
55
68
  /**
@@ -1,3 +1,3 @@
1
- import{Router as y}from"express";import b from"express";import*as g from"fs";import*as d from"path";function w(o){return o?d.isAbsolute(o)?d.normalize(o):d.resolve(o):null}function x(o,t){let u=t.headers.host??"127.0.0.1";return`${(t.headers["x-forwarded-proto"]??"ws")==="https"?"wss":"ws"}://${u}/ws/debugger/${o}/cdp-browser`}function v(o){let t=d.basename(o);return t.endsWith(".test.yaml")||t.endsWith(".test.yml")}function P(o){let{manager:t,staticDir:u,resolveYamlPath:p=w,liveviewUrlBuilder:c=x}=o,a=y();a.post("/api/debugger/sessions",b.json(),async(e,s)=>{let r=e.body?.yamlPath;if(typeof r!="string"||!r){s.status(400).json({error:"yamlPath is required"});return}let i=p(r);if(!i){s.status(403).json({error:"Path outside allowed roots"});return}if(!v(i)){s.status(400).json({error:"Not a Shiplight test file (expected *.test.yaml or *.test.yml)"});return}if(!g.existsSync(i)){s.status(404).json({error:"File not found"});return}let l=t.listSessions().find(n=>n.yamlPath===i&&n.status!=="ended");try{let n=await t.openSession(i);s.status(l?200:201).json({sessionId:n.sessionId,yamlPath:n.yamlPath,startedAt:n.startedAt,status:n.status})}catch(n){s.status(500).json({error:n.message})}}),a.get("/api/debugger/sessions",(e,s)=>{s.setHeader("Cache-Control","no-store"),s.json({sessions:t.listSessions().map(r=>({sessionId:r.sessionId,yamlPath:r.yamlPath,startedAt:r.startedAt,status:r.status}))})}),a.delete("/api/debugger/sessions/:sessionId",async(e,s)=>{let r=e.params.sessionId,i=!!t.getSession(r);await t.closeSession(r),s.json({deleted:!0,alreadyGone:!i})}),g.existsSync(u)?a.use("/debugger/static",b.static(u)):console.error(`[debugger] WARNING: debugger static dir missing at ${u} \u2014 iframe routes will 404`),a.get("/debugger/:sessionId/",(e,s)=>{let r=e.params.sessionId;if(!t.getSession(r)){s.status(404).send("Debugger session not found");return}let l=d.join(u,"index.html");if(!g.existsSync(l)){s.status(500).send(`Debugger SPA bundle missing at ${l}`);return}let f=g.readFileSync(l,"utf-8").replace(/(src|href)="\/assets\//g,'$1="/debugger/static/assets/').replace(/(src|href)="\/index\.html/g,'$1="/debugger/static/index.html');s.type("html").send(f)});let m=(e,s,r)=>{let i=e.params.sessionId,l=e.originalUrl,n=`/debugger/${i}`;if(l.startsWith(n)){let h=l.slice(n.length)||"/";e.url=h,Object.defineProperty(e,"originalUrl",{value:h,configurable:!0})}t.httpProxyFor(i,{liveviewUrlBuilder:()=>c(i,e)})(e,s,r)};return a.use("/debugger/:sessionId",m),a}function R(o,t,u,p){let a=(t.url??"").match(/^\/ws\/debugger\/([^/]+)(.*)$/);if(!a){u.write(`HTTP/1.1 404 Not Found\r
1
+ import{Router as R}from"express";import y from"express";import*as c from"fs";import*as l from"path";function P(i){return i?l.isAbsolute(i)?l.normalize(i):l.resolve(i):null}function S(i,t){let u=t.headers.host??"127.0.0.1";return`${(t.headers["x-forwarded-proto"]??"ws")==="https"?"wss":"ws"}://${u}/ws/debugger/${i}/cdp-browser`}function j(i){let t=l.basename(i);return t.endsWith(".test.yaml")||t.endsWith(".test.yml")}function I(i){let{manager:t,staticDir:u,resolveYamlPath:f=P,liveviewUrlBuilder:b=S,fallbackRouter:g,artifactsDir:m}=i,n=R(),w=m?y.static(m):null;n.post("/api/debugger/sessions",y.json(),async(e,s)=>{let r=e.body?.yamlPath;if(typeof r!="string"||!r){s.status(400).json({error:"yamlPath is required"});return}let o=f(r);if(!o){s.status(403).json({error:"Path outside allowed roots"});return}if(!j(o)){s.status(400).json({error:"Not a Shiplight test file (expected *.test.yaml or *.test.yml)"});return}if(!c.existsSync(o)){s.status(404).json({error:"File not found"});return}let d=t.listSessions().find(a=>a.yamlPath===o&&a.status!=="ended");try{let a=t.openSession(o);s.status(d?200:201).json({sessionId:a.sessionId,yamlPath:a.yamlPath,startedAt:a.startedAt,status:a.status})}catch(a){s.status(500).json({error:a.message})}}),n.get("/api/debugger/sessions",(e,s)=>{s.setHeader("Cache-Control","no-store"),s.json({sessions:t.listSessions().map(r=>({sessionId:r.sessionId,yamlPath:r.yamlPath,startedAt:r.startedAt,status:r.status}))})}),n.delete("/api/debugger/sessions/:sessionId",async(e,s)=>{let r=e.params.sessionId,o=!!t.getSession(r);await t.closeSession(r),s.json({deleted:!0,alreadyGone:!o})}),c.existsSync(u)?n.use("/debugger/static",y.static(u)):console.error(`[debugger] WARNING: debugger static dir missing at ${u} \u2014 iframe routes will 404`),n.get("/debugger/:sessionId/",(e,s)=>{let r=e.params.sessionId;if(!t.getSession(r)){s.status(404).send("Debugger session not found");return}let d=l.join(u,"index.html");if(!c.existsSync(d)){s.status(500).send(`Debugger SPA bundle missing at ${d}`);return}let h=c.readFileSync(d,"utf-8").replace(/(src|href)="\/assets\//g,'$1="/debugger/static/assets/').replace(/(src|href)="\/index\.html/g,'$1="/debugger/static/index.html');s.type("html").send(h)});let x=async(e,s,r)=>{let o=e.params.sessionId;if(o==="static")return r();let d=t.getSession(o);if(!d){s.status(404).json({status:"error",message:"Session not found"});return}let a=e.originalUrl,h=`/debugger/${o}`;if(a.startsWith(h)){let p=a.slice(h.length)||"/";e.url=p,Object.defineProperty(e,"originalUrl",{value:p,configurable:!0})}if(w&&e.path.startsWith("/api/report-assets/")){e.url=e.url.replace(/^\/api\/report-assets/,""),w(e,s,r);return}if(d.status==="idle"||d.status==="starting"){let p=`${e.method} ${e.path}`;if(p==="POST /api/int-runner/create-session")try{await t.startSandbox(o)}catch(v){s.status(500).json({status:"error",message:v.message});return}else if(p==="POST /api/int-runner/liveview-url"){s.json({liveviewUrl:"",browserWsUrl:""});return}else if(e.path.startsWith("/api/int-runner/")){s.status(503).json({status:"error",message:"Sandbox not started"});return}else if(g){g(e,s,r);return}else{s.status(503).json({status:"error",message:"Sandbox not started"});return}}t.httpProxyFor(o,{liveviewUrlBuilder:()=>b(o,e)})(e,s,r)};return n.use("/debugger/:sessionId",x),n}function D(i,t,u,f){let g=(t.url??"").match(/^\/ws\/debugger\/([^/]+)(.*)$/);if(!g){u.write(`HTTP/1.1 404 Not Found\r
2
2
  \r
3
- `),u.destroy();return}let m=a[1],e=a[2]||"";e.startsWith("/cdp-browser/page/")?e=e.slice(12):(e==="/cdp-browser"||e==="/cdp-browser/")&&(e=""),o.wsUpgradeFor(m)(t,u,p,e||void 0)}export{P as createDebuggerHttpRoutes,R as handleDebuggerUpgrade};
3
+ `),u.destroy();return}let m=g[1],n=g[2]||"";n.startsWith("/cdp-browser/page/")?n=n.slice(12):(n==="/cdp-browser"||n==="/cdp-browser/")&&(n=""),i.wsUpgradeFor(m)(t,u,f,n||void 0)}export{I as createDebuggerHttpRoutes,D as handleDebuggerUpgrade};