@zibby/cli 0.1.59 → 0.1.61

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 (40) hide show
  1. package/dist/auth/cli-login.js +12 -12
  2. package/dist/bin/zibby.js +2 -2
  3. package/dist/commands/agent-reliability.js +4 -4
  4. package/dist/commands/analyze-graph.js +14 -14
  5. package/dist/commands/chat-session-store.js +1 -1
  6. package/dist/commands/chat.js +71 -62
  7. package/dist/commands/ci-setup.js +3 -3
  8. package/dist/commands/generate.js +60 -21
  9. package/dist/commands/implement.js +15 -15
  10. package/dist/commands/init.js +87 -67
  11. package/dist/commands/list-projects.js +9 -9
  12. package/dist/commands/memory.js +13 -13
  13. package/dist/commands/project.js +15 -0
  14. package/dist/commands/run-capacity-queue-cli.js +2 -2
  15. package/dist/commands/run.js +52 -50
  16. package/dist/commands/setup-scripts.js +6 -6
  17. package/dist/commands/studio.js +21 -15
  18. package/dist/commands/uninstall.js +10 -10
  19. package/dist/commands/upload.js +15 -15
  20. package/dist/commands/video.js +1 -1
  21. package/dist/commands/workflow.js +36 -34
  22. package/dist/commands/workflows/deploy.js +14 -24
  23. package/dist/commands/workflows/generate.js +15 -14
  24. package/dist/commands/workflows/list.js +19 -0
  25. package/dist/commands/workflows/logs.js +28 -19
  26. package/dist/commands/workflows/run.js +7 -7
  27. package/dist/commands/workflows/start.js +6 -6
  28. package/dist/commands/workflows/trigger.js +10 -0
  29. package/dist/config/config-loader.js +1 -0
  30. package/dist/config/config.js +1 -1
  31. package/dist/config/environments.js +1 -1
  32. package/dist/package.json +4 -4
  33. package/dist/utils/agent-credentials.js +3 -3
  34. package/dist/utils/chat-run-lifecycle.js +3 -3
  35. package/dist/utils/execution-context.js +1 -1
  36. package/dist/utils/progress-reporter.js +1 -1
  37. package/dist/utils/studio-cli-log-mirror.js +1 -1
  38. package/dist/utils/studio-installer.js +5 -5
  39. package/dist/utils/studio-launcher.js +1 -1
  40. package/package.json +1 -1
@@ -1,27 +1,33 @@
1
1
  #!/usr/bin/env node
2
- import bt from"express";import{createServer as Kt}from"http";import{WebSocketServer as Yt}from"ws";import{spawn as st,execSync as Y}from"child_process";import{readFileSync as _,existsSync as u,readdirSync as z,statSync as L,createReadStream as Ht,createWriteStream as Zt,mkdirSync as jt,openSync as qt,readSync as Vt,closeSync as Qt,writeFileSync as H,unlinkSync as Xt}from"fs";import{join as a,resolve as E,dirname as ot,basename as te}from"path";import{homedir as ee}from"os";import{inspect as se}from"util";import{fileURLToPath as oe,pathToFileURL as Pt}from"url";import ne from"dotenv";import{promptAndInstallStudio as re,isStudioInstalled as vt}from"../utils/studio-installer.js";import{launchStudio as ie}from"../utils/studio-launcher.js";import{mergeSessionRunState as G,listRunningSessionStatesFromSessionsRoot as ce}from"@zibby/core/utils/run-state-session.js";import{liveRunsFromSessionStateRows as ae}from"@zibby/core/utils/session-state-live-runs.js";import{findLatestLiveFrameFileSync as wt,readLatestLiveFramePayloadSync as Tt}from"@zibby/core/utils/live-frame-discovery.js";import{getApiUrl as Nt}from"../config/environments.js";const le=oe(import.meta.url),ue=ot(le);function At(v){const m=a(v,"video.webm");if(u(m))return m;const y=a(v,"execute_live");if(!u(y))return null;try{const k=z(y).filter(S=>S.endsWith(".webm"));return k.length===0?null:k.map(S=>{const w=a(y,S);try{return{p:w,mtime:L(w).mtimeMs}}catch{return{p:w,mtime:0}}}).sort((S,w)=>w.mtime-S.mtime)[0].p}catch{return null}}function _t(v){if(process.platform==="win32")try{const m=Y("netstat -ano",{encoding:"utf8"}),y=new Set;for(const k of m.split(`
3
- `)){if(!k.includes("LISTENING"))continue;const O=k.trim().split(/\s+/);if(!(O[1]||"").endsWith(`:${v}`))continue;const w=O[O.length-1];/^\d+$/.test(w)&&y.add(parseInt(w,10))}return[...y].filter(k=>k!==process.pid)}catch{return[]}try{const m=Y(`lsof -tiTCP:${v} -sTCP:LISTEN`,{encoding:"utf8"}).trim();return[...new Set(m.split(`
4
- `).filter(Boolean))].map(y=>parseInt(y,10)).filter(y=>!Number.isNaN(y)&&y!==process.pid)}catch{return[]}}async function kt(v){let m=_t(v);if(m.length!==0){console.log(`[Studio] Port ${v} in use \u2014 stopping previous Studio listener(s): ${m.join(", ")}`);for(const y of m)try{process.kill(y,"SIGTERM")}catch{}await new Promise(y=>setTimeout(y,450)),m=_t(v);for(const y of m)try{process.kill(y,"SIGKILL")}catch{}await new Promise(y=>setTimeout(y,200))}}async function ke(v={}){const m=bt(),y=Kt(m),k=new Yt({server:y}),O=v.port||3847,S=process.cwd(),w=ee();process.env.DOTENV_CONFIG_QUIET="true";const Ot=process.env.NODE_ENV||"development";[E(S,".env.local"),E(S,`.env.${Ot}`),E(S,".env")].forEach(s=>{u(s)&&ne.config({path:s,override:!1})});const x=new Map,D=new Map;let Et=null;const U=96e3,nt="studio-cli.log",J="studio-run.json",Rt=".zibby-studio-stop",Z=512*1024;m.use(bt.json()),m.get("/api/session-run-states",(s,t)=>{try{let e=R();try{const r=new URL(s.url,"http://zibby.studio").searchParams.get("sessionsRoot");if(r&&String(r).trim()){const c=E(decodeURIComponent(String(r).trim()));u(c)&&L(c).isDirectory()&&(e=c)}}catch{}const n=ce(e),{liveIdList:o,progressByKey:i}=ae(n);t.json({rows:n,liveIdList:o,progressByKey:i,sessionsRoot:e,unavailable:!1})}catch(e){t.status(500).json({error:e.message,rows:[],liveIdList:[],progressByKey:{},unavailable:!0})}}),m.get("/api/workflow/graph",async(s,t)=>{try{let e=null;const n=a(S,".zibby","graph.mjs");if(u(n))try{const o=await import(Pt(n).href),i=o.BrowserTestAutomationAgent||o.default;if(i&&typeof i=="function"){const r=new i().buildGraph();r&&typeof r.serialize=="function"&&(e=r.serialize())}}catch{}if(!e)try{const{createRequire:o}=await import("module"),i=o(import.meta.url),l=a(ot(i.resolve("@zibby/core/package.json")),"templates","browser-test-automation","run_test.json");u(l)&&(e=JSON.parse(_(l,"utf-8")))}catch{}t.json({graph:e})}catch{t.json({graph:null})}}),m.use((s,t,e)=>(t.header("Access-Control-Allow-Origin","*"),t.header("Access-Control-Allow-Methods","GET, POST, PUT, DELETE"),t.header("Access-Control-Allow-Headers","Content-Type"),e()));const q=a(ue,"../../../../studio"),rt=u(a(q,"package.json"));function xt(){const s=a(q,"node_modules",".bin",process.platform==="win32"?"electron.cmd":"electron"),t=u(s),o=st(t?s:"npx",t?["."]:["electron","."],{cwd:q,detached:!0,stdio:"ignore",shell:!1,env:{...process.env,ZIBBY_STUDIO_PROJECT_ROOT:S,ZIBBY_STUDIO_API_BASE:`http://localhost:${O}/api`}});o.unref(),Et=o}async function Ct(){if(rt){xt();return}if(v.update||!vt()){if(v.update&&vt()){console.log(`
2
+ var fe=Object.defineProperty;var me=(a,r)=>()=>(a&&(r=a(a=0)),r);var ge=(a,r)=>{for(var p in r)fe(a,p,{get:r[p],enumerable:!0})};var Ct={};ge(Ct,{getStudioAppPath:()=>ot,getStudioVersion:()=>Ae,installStudio:()=>Lt,isStudioInstalled:()=>et,promptAndInstallStudio:()=>ft});import U from"os";import $ from"path";import{existsSync as tt,readFileSync as he,readdirSync as ye,createWriteStream as Se,mkdirSync as be,rmSync as Ie,unlinkSync as we}from"fs";import{execFileSync as Ut}from"child_process";import{createInterface as ve}from"readline";import C from"chalk";function Bt(){let a=U.platform(),r=U.arch();if(a==="darwin"&&r==="arm64")return{archive:"Zibby Studio-mac-arm64.zip",label:"macOS (Apple Silicon)"};if(a==="darwin")return{archive:"Zibby Studio-mac-x64.zip",label:"macOS (Intel)"};if(a==="win32"&&r==="arm64")return{archive:"Zibby Studio-win-arm64.zip",label:"Windows (ARM)"};if(a==="win32")return{archive:"Zibby Studio-win-x64.zip",label:"Windows (x64)"};if(a==="linux"&&r==="arm64")return{archive:"Zibby Studio-arm64.AppImage",label:"Linux (ARM64)"};if(a==="linux")return{archive:"Zibby Studio-1.0.0.AppImage",label:"Linux (x64)"};throw new Error(`Unsupported platform: ${a} ${r}`)}function je(){let{archive:a}=Bt();return`${Pe}/download/latest/${encodeURIComponent(a)}`}function _e(a){let r=ve({input:process.stdin,output:process.stdout});return new Promise(p=>{r.question(a,S=>{r.close(),p(S.trim().toLowerCase())})})}async function ft(){let{label:a}=Bt();console.log(""),console.log(C.cyan(" Zibby Studio is not installed.")),console.log(C.gray(` Platform: ${a}`)),console.log("");let r=await _e(C.white(" Download and install Zibby Studio? (Y/n): "));return r&&r!=="y"&&r!=="yes"?(console.log(C.yellow(`
3
+ Skipped.
4
+ `)),!1):(await Lt(),!0)}async function Lt(){let a=je(),r=decodeURIComponent($.basename(a));console.log(C.cyan(`
5
+ \u{1F4E6} Downloading Zibby Studio...`));try{let p=await fetch(a);if(!p.ok)throw new Error(`Download failed: ${p.status} ${p.statusText}`);let S=parseInt(p.headers.get("content-length")||"0",10),w=$.join(U.tmpdir(),r),h=0,_=Se(w);for await(let E of p.body)if(h+=E.length,_.write(E),S>0){let F=(h/S*100).toFixed(1),k=(h/1024/1024).toFixed(1),Y=(S/1024/1024).toFixed(1);process.stdout.write(`\r \u{1F4E5} ${F}% (${k}MB / ${Y}MB)`)}if(await new Promise((E,F)=>{_.on("finish",E),_.on("error",F),_.end()}),console.log(""),tt(O)&&Ie(O,{recursive:!0,force:!0}),be(O,{recursive:!0}),console.log(C.cyan(" \u{1F4C2} Installing...")),w.endsWith(".zip"))if(U.platform()==="darwin"||U.platform()==="linux"){if(Ut("unzip",["-oq",w,"-d",O]),U.platform()==="darwin")try{Ut("xattr",["-cr",O])}catch{}}else{let E=(await import("adm-zip")).default;new E(w).extractAllTo(O,!0)}else if(w.endsWith(".AppImage")){let{copyFileSync:E,chmodSync:F}=await import("fs"),k=$.join(O,r);E(w,k),F(k,493)}try{we(w)}catch{}return console.log(C.green(` \u2705 Zibby Studio installed!
6
+ `)),!0}catch(p){throw console.log(C.red(`
7
+ \u274C Installation failed: ${p.message}
8
+ `)),p}}function et(){let a=ot();return!!(a&&tt(a))}function ot(){if(U.platform()==="darwin")return $.join(O,"Zibby Studio.app");if(U.platform()==="win32")return $.join(O,"Zibby Studio.exe");if(U.platform()==="linux"){if(!tt(O))return null;let a=ye(O).find(r=>r.endsWith(".AppImage"));return a?$.join(O,a):null}return null}function Ae(){let a=$.join(O,"version.txt");return tt(a)?he(a,"utf-8").trim():"unknown"}var Pe,O,nt=me(()=>{Pe=process.env.ZIBBY_STUDIO_CDN||"https://dl.zibby.app",O=$.join(U.homedir(),".zibby","studio")});nt();import Gt from"express";import{createServer as ke}from"http";import{WebSocketServer as Ee}from"ws";import{spawn as ht,execSync as rt}from"child_process";import{readFileSync as x,existsSync as f,readdirSync as W,statSync as Z,createReadStream as Ue,createWriteStream as Be,mkdirSync as Wt,openSync as Le,readSync as Ce,closeSync as $e,writeFileSync as st,unlinkSync as Fe}from"fs";import{join as u,resolve as B,dirname as yt,basename as ze}from"path";import{homedir as De}from"os";import{inspect as Ze}from"util";import{fileURLToPath as Ye,pathToFileURL as Mt}from"url";import Ge from"dotenv";nt();import{spawn as Dt,execFileSync as $t}from"child_process";import mt from"os";import Zt from"path";import{existsSync as Ne,mkdirSync as Te,writeFileSync as Re}from"fs";var Ft=Zt.join(mt.homedir(),".zibby","studio-launch.json");function Oe(a){let r={};a.projectRoot&&(r.projectRoot=a.projectRoot),a.port&&(r.port=a.port),r.launchedAt=Date.now();let p=Zt.dirname(Ft);Te(p,{recursive:!0}),Re(Ft,JSON.stringify(r,null,2))}function xe(a){if(mt.platform()==="darwin"){try{$t("xattr",["-cr",a])}catch{}try{$t("spctl",["--add","--label","ZibbyStudio",a])}catch{}}}function zt(a,r){return new Promise(p=>{let S=Dt("open",["-a",a,"--env",`ZIBBY_STUDIO_PROJECT_ROOT=${r.projectRoot}`],{stdio:["ignore","pipe","pipe"]}),w="";S.stderr?.on("data",h=>{w+=h.toString()}),S.on("close",h=>p({code:h,stderr:w})),S.on("error",h=>p({code:1,stderr:h.message}))})}async function Yt(a={}){let r=ot();if(!r)throw new Error("Studio not installed");if(!Ne(r))throw new Error(`Studio app not found at ${r}`);Oe(a);let p=a.projectRoot||process.cwd();if(mt.platform()==="darwin"){let S=await zt(r,{projectRoot:p});if(S.code!==0&&S.stderr.includes("Launch failed")&&(xe(r),S=await zt(r,{projectRoot:p})),S.code!==0){let w=S.stderr.trim();w&&console.error(`[Studio Desktop] ${w}`),console.error(`Zibby Studio exited with code ${S.code}`)}}else{let S=Dt(r,[],{detached:!0,stdio:["ignore","pipe","pipe"],env:{...process.env,ZIBBY_STUDIO_PROJECT_ROOT:p}});S.unref(),S.stderr?.on("data",w=>{let h=w.toString().trim();h&&console.error(`[Studio Desktop] ${h}`)}),S.on("error",w=>{console.error(`Failed to open Zibby Studio: ${w.message}`)})}}import{mergeSessionRunState as K,listRunningSessionStatesFromSessionsRoot as We}from"@zibby/core/utils/run-state-session.js";import{liveRunsFromSessionStateRows as Me}from"@zibby/core/utils/session-state-live-runs.js";import{findLatestLiveFrameFileSync as Jt,readLatestLiveFramePayloadSync as Kt}from"@zibby/core/utils/live-frame-discovery.js";var J={local:{name:"Local Development",apiUrl:"http://localhost:3001",accountApiUrl:"http://localhost:3001",frontendUrl:"http://localhost:3000",description:"Local backend running on port 3001"},prod:{name:"Production",apiUrl:process.env.ZIBBY_PROD_API_URL||"https://api-prod.zibby.app",accountApiUrl:process.env.ZIBBY_PROD_ACCOUNT_API_URL||"https://account-api-prod.zibby.app",frontendUrl:process.env.ZIBBY_PROD_FRONTEND_URL||"https://studio.zibby.app",description:"Production environment"}};function gt(){let a;if(process.env.ZIBBY_API_URL)a=process.env.ZIBBY_API_URL;else{let r=process.env.ZIBBY_ENV||"prod";J[r]?a=J[r].apiUrl:a=J.prod.apiUrl}try{let r=new URL(a);return r.protocol!=="http:"&&r.protocol!=="https:"?(console.error(`\u26A0\uFE0F Invalid API URL protocol: ${r.protocol} (only http/https allowed)`),J.prod.apiUrl):a}catch{return console.error(`\u26A0\uFE0F Invalid API URL: ${a}`),J.prod.apiUrl}}var Je=Ye(import.meta.url),Ke=yt(Je);function Ht(a){let r=u(a,"video.webm");if(f(r))return r;let p=u(a,"execute_live");if(!f(p))return null;try{let S=W(p).filter(h=>h.endsWith(".webm"));return S.length===0?null:S.map(h=>{let _=u(p,h);try{return{p:_,mtime:Z(_).mtimeMs}}catch{return{p:_,mtime:0}}}).sort((h,_)=>_.mtime-h.mtime)[0].p}catch{return null}}function Vt(a){if(process.platform==="win32")try{let r=rt("netstat -ano",{encoding:"utf8"}),p=new Set;for(let S of r.split(`
9
+ `)){if(!S.includes("LISTENING"))continue;let w=S.trim().split(/\s+/);if(!(w[1]||"").endsWith(`:${a}`))continue;let _=w[w.length-1];/^\d+$/.test(_)&&p.add(parseInt(_,10))}return[...p].filter(S=>S!==process.pid)}catch{return[]}try{let r=rt(`lsof -tiTCP:${a} -sTCP:LISTEN`,{encoding:"utf8"}).trim();return[...new Set(r.split(`
10
+ `).filter(Boolean))].map(p=>parseInt(p,10)).filter(p=>!Number.isNaN(p)&&p!==process.pid)}catch{return[]}}async function qt(a){let r=Vt(a);if(r.length!==0){console.log(`[Studio] Port ${a} in use \u2014 stopping previous Studio listener(s): ${r.join(", ")}`);for(let p of r)try{process.kill(p,"SIGTERM")}catch{}await new Promise(p=>setTimeout(p,450)),r=Vt(a);for(let p of r)try{process.kill(p,"SIGKILL")}catch{}await new Promise(p=>setTimeout(p,200))}}async function No(a={}){let r=Gt(),p=ke(r),S=new Ee({server:p}),w=a.port||3847,h=process.cwd(),_=De();process.env.DOTENV_CONFIG_QUIET="true";let E=process.env.NODE_ENV||"development";[B(h,".env.local"),B(h,`.env.${E}`),B(h,".env")].forEach(o=>{f(o)&&Ge.config({path:o,override:!1})});let k=new Map,Y=new Map,Qt=null,H=96e3,St="studio-cli.log",V="studio-run.json",Xt=".zibby-studio-stop",it=512*1024;r.use(Gt.json()),r.get("/api/session-run-states",(o,t)=>{try{let e=L();try{let i=new URL(o.url,"http://zibby.studio").searchParams.get("sessionsRoot");if(i&&String(i).trim()){let l=B(decodeURIComponent(String(i).trim()));f(l)&&Z(l).isDirectory()&&(e=l)}}catch{}let s=We(e),{liveIdList:n,progressByKey:c}=Me(s);t.json({rows:s,liveIdList:n,progressByKey:c,sessionsRoot:e,unavailable:!1})}catch(e){t.status(500).json({error:e.message,rows:[],liveIdList:[],progressByKey:{},unavailable:!0})}}),r.get("/api/workflow/graph",async(o,t)=>{try{let e=null,s=u(h,".zibby","graph.mjs");if(f(s))try{let n=await import(Mt(s).href),c=n.BrowserTestAutomationAgent||n.default;if(c&&typeof c=="function"){let i=new c().buildGraph();i&&typeof i.serialize=="function"&&(e=i.serialize())}}catch{}if(!e)try{let{createRequire:n}=await import("module"),c=n(import.meta.url),d=u(yt(c.resolve("@zibby/core/package.json")),"templates","browser-test-automation","run_test.json");f(d)&&(e=JSON.parse(x(d,"utf-8")))}catch{}t.json({graph:e})}catch{t.json({graph:null})}}),r.use((o,t,e)=>(t.header("Access-Control-Allow-Origin","*"),t.header("Access-Control-Allow-Methods","GET, POST, PUT, DELETE"),t.header("Access-Control-Allow-Headers","Content-Type"),e()));let ct=u(Ke,"../../../../studio"),bt=f(u(ct,"package.json"));function te(){let o=u(ct,"node_modules",".bin",process.platform==="win32"?"electron.cmd":"electron"),t=f(o),n=ht(t?o:"npx",t?["."]:["electron","."],{cwd:ct,detached:!0,stdio:"ignore",shell:!1,env:{...process.env,ZIBBY_STUDIO_PROJECT_ROOT:h,ZIBBY_STUDIO_API_BASE:`http://localhost:${w}/api`}});n.unref(),Qt=n}async function ee(){if(bt){te();return}if(a.update||!et()){if(a.update&&et()){console.log(`
5
11
  Updating Zibby Studio...
6
- `);const{installStudio:s}=await import("../utils/studio-installer.js");await s()}else if(!await re()){y.close(),process.exit(0);return}}console.log(`
12
+ `);let{installStudio:o}=await Promise.resolve().then(()=>(nt(),Ct));await o()}else if(!await ft()){p.close(),process.exit(0);return}}console.log(`
7
13
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
8
14
  \u2551 Zibby Studio \u2551
9
15
  \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
10
- `),await ie({projectRoot:S}),setTimeout(()=>{y.close(),process.exit(0)},1e3)}function it(){const s=a(S,".zibby.config.mjs");return u(s)?s:a(w,".zibby.config.mjs")}function R(){it();const s=a(S,".zibby","output","sessions");return u(s)?s:a(w,".zibby","output","sessions")}function V(s,t){let e=null,n=null,o=null;const i=a(s,"codegen");if(u(i)){const r=a(i,"test.spec.ts"),c=a(i,"generated-test.spec.js");e=u(r)?r:u(c)?c:null;const f=a(i,"test.selenium.py"),h=a(i,"generated-test-selenium.js");n=u(f)?f:u(h)?h:null;const d=a(i,"trace.zip");o=u(d)?d:null}const l=a(s,"generate_script","result.json");if(!e&&u(l))try{const c=JSON.parse(_(l,"utf8"))?.scriptPath;if(typeof c=="string"&&c.trim()){const f=c.trim(),h=f.startsWith("/")||process.platform==="win32"&&/^[A-Za-z]:[\\/]/.test(f)?f:a(t,f);u(h)&&(e=h)}}catch(r){console.warn("[Studio API] generate_script result.json:",r.message)}return!e&&!n&&!o?null:{playwrightFile:e,seleniumFile:n,tracePath:o}}function Ft(s,t,e){try{H(a(s,J),JSON.stringify({sessionId:t,pid:e??null,startedAt:Date.now()},null,2),"utf8")}catch(n){console.error("[Studio API] writeStudioRunMeta:",n.message)}}function B(s){try{const t=a(s,J);u(t)&&Xt(t)}catch{}}function Lt(s){const t=Number(s);if(!Number.isFinite(t)||t<=0)return[];try{const e=Y(`pgrep -P ${t}`,{encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:524288}).trim();return e?e.split(/\n/).map(n=>parseInt(n.trim(),10)).filter(n=>Number.isFinite(n)&&n>0):[]}catch{return[]}}function W(s,t){const e=Number(s);if(!Number.isFinite(e)||e<=0)return;const n=new Set;function o(i){if(!n.has(i)){n.add(i);for(const l of Lt(i))o(l);try{process.kill(i,t)}catch{}}}o(e)}function ct(s){const t=Number(s);if(!(!Number.isFinite(t)||t<=0))try{Y(`taskkill /PID ${t} /T /F`,{stdio:"ignore",windowsHide:!0})}catch{}}function at(s){if(!Number.isFinite(s)||s<=0)return;if(process.platform==="win32"){ct(s);return}W(s,"SIGTERM");const t=setTimeout(()=>{W(s,"SIGKILL")},800);typeof t.unref=="function"&&t.unref()}function $t(s,t){const n=C(s)||a(R(),s);try{jt(n,{recursive:!0}),H(a(n,Rt),JSON.stringify({requestedAt:Date.now()}),"utf8")}catch(r){console.error("[Studio API] write studio stop request:",r.message)}const o=x.get(s);if(o){const r=o.pid;if(process.platform==="win32")ct(r);else{W(r,"SIGTERM");const c=setTimeout(()=>{W(r,"SIGKILL")},800);typeof c.unref=="function"&&c.unref()}return x.delete(s),B(n),!0}const i=a(n,J);if(u(i)){let r=null;try{const c=JSON.parse(_(i,"utf8"));r=Number(c.pid)}catch(c){console.error("[Studio API] studio-run.json read:",c.message)}if(B(n),Number.isFinite(r)&&r>0)return at(r),!0}const l=Number(t);return Number.isFinite(l)&&l>0?(at(l),B(n),!0):!1}function M(s){try{const t=s?.query?.sessionsRoot;if(typeof t!="string")return"";const e=t.trim();if(!e)return"";const n=decodeURIComponent(e),o=E(n);return u(o)?o:""}catch{return""}}function C(s,t=""){const e=typeof s=="string"?decodeURIComponent(s).trim():String(s||"").trim();if(!e)return null;const n=[e],o=e.split("_")[0]?.trim()||"";o&&o!==e&&n.push(o);const i=new Set,l=[],r=new Set,c=[],f=g=>{if(!g)return;const p=E(g);r.has(p)||(r.add(p),c.push(p))},h=g=>{if(g){f(g);for(const p of n){const I=a(g,p);i.has(I)||(i.add(I),l.push(I))}}};h(t),h(R()),h(a(w,".zibby","output","sessions"));let d;try{d=z(S)}catch{d=[]}for(const g of d){if(g.startsWith("."))continue;const p=a(S,g);try{if(!L(p).isDirectory())continue}catch{continue}h(a(p,".zibby","output","sessions"));let I;try{I=z(p,{withFileTypes:!0})}catch{I=[]}for(const N of I)N?.isDirectory?.()&&String(N.name||"").startsWith(".zibby")&&h(a(p,N.name,"output","sessions"))}const T=l.filter(g=>u(g));if(T.length===0)for(const g of c){if(!u(g))continue;let p;try{p=z(g,{withFileTypes:!0})}catch{p=[]}for(const I of p){if(!I?.isDirectory?.())continue;const N=String(I.name||"");if(N===e||N===o||N.startsWith(`${e}_`)||o&&N.startsWith(`${o}_`)){const F=a(g,N);if(i.has(F))continue;i.add(F),T.push(F)}}}if(T.length===0)return null;if(T.length===1)return T[0];const $=g=>{let p=0;u(a(g,"execute_live"))&&(p+=20),wt(g)&&(p+=15),u(a(g,"execute_live","events.json"))&&(p+=8),u(a(g,"execute_live","result.json"))&&(p+=6),u(a(g,J))&&(p+=4),u(a(g,".session-info.json"))&&(p+=2);try{const I=a(g,"execute_live");u(I)&&(p+=Math.min(L(I).mtimeMs/1e12,3))}catch{}return p};return T.sort((g,p)=>$(p)-$(g)),T[0]}function zt(s,t){try{const e=typeof s=="string"?decodeURIComponent(s).trim():String(s||"").trim();if(!e||!t)return null;const n=(e.split("_")[0]||"").trim(),o=ot(t);if(!n||!o||!u(o))return null;let i=[];try{i=z(o,{withFileTypes:!0})}catch{i=[]}let l=null;for(const r of i){if(!r?.isDirectory?.())continue;const c=String(r.name||"");if(!(c===e||c===n||c.startsWith(`${e}_`)||c.startsWith(`${n}_`)))continue;const f=a(o,c),h=Tt(f);h&&(!l||Number(h.mtime||0)>Number(l.mtime||0))&&(l=h)}return l}catch{return null}}m.get("/api/config/check",(s,t)=>{const e=it();t.json({exists:u(e),path:e,isProjectLevel:e.startsWith(S)})}),m.get("/api/projects",(s,t)=>{try{const e=a(w,".zibby","config.json");if(!u(e))return t.json({projects:[]});const n=JSON.parse(_(e,"utf8")),o=typeof n.sessionToken=="string"&&n.sessionToken.trim()!==""?n.sessionToken.trim():"",i=Array.isArray(n.projects)?n.projects:[];if(!o)return t.json({projects:i});const l=String(Nt()).replace(/\/$/,"");fetch(`${l}/projects`,{headers:{Authorization:`Bearer ${o}`}}).then(r=>r.json().catch(()=>({})).then(c=>({ok:r.ok,status:r.status,body:c}))).then(({ok:r,body:c})=>{if(!r)return t.json({projects:i});const h=(Array.isArray(c?.projects)?c.projects:[]).map(d=>({name:d?.name,projectId:d?.projectId,apiToken:d?.apiToken||null,createdAt:d?.createdAt||null,updatedAt:d?.updatedAt||null}));n.projects=h;try{H(e,`${JSON.stringify(n,null,2)}
11
- `,"utf8")}catch{}return t.json({projects:h})}).catch(()=>t.json({projects:i}))}catch(e){t.status(500).json({error:e.message})}}),m.post("/api/projects/create",async(s,t)=>{try{const e=typeof s.body?.name=="string"?s.body.name.trim():"";if(!e)return t.status(400).json({error:"Project name is required"});const n=a(w,".zibby","config.json");if(!u(n))return t.status(401).json({error:"Not logged in"});const o=JSON.parse(_(n,"utf8")),i=typeof o.sessionToken=="string"&&o.sessionToken.trim()!==""?o.sessionToken.trim():"";if(!i)return t.status(401).json({error:"Not logged in"});const l=String(Nt()).replace(/\/$/,""),r=await fetch(`${l}/projects`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`},body:JSON.stringify({name:e})}),c=await r.json().catch(()=>({}));if(!r.ok)return t.status(r.status).json({error:c.error||c.message||`HTTP ${r.status}`});const f=c?.project&&typeof c.project=="object"?c.project:null;if(f?.projectId){const d=(Array.isArray(o.projects)?o.projects:[]).filter(T=>String(T?.projectId||"")!==String(f.projectId));d.push({name:f.name||e,projectId:f.projectId,apiToken:f.apiToken||c?.apiToken||null,createdAt:f.createdAt||null,updatedAt:f.updatedAt||null}),o.projects=d;try{H(n,`${JSON.stringify(o,null,2)}
12
- `,"utf8")}catch{}}return t.json(c)}catch(e){return t.status(500).json({error:e.message||String(e)})}});function Bt(s,t){const e=Array.isArray(t)?t.filter(f=>typeof f=="string"):[],n=process.argv[1]?E(process.argv[1]):"",o=n?te(n):"";let i;try{i=!!n&&u(n)&&L(n).isFile()&&!/\.(cmd|ps1|bat)$/i.test(n)&&(/\.(m|c)?js$/i.test(n)||o==="zibby")}catch{i=!1}if(i){const f=[n,"test",s,...e],h=[process.execPath,...f].map(d=>/\s/.test(String(d))?`"${String(d).replace(/"/g,'\\"')}"`:d).join(" ");return{useShell:!1,cmd:process.execPath,args:f,display:h}}const l=s.replace(/"/g,'\\"'),r=e.join(" "),c=`zibby test "${l}" ${r}`.trim();return{useShell:!0,cmd:c,args:[],display:c}}m.get("/api/run/result/:sessionId",(s,t)=>{const{sessionId:e}=s.params,n=D.get(e);if(!n)return t.status(404).json({pending:!0,error:"unknown_session"});t.json(n)}),m.post("/api/run",async(s,t)=>{const{task:e,args:n=[],sessionId:o,studioTestCaseId:i}=s.body;if(!e||!o)return t.status(400).json({success:!1,error:"task and sessionId are required"});const l=R(),r=a(l,o),c=E(r),f=i!=null&&String(i).trim()!==""?String(i).trim():String(o);D.set(o,{pending:!0});let h=!1,d=null;const T=()=>{if(d&&!d.destroyed)try{d.end()}catch{}d=null},$=(p,I)=>{D.delete(o),T(),h||t.status(p).json(I)},g=()=>{const p=[];return k.clients.forEach(I=>{I.sessionId===o&&p.push(I)}),p};try{const p=Bt(e,n);console.log("[Studio API] Running:",p.display),p.useShell?console.warn('[Studio API] Falling back to shell "zibby" from PATH \u2014 if runs do nothing, run Studio from this repo or ensure `zibby` points to the same install.'):console.log("[Studio API] Using same CLI as this server (argv[1]):",process.argv[1]),jt(r,{recursive:!0}),d=Zt(a(r,nt),{flags:"w"}),G(c,{sessionId:String(o),studioTestCaseId:f,status:"running",runSource:"studio",activeStageIndex:0,activeNode:"preflight",cwd:S,outputBase:".zibby/output",sessionPathAbs:c});const I=st(p.cmd,p.args,{cwd:S,shell:p.useShell,env:{...process.env,ZIBBY_SESSION_ID:o,ZIBBY_SESSION_PATH:E(r)}});x.set(o,I),Ft(r,o,I.pid),G(c,{sessionId:String(o),studioTestCaseId:f,status:"running",runSource:"studio",activeStageIndex:0,activeNode:"preflight",cwd:S,outputBase:".zibby/output",sessionPathAbs:c,pid:I.pid??null});let N="",F="",ft=!1,pt=0;const mt=setInterval(()=>{try{const b=wt(r);if(!b||b.mtime<=pt)return;pt=b.mtime;const j=_(b.p).toString("base64"),P=b.p.toLowerCase().endsWith(".png")?"image/png":"image/jpeg";g().forEach(K=>{try{K.send(JSON.stringify({type:"video-frame",sessionId:o,frame:j,mime:P}))}catch{}})}catch{}},320),gt=b=>{if(ft)return;ft=!0,T();const j={pending:!1,...b};typeof j.stdout=="string"&&j.stdout.length>U&&(j.stdout=`\u2026(truncated)
13
- ${j.stdout.slice(-U)}`),typeof j.stderr=="string"&&j.stderr.length>U&&(j.stderr=`\u2026(truncated)
14
- ${j.stderr.slice(-U)}`),D.set(o,j),g().forEach(P=>{try{P.send(JSON.stringify({type:"run-complete",sessionId:o,result:b}))}catch{}})};I.stdout.on("data",b=>{const j=b.toString();if(N+=j,d&&!d.destroyed)try{d.write(j)}catch(P){console.error("[Studio API] studio-cli.log write failed:",P.message)}g().forEach(P=>{try{P.send(JSON.stringify({type:"stdout",data:j}))}catch{}})}),I.stderr.on("data",b=>{const j=b.toString();if(F+=j,d&&!d.destroyed)try{d.write(`[stderr] ${j}`)}catch(P){console.error("[Studio API] studio-cli.log write failed:",P.message)}g().forEach(P=>{try{P.send(JSON.stringify({type:"stderr",data:j}))}catch{}})}),I.on("error",b=>{clearInterval(mt),x.delete(o),G(c,{sessionId:String(o),studioTestCaseId:f,status:"failed",runSource:"studio",activeNode:null,activeStageIndex:null,errorMessage:b.message,cwd:S,outputBase:".zibby/output",sessionPathAbs:c}),console.error("[Studio API] Spawn error:",b.message),gt({heldUntilExit:!0,success:!1,exitCode:null,error:b.message,stderr:F,stdout:N,runName:null,videoPath:null,eventsPath:null,codegenFiles:null,metadata:{sessionId:o,task:e}})}),I.on("close",b=>{clearInterval(mt),x.delete(o),G(c,{sessionId:String(o),studioTestCaseId:f,status:b===0?"completed":b===130||b===143?"interrupted":"failed",runSource:"studio",activeNode:null,activeStageIndex:null,exitCode:b,cwd:S,outputBase:".zibby/output",sessionPathAbs:c}),B(r),g().forEach(A=>{try{A.send(JSON.stringify({type:"exit",code:b}))}catch{}});const j=b===0;let P=null;const K=a(r,".session-info.json"),ht=a(r,"result.json");if(u(K))try{const A=JSON.parse(_(K,"utf8"));P=A.name||A.task}catch(A){console.error("[Studio API] Failed to read session info:",A.message)}else if(u(ht))try{const A=JSON.parse(_(ht,"utf8"));P=A.name||A.task}catch(A){console.error("[Studio API] Failed to read result:",A.message)}const Wt=[/\[SessionRecorder\] Run name set: (.+)/,/Run name: (.+)/];if(!P)for(const A of Wt){const It=N.match(A);if(It){P=It[1].trim();break}}const Mt=V(r,S),tt=At(r),yt=a(r,"events.json"),St=a(r,"execute_live","events.json"),et=u(yt)?yt:u(St)?St:null;gt({heldUntilExit:!0,success:j,exitCode:b,stdout:N,stderr:F,runName:P,videoPath:tt&&u(tt)?tt:null,eventsPath:et&&u(et)?et:null,codegenFiles:Mt,metadata:{sessionId:o,task:e}})}),h=!0,t.status(202).json({accepted:!0,sessionId:o,pid:I.pid!=null&&Number.isFinite(Number(I.pid))?Number(I.pid):null,pollPath:`/api/run/result/${o}`,message:"Run started. Poll GET /api/run/result/:sessionId until pending is false."})}catch(p){$(500,{success:!1,error:p.message})}}),m.post("/api/stop/:sessionId",(s,t)=>{const{sessionId:e}=s.params,n=s.body&&s.body.pid!=null?s.body.pid:null;if($t(e,n))return t.json({success:!0});t.status(404).json({success:!1,error:"Session not found"})}),m.get("/api/recordings",(s,t)=>{try{const e=R();if(!u(e))return t.json({recordings:[],path:e});const o=z(e).map(i=>{const l=a(e,i),r=a(l,".session-info.json"),c=a(l,"result.json");let f={};return u(r)?f=JSON.parse(_(r,"utf8")):u(c)&&(f=JSON.parse(_(c,"utf8"))),{sessionId:i,...f,path:l}});t.json({path:e,recordings:o})}catch(e){t.status(500).json({error:e.message})}}),m.get("/api/sessions/:sessionId/cli-log",(s,t)=>{try{const{sessionId:e}=s.params;let n=parseInt(String(s.query.offset||"0"),10);(Number.isNaN(n)||n<0)&&(n=0);const o=M(s),i=C(e,o),l=a(i||a(R(),e),nt);if(!u(l))return t.json({exists:!1,content:"",size:0,fromByte:0,nextOffset:0});const r=L(l).size;n>r&&(n=r);let c=n,f=r-c;f>Z&&(c=r-Z,f=Z);const h=Buffer.alloc(f),d=qt(l,"r");try{Vt(d,h,0,f,c)}finally{Qt(d)}t.json({exists:!0,content:h.toString("utf8"),size:r,fromByte:c,nextOffset:r})}catch(e){t.status(500).json({error:e.message})}}),m.get("/api/sessions/:sessionId/events",(s,t)=>{try{const{sessionId:e}=s.params,n=M(s),o=C(e,n);if(!o)return t.json({events:[]});let i=a(o,"events.json");if(!u(i)){const r=a(o,"execute_live","events.json");if(u(r))i=r;else return t.json({events:[]})}const l=JSON.parse(_(i,"utf8"));t.json({events:l})}catch(e){t.status(500).json({error:e.message})}}),m.get("/api/sessions/:sessionId/live-preview",(s,t)=>{try{const{sessionId:e}=s.params,n=M(s),o=C(e,n);if(!o)return t.json({ok:!1,error:"session_not_found"});const l=Tt(o)||zt(e,o);if(!l)return t.json({ok:!1,error:"no_frame",sessionId:e,sessionPath:o});t.json({ok:!0,frame:l.base64,mime:l.mime,mtime:l.mtime,path:l.path})}catch(e){t.status(500).json({ok:!1,error:e.message})}}),m.get("/api/sessions/:sessionId/video",(s,t)=>{try{const{sessionId:e}=s.params,n=M(s),o=C(e,n);if(!o)return t.status(404).json({error:"Session not found"});const i=At(o);if(!i||!u(i))return t.status(404).json({error:"Video not found"});const l=L(i);t.writeHead(200,{"Content-Type":"video/webm","Content-Length":l.size}),Ht(i).pipe(t)}catch(e){t.status(500).json({error:e.message})}}),m.get("/api/sessions/:sessionId/codegen/playwright",(s,t)=>{try{const{sessionId:e}=s.params,n=C(e)||a(R(),e);if(!u(n))return t.status(404).type("text/plain").send("Session not found");const o=V(n,S);if(!o?.playwrightFile||!u(o.playwrightFile))return t.status(404).type("text/plain").send("No Playwright artifact");t.type("text/plain; charset=utf-8").send(_(o.playwrightFile,"utf8"))}catch(e){t.status(500).type("text/plain").send(e.message)}}),m.get("/api/sessions/:sessionId/codegen/selenium",(s,t)=>{try{const{sessionId:e}=s.params,n=C(e)||a(R(),e);if(!u(n))return t.status(404).type("text/plain").send("Session not found");const o=V(n,S);if(!o?.seleniumFile||!u(o.seleniumFile))return t.status(404).type("text/plain").send("No Selenium artifact");t.type("text/plain; charset=utf-8").send(_(o.seleniumFile,"utf8"))}catch(e){t.status(500).type("text/plain").send(e.message)}}),m.post("/api/init",async(s,t)=>{const{agentType:e,apiKey:n}=s.body;try{const o=`zibby init --agent ${e} --headed`,i=st(o,[],{cwd:S,env:{...process.env,CURSOR_API_KEY:e==="cursor"?n:process.env.CURSOR_API_KEY,ANTHROPIC_API_KEY:e==="claude"?n:process.env.ANTHROPIC_API_KEY,OPENAI_API_KEY:e==="codex"?n:process.env.OPENAI_API_KEY,GEMINI_API_KEY:e==="gemini"?n:process.env.GEMINI_API_KEY,GOOGLE_API_KEY:e==="gemini"?n:process.env.GOOGLE_API_KEY,CI:"true",ZIBBY_CI:"true"},shell:!0});let l="",r="";i.stdout.on("data",d=>{l+=d.toString()}),i.stderr.on("data",d=>{r+=d.toString()});let c=!1;const f=(d,T)=>{if(!c)if(c=!0,d===0)t.json({success:!0,stdout:l});else{const $=T||r||"zibby init failed";t.status(500).json({success:!1,error:$,stdout:l})}},h=setTimeout(()=>{try{i.kill()}catch{}f(1,`zibby init timed out after 3 minutes. stdout so far:
15
- ${l}
16
+ `),await Yt({projectRoot:h}),setTimeout(()=>{p.close(),process.exit(0)},1e3)}function It(){let o=u(h,".zibby.config.mjs");return f(o)?o:u(_,".zibby.config.mjs")}function L(){It();let o=u(h,".zibby","output","sessions");return f(o)?o:u(_,".zibby","output","sessions")}function at(o,t){let e=null,s=null,n=null,c=u(o,"codegen");if(f(c)){let i=u(c,"test.spec.ts"),l=u(c,"generated-test.spec.js");e=f(i)?i:f(l)?l:null;let g=u(c,"test.selenium.py"),I=u(c,"generated-test-selenium.js");s=f(g)?g:f(I)?I:null;let m=u(c,"trace.zip");n=f(m)?m:null}let d=u(o,"generate_script","result.json");if(!e&&f(d))try{let l=JSON.parse(x(d,"utf8"))?.scriptPath;if(typeof l=="string"&&l.trim()){let g=l.trim(),I=g.startsWith("/")||process.platform==="win32"&&/^[A-Za-z]:[\\/]/.test(g)?g:u(t,g);f(I)&&(e=I)}}catch(i){console.warn("[Studio API] generate_script result.json:",i.message)}return!e&&!s&&!n?null:{playwrightFile:e,seleniumFile:s,tracePath:n}}function oe(o,t,e){try{st(u(o,V),JSON.stringify({sessionId:t,pid:e??null,startedAt:Date.now()},null,2),"utf8")}catch(s){console.error("[Studio API] writeStudioRunMeta:",s.message)}}function M(o){try{let t=u(o,V);f(t)&&Fe(t)}catch{}}function ne(o){let t=Number(o);if(!Number.isFinite(t)||t<=0)return[];try{let e=rt(`pgrep -P ${t}`,{encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:524288}).trim();return e?e.split(/\n/).map(s=>parseInt(s.trim(),10)).filter(s=>Number.isFinite(s)&&s>0):[]}catch{return[]}}function q(o,t){let e=Number(o);if(!Number.isFinite(e)||e<=0)return;let s=new Set;function n(c){if(!s.has(c)){s.add(c);for(let d of ne(c))n(d);try{process.kill(c,t)}catch{}}}n(e)}function wt(o){let t=Number(o);if(!(!Number.isFinite(t)||t<=0))try{rt(`taskkill /PID ${t} /T /F`,{stdio:"ignore",windowsHide:!0})}catch{}}function vt(o){if(!Number.isFinite(o)||o<=0)return;if(process.platform==="win32"){wt(o);return}q(o,"SIGTERM");let t=setTimeout(()=>{q(o,"SIGKILL")},800);typeof t.unref=="function"&&t.unref()}function se(o,t){let s=z(o)||u(L(),o);try{Wt(s,{recursive:!0}),st(u(s,Xt),JSON.stringify({requestedAt:Date.now()}),"utf8")}catch(i){console.error("[Studio API] write studio stop request:",i.message)}let n=k.get(o);if(n){let i=n.pid;if(process.platform==="win32")wt(i);else{q(i,"SIGTERM");let l=setTimeout(()=>{q(i,"SIGKILL")},800);typeof l.unref=="function"&&l.unref()}return k.delete(o),M(s),!0}let c=u(s,V);if(f(c)){let i=null;try{let l=JSON.parse(x(c,"utf8"));i=Number(l.pid)}catch(l){console.error("[Studio API] studio-run.json read:",l.message)}if(M(s),Number.isFinite(i)&&i>0)return vt(i),!0}let d=Number(t);return Number.isFinite(d)&&d>0?(vt(d),M(s),!0):!1}function Q(o){try{let t=o?.query?.sessionsRoot;if(typeof t!="string")return"";let e=t.trim();if(!e)return"";let s=decodeURIComponent(e),n=B(s);return f(n)?n:""}catch{return""}}function z(o,t=""){let e=typeof o=="string"?decodeURIComponent(o).trim():String(o||"").trim();if(!e)return null;let s=[e],n=e.split("_")[0]?.trim()||"";n&&n!==e&&s.push(n);let c=new Set,d=[],i=new Set,l=[],g=b=>{if(!b)return;let y=B(b);i.has(y)||(i.add(y),l.push(y))},I=b=>{if(b){g(b);for(let y of s){let v=u(b,y);c.has(v)||(c.add(v),d.push(v))}}};I(t),I(L()),I(u(_,".zibby","output","sessions"));let m;try{m=W(h)}catch{m=[]}for(let b of m){if(b.startsWith("."))continue;let y=u(h,b);try{if(!Z(y).isDirectory())continue}catch{continue}I(u(y,".zibby","output","sessions"));let v;try{v=W(y,{withFileTypes:!0})}catch{v=[]}for(let T of v)T?.isDirectory?.()&&String(T.name||"").startsWith(".zibby")&&I(u(y,T.name,"output","sessions"))}let N=d.filter(b=>f(b));if(N.length===0)for(let b of l){if(!f(b))continue;let y;try{y=W(b,{withFileTypes:!0})}catch{y=[]}for(let v of y){if(!v?.isDirectory?.())continue;let T=String(v.name||"");if(T===e||T===n||T.startsWith(`${e}_`)||n&&T.startsWith(`${n}_`)){let D=u(b,T);if(c.has(D))continue;c.add(D),N.push(D)}}}if(N.length===0)return null;if(N.length===1)return N[0];let G=b=>{let y=0;f(u(b,"execute_live"))&&(y+=20),Jt(b)&&(y+=15),f(u(b,"execute_live","events.json"))&&(y+=8),f(u(b,"execute_live","result.json"))&&(y+=6),f(u(b,V))&&(y+=4),f(u(b,".session-info.json"))&&(y+=2);try{let v=u(b,"execute_live");f(v)&&(y+=Math.min(Z(v).mtimeMs/1e12,3))}catch{}return y};return N.sort((b,y)=>G(y)-G(b)),N[0]}function re(o,t){try{let e=typeof o=="string"?decodeURIComponent(o).trim():String(o||"").trim();if(!e||!t)return null;let s=(e.split("_")[0]||"").trim(),n=yt(t);if(!s||!n||!f(n))return null;let c=[];try{c=W(n,{withFileTypes:!0})}catch{c=[]}let d=null;for(let i of c){if(!i?.isDirectory?.())continue;let l=String(i.name||"");if(!(l===e||l===s||l.startsWith(`${e}_`)||l.startsWith(`${s}_`)))continue;let g=u(n,l),I=Kt(g);I&&(!d||Number(I.mtime||0)>Number(d.mtime||0))&&(d=I)}return d}catch{return null}}r.get("/api/config/check",(o,t)=>{let e=It();t.json({exists:f(e),path:e,isProjectLevel:e.startsWith(h)})}),r.get("/api/projects",(o,t)=>{try{let e=u(_,".zibby","config.json");if(!f(e))return t.json({projects:[]});let s=JSON.parse(x(e,"utf8")),n=typeof s.sessionToken=="string"&&s.sessionToken.trim()!==""?s.sessionToken.trim():"",c=Array.isArray(s.projects)?s.projects:[];if(!n)return t.json({projects:c});let d=String(gt()).replace(/\/$/,"");fetch(`${d}/projects`,{headers:{Authorization:`Bearer ${n}`}}).then(i=>i.json().catch(()=>({})).then(l=>({ok:i.ok,status:i.status,body:l}))).then(({ok:i,body:l})=>{if(!i)return t.json({projects:c});let I=(Array.isArray(l?.projects)?l.projects:[]).map(m=>({name:m?.name,projectId:m?.projectId,apiToken:m?.apiToken||null,createdAt:m?.createdAt||null,updatedAt:m?.updatedAt||null}));s.projects=I;try{st(e,`${JSON.stringify(s,null,2)}
17
+ `,"utf8")}catch{}return t.json({projects:I})}).catch(()=>t.json({projects:c}))}catch(e){t.status(500).json({error:e.message})}}),r.post("/api/projects/create",async(o,t)=>{try{let e=typeof o.body?.name=="string"?o.body.name.trim():"";if(!e)return t.status(400).json({error:"Project name is required"});let s=u(_,".zibby","config.json");if(!f(s))return t.status(401).json({error:"Not logged in"});let n=JSON.parse(x(s,"utf8")),c=typeof n.sessionToken=="string"&&n.sessionToken.trim()!==""?n.sessionToken.trim():"";if(!c)return t.status(401).json({error:"Not logged in"});let d=String(gt()).replace(/\/$/,""),i=await fetch(`${d}/projects`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${c}`},body:JSON.stringify({name:e})}),l=await i.json().catch(()=>({}));if(!i.ok)return t.status(i.status).json({error:l.error||l.message||`HTTP ${i.status}`});let g=l?.project&&typeof l.project=="object"?l.project:null;if(g?.projectId){let m=(Array.isArray(n.projects)?n.projects:[]).filter(N=>String(N?.projectId||"")!==String(g.projectId));m.push({name:g.name||e,projectId:g.projectId,apiToken:g.apiToken||l?.apiToken||null,createdAt:g.createdAt||null,updatedAt:g.updatedAt||null}),n.projects=m;try{st(s,`${JSON.stringify(n,null,2)}
18
+ `,"utf8")}catch{}}return t.json(l)}catch(e){return t.status(500).json({error:e.message||String(e)})}});function ie(o,t){let e=Array.isArray(t)?t.filter(g=>typeof g=="string"):[],s=process.argv[1]?B(process.argv[1]):"",n=s?ze(s):"",c;try{c=!!s&&f(s)&&Z(s).isFile()&&!/\.(cmd|ps1|bat)$/i.test(s)&&(/\.(m|c)?js$/i.test(s)||n==="zibby")}catch{c=!1}if(c){let g=[s,"test",o,...e],I=[process.execPath,...g].map(m=>/\s/.test(String(m))?`"${String(m).replace(/"/g,'\\"')}"`:m).join(" ");return{useShell:!1,cmd:process.execPath,args:g,display:I}}let d=o.replace(/"/g,'\\"'),i=e.join(" "),l=`zibby test "${d}" ${i}`.trim();return{useShell:!0,cmd:l,args:[],display:l}}r.get("/api/run/result/:sessionId",(o,t)=>{let{sessionId:e}=o.params,s=Y.get(e);if(!s)return t.status(404).json({pending:!0,error:"unknown_session"});t.json(s)}),r.post("/api/run",async(o,t)=>{let{task:e,args:s=[],sessionId:n,studioTestCaseId:c}=o.body;if(!e||!n)return t.status(400).json({success:!1,error:"task and sessionId are required"});let d=L(),i=u(d,n),l=B(i),g=c!=null&&String(c).trim()!==""?String(c).trim():String(n);Y.set(n,{pending:!0});let I=!1,m=null,N=()=>{if(m&&!m.destroyed)try{m.end()}catch{}m=null},G=(y,v)=>{Y.delete(n),N(),I||t.status(y).json(v)},b=()=>{let y=[];return S.clients.forEach(v=>{v.sessionId===n&&y.push(v)}),y};try{let y=ie(e,s);console.log("[Studio API] Running:",y.display),y.useShell?console.warn('[Studio API] Falling back to shell "zibby" from PATH \u2014 if runs do nothing, run Studio from this repo or ensure `zibby` points to the same install.'):console.log("[Studio API] Using same CLI as this server (argv[1]):",process.argv[1]),Wt(i,{recursive:!0}),m=Be(u(i,St),{flags:"w"}),K(l,{sessionId:String(n),studioTestCaseId:g,status:"running",runSource:"studio",activeStageIndex:0,activeNode:"preflight",cwd:h,outputBase:".zibby/output",sessionPathAbs:l});let v=ht(y.cmd,y.args,{cwd:h,shell:y.useShell,env:{...process.env,ZIBBY_SESSION_ID:n,ZIBBY_SESSION_PATH:B(i)}});k.set(n,v),oe(i,n,v.pid),K(l,{sessionId:String(n),studioTestCaseId:g,status:"running",runSource:"studio",activeStageIndex:0,activeNode:"preflight",cwd:h,outputBase:".zibby/output",sessionPathAbs:l,pid:v.pid??null});let T="",D="",At=!1,Nt=0,Tt=setInterval(()=>{try{let P=Jt(i);if(!P||P.mtime<=Nt)return;Nt=P.mtime;let j=x(P.p).toString("base64"),A=P.p.toLowerCase().endsWith(".png")?"image/png":"image/jpeg";b().forEach(X=>{try{X.send(JSON.stringify({type:"video-frame",sessionId:n,frame:j,mime:A}))}catch{}})}catch{}},320),Rt=P=>{if(At)return;At=!0,N();let j={pending:!1,...P};typeof j.stdout=="string"&&j.stdout.length>H&&(j.stdout=`\u2026(truncated)
19
+ ${j.stdout.slice(-H)}`),typeof j.stderr=="string"&&j.stderr.length>H&&(j.stderr=`\u2026(truncated)
20
+ ${j.stderr.slice(-H)}`),Y.set(n,j),b().forEach(A=>{try{A.send(JSON.stringify({type:"run-complete",sessionId:n,result:P}))}catch{}})};v.stdout.on("data",P=>{let j=P.toString();if(T+=j,m&&!m.destroyed)try{m.write(j)}catch(A){console.error("[Studio API] studio-cli.log write failed:",A.message)}b().forEach(A=>{try{A.send(JSON.stringify({type:"stdout",data:j}))}catch{}})}),v.stderr.on("data",P=>{let j=P.toString();if(D+=j,m&&!m.destroyed)try{m.write(`[stderr] ${j}`)}catch(A){console.error("[Studio API] studio-cli.log write failed:",A.message)}b().forEach(A=>{try{A.send(JSON.stringify({type:"stderr",data:j}))}catch{}})}),v.on("error",P=>{clearInterval(Tt),k.delete(n),K(l,{sessionId:String(n),studioTestCaseId:g,status:"failed",runSource:"studio",activeNode:null,activeStageIndex:null,errorMessage:P.message,cwd:h,outputBase:".zibby/output",sessionPathAbs:l}),console.error("[Studio API] Spawn error:",P.message),Rt({heldUntilExit:!0,success:!1,exitCode:null,error:P.message,stderr:D,stdout:T,runName:null,videoPath:null,eventsPath:null,codegenFiles:null,metadata:{sessionId:n,task:e}})}),v.on("close",P=>{clearInterval(Tt),k.delete(n),K(l,{sessionId:String(n),studioTestCaseId:g,status:P===0?"completed":P===130||P===143?"interrupted":"failed",runSource:"studio",activeNode:null,activeStageIndex:null,exitCode:P,cwd:h,outputBase:".zibby/output",sessionPathAbs:l}),M(i),b().forEach(R=>{try{R.send(JSON.stringify({type:"exit",code:P}))}catch{}});let j=P===0,A=null,X=u(i,".session-info.json"),Ot=u(i,"result.json");if(f(X))try{let R=JSON.parse(x(X,"utf8"));A=R.name||R.task}catch(R){console.error("[Studio API] Failed to read session info:",R.message)}else if(f(Ot))try{let R=JSON.parse(x(Ot,"utf8"));A=R.name||R.task}catch(R){console.error("[Studio API] Failed to read result:",R.message)}let de=[/\[SessionRecorder\] Run name set: (.+)/,/Run name: (.+)/];if(!A)for(let R of de){let Et=T.match(R);if(Et){A=Et[1].trim();break}}let pe=at(i,h),dt=Ht(i),xt=u(i,"events.json"),kt=u(i,"execute_live","events.json"),pt=f(xt)?xt:f(kt)?kt:null;Rt({heldUntilExit:!0,success:j,exitCode:P,stdout:T,stderr:D,runName:A,videoPath:dt&&f(dt)?dt:null,eventsPath:pt&&f(pt)?pt:null,codegenFiles:pe,metadata:{sessionId:n,task:e}})}),I=!0,t.status(202).json({accepted:!0,sessionId:n,pid:v.pid!=null&&Number.isFinite(Number(v.pid))?Number(v.pid):null,pollPath:`/api/run/result/${n}`,message:"Run started. Poll GET /api/run/result/:sessionId until pending is false."})}catch(y){G(500,{success:!1,error:y.message})}}),r.post("/api/stop/:sessionId",(o,t)=>{let{sessionId:e}=o.params,s=o.body&&o.body.pid!=null?o.body.pid:null;if(se(e,s))return t.json({success:!0});t.status(404).json({success:!1,error:"Session not found"})}),r.get("/api/recordings",(o,t)=>{try{let e=L();if(!f(e))return t.json({recordings:[],path:e});let n=W(e).map(c=>{let d=u(e,c),i=u(d,".session-info.json"),l=u(d,"result.json"),g={};return f(i)?g=JSON.parse(x(i,"utf8")):f(l)&&(g=JSON.parse(x(l,"utf8"))),{sessionId:c,...g,path:d}});t.json({path:e,recordings:n})}catch(e){t.status(500).json({error:e.message})}}),r.get("/api/sessions/:sessionId/cli-log",(o,t)=>{try{let{sessionId:e}=o.params,s=parseInt(String(o.query.offset||"0"),10);(Number.isNaN(s)||s<0)&&(s=0);let n=Q(o),c=z(e,n),d=u(c||u(L(),e),St);if(!f(d))return t.json({exists:!1,content:"",size:0,fromByte:0,nextOffset:0});let i=Z(d).size;s>i&&(s=i);let l=s,g=i-l;g>it&&(l=i-it,g=it);let I=Buffer.alloc(g),m=Le(d,"r");try{Ce(m,I,0,g,l)}finally{$e(m)}t.json({exists:!0,content:I.toString("utf8"),size:i,fromByte:l,nextOffset:i})}catch(e){t.status(500).json({error:e.message})}}),r.get("/api/sessions/:sessionId/events",(o,t)=>{try{let{sessionId:e}=o.params,s=Q(o),n=z(e,s);if(!n)return t.json({events:[]});let c=u(n,"events.json");if(!f(c)){let i=u(n,"execute_live","events.json");if(f(i))c=i;else return t.json({events:[]})}let d=JSON.parse(x(c,"utf8"));t.json({events:d})}catch(e){t.status(500).json({error:e.message})}}),r.get("/api/sessions/:sessionId/live-preview",(o,t)=>{try{let{sessionId:e}=o.params,s=Q(o),n=z(e,s);if(!n)return t.json({ok:!1,error:"session_not_found"});let d=Kt(n)||re(e,n);if(!d)return t.json({ok:!1,error:"no_frame",sessionId:e,sessionPath:n});t.json({ok:!0,frame:d.base64,mime:d.mime,mtime:d.mtime,path:d.path})}catch(e){t.status(500).json({ok:!1,error:e.message})}}),r.get("/api/sessions/:sessionId/video",(o,t)=>{try{let{sessionId:e}=o.params,s=Q(o),n=z(e,s);if(!n)return t.status(404).json({error:"Session not found"});let c=Ht(n);if(!c||!f(c))return t.status(404).json({error:"Video not found"});let d=Z(c);t.writeHead(200,{"Content-Type":"video/webm","Content-Length":d.size}),Ue(c).pipe(t)}catch(e){t.status(500).json({error:e.message})}}),r.get("/api/sessions/:sessionId/codegen/playwright",(o,t)=>{try{let{sessionId:e}=o.params,s=z(e)||u(L(),e);if(!f(s))return t.status(404).type("text/plain").send("Session not found");let n=at(s,h);if(!n?.playwrightFile||!f(n.playwrightFile))return t.status(404).type("text/plain").send("No Playwright artifact");t.type("text/plain; charset=utf-8").send(x(n.playwrightFile,"utf8"))}catch(e){t.status(500).type("text/plain").send(e.message)}}),r.get("/api/sessions/:sessionId/codegen/selenium",(o,t)=>{try{let{sessionId:e}=o.params,s=z(e)||u(L(),e);if(!f(s))return t.status(404).type("text/plain").send("Session not found");let n=at(s,h);if(!n?.seleniumFile||!f(n.seleniumFile))return t.status(404).type("text/plain").send("No Selenium artifact");t.type("text/plain; charset=utf-8").send(x(n.seleniumFile,"utf8"))}catch(e){t.status(500).type("text/plain").send(e.message)}}),r.post("/api/init",async(o,t)=>{let{agentType:e,apiKey:s}=o.body;try{let n=`zibby init --agent ${e} --headed`,c=ht(n,[],{cwd:h,env:{...process.env,CURSOR_API_KEY:e==="cursor"?s:process.env.CURSOR_API_KEY,ANTHROPIC_API_KEY:e==="claude"?s:process.env.ANTHROPIC_API_KEY,OPENAI_API_KEY:e==="codex"?s:process.env.OPENAI_API_KEY,GEMINI_API_KEY:e==="gemini"?s:process.env.GEMINI_API_KEY,GOOGLE_API_KEY:e==="gemini"?s:process.env.GOOGLE_API_KEY,CI:"true",ZIBBY_CI:"true"},shell:!0}),d="",i="";c.stdout.on("data",m=>{d+=m.toString()}),c.stderr.on("data",m=>{i+=m.toString()});let l=!1,g=(m,N)=>{if(!l)if(l=!0,m===0)t.json({success:!0,stdout:d});else{let G=N||i||"zibby init failed";t.status(500).json({success:!1,error:G,stdout:d})}},I=setTimeout(()=>{try{c.kill()}catch{}g(1,`zibby init timed out after 3 minutes. stdout so far:
21
+ ${d}
16
22
  stderr:
17
- ${r}`)},18e4);i.on("close",d=>{clearTimeout(h),f(d)}),i.on("error",d=>{clearTimeout(h),f(1,d.message)})}catch(o){t.status(500).json({success:!1,error:o.message})}});const Q=(s,t)=>{const e={type:s,message:t,time:new Date().toLocaleTimeString()};k.clients.forEach(n=>{if(n.listenType==="console")try{n.send(JSON.stringify(e))}catch{}})},Gt=console.log,Dt=console.error,Ut=console.warn,X=s=>s.map(t=>{if(t==null)return String(t);if(typeof t=="object")try{return se(t,{depth:4,colors:!1,breakLength:100})}catch{return String(t)}return String(t)}).join(" ");console.log=(...s)=>{Gt(...s),Q("info",X(s))},console.error=(...s)=>{Dt(...s),Q("error",X(s))},console.warn=(...s)=>{Ut(...s),Q("warn",X(s))},k.on("connection",(s,t)=>{const e=t.url;if(e.includes("/console"))s.listenType="console",console.log("[WebSocket] Console listener connected"),s.send(JSON.stringify({type:"success",message:"\u{1F3AD} Connected to Studio server logs",time:new Date().toLocaleTimeString()}));else{let n=e.split("/").pop()||"";try{n=decodeURIComponent(n)}catch{}s.sessionId=n,s.listenType="session",console.log(`[WebSocket] Session listener connected: ${n}`)}s.on("close",()=>{s.listenType==="console"?console.log("[WebSocket] Console listener disconnected"):console.log(`[WebSocket] Session listener disconnected: ${s.sessionId}`)})}),m.get("/api/workflow/graph",async(s,t)=>{try{const e=a(S,".zibby","graph.mjs");if(!u(e))return console.warn("[Workflow Graph API] Graph file not found at",e),t.json({graph:null,error:"No graph.mjs found in .zibby folder"});const n=await import(`${Pt(e).href}?t=${Date.now()}`),o=n.default||Object.values(n)[0];if(!o||typeof o!="function")return console.error("[Workflow Graph API] Invalid graph module, got:",typeof o),t.json({graph:null,error:"Invalid graph module"});const l=new o().buildGraph(),r=l.toJSON?l.toJSON():l.serialize();console.log(`[Workflow Graph API] graph OK nodes=${r.nodes?.length??0} edges=${r.edges?.length??0} (${e})`),t.json({graph:r})}catch(e){console.error("[Workflow Graph API] Failed to load workflow graph:",e),t.status(500).json({graph:null,error:e.message})}});const Jt=()=>{rt&&console.log(`
23
+ ${i}`)},18e4);c.on("close",m=>{clearTimeout(I),g(m)}),c.on("error",m=>{clearTimeout(I),g(1,m.message)})}catch(n){t.status(500).json({success:!1,error:n.message})}});let lt=(o,t)=>{let e={type:o,message:t,time:new Date().toLocaleTimeString()};S.clients.forEach(s=>{if(s.listenType==="console")try{s.send(JSON.stringify(e))}catch{}})},ce=console.log,ae=console.error,le=console.warn,ut=o=>o.map(t=>{if(t==null)return String(t);if(typeof t=="object")try{return Ze(t,{depth:4,colors:!1,breakLength:100})}catch{return String(t)}return String(t)}).join(" ");console.log=(...o)=>{ce(...o),lt("info",ut(o))},console.error=(...o)=>{ae(...o),lt("error",ut(o))},console.warn=(...o)=>{le(...o),lt("warn",ut(o))},S.on("connection",(o,t)=>{let e=t.url;if(e.includes("/console"))o.listenType="console",console.log("[WebSocket] Console listener connected"),o.send(JSON.stringify({type:"success",message:"\u{1F3AD} Connected to Studio server logs",time:new Date().toLocaleTimeString()}));else{let s=e.split("/").pop()||"";try{s=decodeURIComponent(s)}catch{}o.sessionId=s,o.listenType="session",console.log(`[WebSocket] Session listener connected: ${s}`)}o.on("close",()=>{o.listenType==="console"?console.log("[WebSocket] Console listener disconnected"):console.log(`[WebSocket] Session listener disconnected: ${o.sessionId}`)})}),r.get("/api/workflow/graph",async(o,t)=>{try{let e=u(h,".zibby","graph.mjs");if(!f(e))return console.warn("[Workflow Graph API] Graph file not found at",e),t.json({graph:null,error:"No graph.mjs found in .zibby folder"});let s=await import(`${Mt(e).href}?t=${Date.now()}`),n=s.default||Object.values(s)[0];if(!n||typeof n!="function")return console.error("[Workflow Graph API] Invalid graph module, got:",typeof n),t.json({graph:null,error:"Invalid graph module"});let d=new n().buildGraph(),i=d.toJSON?d.toJSON():d.serialize();console.log(`[Workflow Graph API] graph OK nodes=${i.nodes?.length??0} edges=${i.edges?.length??0} (${e})`),t.json({graph:i})}catch(e){console.error("[Workflow Graph API] Failed to load workflow graph:",e),t.status(500).json({graph:null,error:e.message})}});let ue=()=>{bt&&console.log(`
18
24
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
19
25
  \u2551 Zibby Studio \u2551
20
26
  \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
21
27
 
22
- API: http://localhost:${O}/api
23
- Project: ${S}
28
+ API: http://localhost:${w}/api
29
+ Project: ${h}
24
30
 
25
31
  Press Ctrl+C to stop
26
- `),v.open!==!1&&Ct().catch(s=>{console.error(`Failed to launch Zibby Studio: ${s.message}`)})};let lt=!1;function ut(){const s=async t=>{if(y.removeListener("error",s),t.code==="EADDRINUSE"&&!lt){lt=!0,console.log(`[Studio] Port ${O} still busy \u2014 forcing cleanup and retrying...`),await kt(O),ut();return}console.error("[Studio] Server error:",t.message),process.exit(1)};y.once("error",s),y.listen(O,()=>{y.removeListener("error",s),y.on("error",t=>{console.error("[Studio] Runtime error:",t.message)}),Jt()})}await kt(O),ut();const dt=()=>{console.log(`
27
- Stopping Studio...`);const s=R();for(const t of x.keys()){const e=E(a(s,t));try{G(e,{status:"interrupted",runSource:"studio",activeNode:null,activeStageIndex:null,exitReason:"studio-shutdown"})}catch{}try{B(a(s,t))}catch{}}x.forEach(t=>t.kill()),y.close(()=>{console.log("Studio stopped"),process.exit(0)})};process.on("SIGINT",dt),process.on("SIGTERM",dt)}export{ke as studioCommand};
32
+ `),a.open!==!1&&ee().catch(o=>{console.error(`Failed to launch Zibby Studio: ${o.message}`)})},Pt=!1;function jt(){let o=async t=>{if(p.removeListener("error",o),t.code==="EADDRINUSE"&&!Pt){Pt=!0,console.log(`[Studio] Port ${w} still busy \u2014 forcing cleanup and retrying...`),await qt(w),jt();return}console.error("[Studio] Server error:",t.message),process.exit(1)};p.once("error",o),p.listen(w,()=>{p.removeListener("error",o),p.on("error",t=>{console.error("[Studio] Runtime error:",t.message)}),ue()})}await qt(w),jt();let _t=()=>{console.log(`
33
+ Stopping Studio...`);let o=L();for(let t of k.keys()){let e=B(u(o,t));try{K(e,{status:"interrupted",runSource:"studio",activeNode:null,activeStageIndex:null,exitReason:"studio-shutdown"})}catch{}try{M(u(o,t))}catch{}}k.forEach(t=>t.kill()),p.close(()=>{console.log("Studio stopped"),process.exit(0)})};process.on("SIGINT",_t),process.on("SIGTERM",_t)}export{No as studioCommand};
@@ -1,15 +1,15 @@
1
- import{spawnSync as C}from"child_process";import{existsSync as i,readFileSync as z,writeFileSync as S,readdirSync as m,cpSync as R,rmSync as p,mkdirSync as x}from"fs";import{join as r}from"path";import{homedir as f}from"os";import o from"chalk";const h=r(f(),".zibby-cleanup-backups",new Date().toISOString().replace(/[:.]/g,"-").slice(0,19));function u(s){x(h,{recursive:!0});try{R(s,r(h,s.replace(/\//g,"_")),{recursive:!0})}catch{}}function j(s,l){i(s)&&(u(s),p(s,{recursive:!0,force:!0}),console.log(o.gray(` \u2713 Removed ${l||s}`)))}async function A(s={}){const l=s.dryRun||!1,y=process.cwd();if(console.log(o.cyan(`
1
+ import{spawnSync as C}from"child_process";import{existsSync as i,readFileSync as z,writeFileSync as S,readdirSync as m,cpSync as R,rmSync as p,mkdirSync as x}from"fs";import{join as r}from"path";import{homedir as f}from"os";import o from"chalk";var h=r(f(),".zibby-cleanup-backups",new Date().toISOString().replace(/[:.]/g,"-").slice(0,19));function u(s){x(h,{recursive:!0});try{R(s,r(h,s.replace(/\//g,"_")),{recursive:!0})}catch{}}function j(s,l){i(s)&&(u(s),p(s,{recursive:!0,force:!0}),console.log(o.gray(` \u2713 Removed ${l||s}`)))}async function A(s={}){let l=s.dryRun||!1,y=process.cwd();if(console.log(o.cyan(`
2
2
  \u{1F9F9} Zibby Uninstall
3
3
  `)),l&&console.log(o.yellow(` (dry-run mode \u2014 nothing will be deleted)
4
- `)),console.log(o.white("Global CLI:")),l)console.log(o.gray(" [dry-run] npm uninstall -g @zibby/cli"));else try{C("npm",["uninstall","-g","@zibby/cli"],{stdio:"pipe",timeout:3e4}),console.log(o.gray(" \u2713 npm uninstall -g @zibby/cli"))}catch{console.log(o.yellow(" \u26A0 Could not uninstall global CLI"))}const b=r(f(),".zibby");console.log(o.white(`
5
- Global config (~/.zibby):`)),i(b)?l?console.log(o.gray(` [dry-run] rm -rf ${b}`)):j(b,"~/.zibby"):console.log(o.gray(" (not found)"));const g=r(f(),".cursor","mcp.json");if(console.log(o.white(`
6
- Cursor MCP config:`)),i(g))try{const n=JSON.parse(z(g,"utf-8"));n.mcpServers?.["playwright-official"]?l?console.log(o.gray(" [dry-run] Remove playwright-official from ~/.cursor/mcp.json")):(u(g),delete n.mcpServers["playwright-official"],S(g,`${JSON.stringify(n,null,2)}
7
- `),console.log(o.gray(" \u2713 Removed playwright-official from ~/.cursor/mcp.json"))):console.log(o.gray(" (no playwright-official entry)"))}catch{console.log(o.yellow(" \u26A0 Could not parse ~/.cursor/mcp.json"))}else console.log(o.gray(" (not found)"));const d=r(f(),".cursor","projects");if(console.log(o.white(`
8
- Cursor MCP approvals:`)),i(d)){let n=0;for(const t of m(d)){const e=r(d,t,"mcp-approvals.json");i(e)&&(l?console.log(o.gray(` [dry-run] rm ${e}`)):j(e,`${t}/mcp-approvals.json`),n++)}n===0&&console.log(o.gray(" (none found)"))}else console.log(o.gray(" (not found)"));const k="/Applications/Zibby Studio.app",$=r(f(),"Library","Application Support","@zibby");console.log(o.white(`
9
- Zibby Studio:`));let v=!1;for(const n of[k,$])i(n)&&(v=!0,l?console.log(o.gray(` [dry-run] rm -rf "${n}"`)):(p(n,{recursive:!0,force:!0}),console.log(o.gray(` \u2713 Removed ${n}`))));if(v||console.log(o.gray(" (not found)")),i(r(y,".zibby"))||i(r(y,".zibby.config.mjs"))){console.log(o.white(`
10
- Current project (${y}):`));const n=[".zibby",".zibby.config.mjs",".env.example","node_modules","package-lock.json","test-results","playwright-report"];for(const e of n){const c=r(y,e);i(c)&&(l?console.log(o.gray(` [dry-run] rm -rf ${e}`)):([".zibby",".zibby.config.mjs",".env.example"].includes(e)&&u(c),p(c,{recursive:!0,force:!0}),console.log(o.gray(` \u2713 Removed ${e}`))))}const t=r(y,"package.json");if(i(t))try{const e=JSON.parse(z(t,"utf-8"));let c=!1;for(const a of["dependencies","devDependencies","peerDependencies"])if(e[a]){for(const w of Object.keys(e[a]))w.startsWith("@zibby/")&&(delete e[a][w],c=!0);e[a]&&Object.keys(e[a]).length===0&&delete e[a]}c&&(l?console.log(o.gray(" [dry-run] Strip @zibby/* from package.json")):(u(t),S(t,`${JSON.stringify(e,null,2)}
11
- `),console.log(o.gray(" \u2713 Stripped @zibby/* from package.json"))))}catch{}}if(s.deep){const n=r(f(),".npm","_npx");if(console.log(o.white(`
12
- npx cache (--deep):`)),i(n)){let t=0;for(const e of m(n)){const c=r(n,e);try{const a=m(c,{recursive:!0}).join(`
4
+ `)),console.log(o.white("Global CLI:")),l)console.log(o.gray(" [dry-run] npm uninstall -g @zibby/cli"));else try{C("npm",["uninstall","-g","@zibby/cli"],{stdio:"pipe",timeout:3e4}),console.log(o.gray(" \u2713 npm uninstall -g @zibby/cli"))}catch{console.log(o.yellow(" \u26A0 Could not uninstall global CLI"))}let b=r(f(),".zibby");console.log(o.white(`
5
+ Global config (~/.zibby):`)),i(b)?l?console.log(o.gray(` [dry-run] rm -rf ${b}`)):j(b,"~/.zibby"):console.log(o.gray(" (not found)"));let g=r(f(),".cursor","mcp.json");if(console.log(o.white(`
6
+ Cursor MCP config:`)),i(g))try{let n=JSON.parse(z(g,"utf-8"));n.mcpServers?.["playwright-official"]?l?console.log(o.gray(" [dry-run] Remove playwright-official from ~/.cursor/mcp.json")):(u(g),delete n.mcpServers["playwright-official"],S(g,`${JSON.stringify(n,null,2)}
7
+ `),console.log(o.gray(" \u2713 Removed playwright-official from ~/.cursor/mcp.json"))):console.log(o.gray(" (no playwright-official entry)"))}catch{console.log(o.yellow(" \u26A0 Could not parse ~/.cursor/mcp.json"))}else console.log(o.gray(" (not found)"));let d=r(f(),".cursor","projects");if(console.log(o.white(`
8
+ Cursor MCP approvals:`)),i(d)){let n=0;for(let t of m(d)){let e=r(d,t,"mcp-approvals.json");i(e)&&(l?console.log(o.gray(` [dry-run] rm ${e}`)):j(e,`${t}/mcp-approvals.json`),n++)}n===0&&console.log(o.gray(" (none found)"))}else console.log(o.gray(" (not found)"));let k="/Applications/Zibby Studio.app",$=r(f(),"Library","Application Support","@zibby");console.log(o.white(`
9
+ Zibby Studio:`));let v=!1;for(let n of[k,$])i(n)&&(v=!0,l?console.log(o.gray(` [dry-run] rm -rf "${n}"`)):(p(n,{recursive:!0,force:!0}),console.log(o.gray(` \u2713 Removed ${n}`))));if(v||console.log(o.gray(" (not found)")),i(r(y,".zibby"))||i(r(y,".zibby.config.mjs"))){console.log(o.white(`
10
+ Current project (${y}):`));let n=[".zibby",".zibby.config.mjs",".env.example","node_modules","package-lock.json","test-results","playwright-report"];for(let e of n){let c=r(y,e);i(c)&&(l?console.log(o.gray(` [dry-run] rm -rf ${e}`)):([".zibby",".zibby.config.mjs",".env.example"].includes(e)&&u(c),p(c,{recursive:!0,force:!0}),console.log(o.gray(` \u2713 Removed ${e}`))))}let t=r(y,"package.json");if(i(t))try{let e=JSON.parse(z(t,"utf-8")),c=!1;for(let a of["dependencies","devDependencies","peerDependencies"])if(e[a]){for(let w of Object.keys(e[a]))w.startsWith("@zibby/")&&(delete e[a][w],c=!0);e[a]&&Object.keys(e[a]).length===0&&delete e[a]}c&&(l?console.log(o.gray(" [dry-run] Strip @zibby/* from package.json")):(u(t),S(t,`${JSON.stringify(e,null,2)}
11
+ `),console.log(o.gray(" \u2713 Stripped @zibby/* from package.json"))))}catch{}}if(s.deep){let n=r(f(),".npm","_npx");if(console.log(o.white(`
12
+ npx cache (--deep):`)),i(n)){let t=0;for(let e of m(n)){let c=r(n,e);try{let a=m(c,{recursive:!0}).join(`
13
13
  `);/zibby/i.test(a)&&(l?console.log(o.gray(` [dry-run] rm -rf ${c}`)):(p(c,{recursive:!0,force:!0}),console.log(o.gray(` \u2713 Removed ${c}`))),t++)}catch{}}t===0&&console.log(o.gray(" (no zibby-related npx cache found)"))}else console.log(o.gray(" (no ~/.npm/_npx)"))}console.log(o.green(`
14
14
  \u2705 Uninstall complete.`)),l||console.log(o.gray(` Backups: ${h}`)),console.log(o.gray(` To reinstall: npm i -g @zibby/cli && zibby init
15
15
  `))}export{A as uninstallCommand};
@@ -1,22 +1,22 @@
1
- import{readFileSync as u,existsSync as s,statSync as v}from"fs";import{join as c,basename as E}from"path";import{glob as P}from"glob";import e from"chalk";import T from"ora";import _ from"dotenv";import{getApiUrl as S,getCurrentEnvironment as j,getFrontendUrl as A}from"../config/environments.js";import{getSessionToken as k,getUserInfo as D}from"../config/config.js";_.config();function C(a,t){const i=t?.paths?.specs||"test-specs",r=t?.paths?.generated||"tests";return a.replace(i,r).replace(/\.txt$/,".spec.js")}async function L(a,t){const i=j();console.log(e.bold.cyan(`
1
+ import{readFileSync as m,existsSync as l,statSync as P}from"fs";import{join as p,basename as j}from"path";import{glob as F}from"glob";import e from"chalk";import k from"ora";import z from"dotenv";var r={local:{name:"Local Development",apiUrl:"http://localhost:3001",accountApiUrl:"http://localhost:3001",frontendUrl:"http://localhost:3000",description:"Local backend running on port 3001"},prod:{name:"Production",apiUrl:process.env.ZIBBY_PROD_API_URL||"https://api-prod.zibby.app",accountApiUrl:process.env.ZIBBY_PROD_ACCOUNT_API_URL||"https://account-api-prod.zibby.app",frontendUrl:process.env.ZIBBY_PROD_FRONTEND_URL||"https://studio.zibby.app",description:"Production environment"}};function A(){let n;if(process.env.ZIBBY_API_URL)n=process.env.ZIBBY_API_URL;else{let o=process.env.ZIBBY_ENV||"prod";r[o]?n=r[o].apiUrl:n=r.prod.apiUrl}try{let o=new URL(n);return o.protocol!=="http:"&&o.protocol!=="https:"?(console.error(`\u26A0\uFE0F Invalid API URL protocol: ${o.protocol} (only http/https allowed)`),r.prod.apiUrl):n}catch{return console.error(`\u26A0\uFE0F Invalid API URL: ${n}`),r.prod.apiUrl}}function R(){let n=process.env.ZIBBY_ENV||"prod";return r[n]||r.prod}function E(){let n;if(process.env.ZIBBY_FRONTEND_URL)n=process.env.ZIBBY_FRONTEND_URL;else{let o=process.env.ZIBBY_ENV||"prod";r[o]?n=r[o].frontendUrl:n=r.prod.frontendUrl}try{let o=new URL(n);if(o.protocol!=="http:"&&o.protocol!=="https:")return console.error(`\u26A0\uFE0F Invalid frontend URL protocol: ${o.protocol} (only http/https allowed)`),r.local.frontendUrl;if(process.env.NODE_ENV==="production"||process.env.ZIBBY_ENV==="prod"){let s=["zibby.app","studio.zibby.app","studio-staging.zibby.app"],i=o.hostname;if(!s.some(u=>i===u||i.endsWith(`.${u}`))&&!i.includes("localhost")&&i!=="127.0.0.1")return console.error(`\u26A0\uFE0F Untrusted frontend URL in production: ${i}`),"https://studio.zibby.app"}return n}catch{return console.error(`\u26A0\uFE0F Invalid frontend URL: ${n}`),r.local.frontendUrl}}import{existsSync as O,mkdirSync as q,readFileSync as D,writeFileSync as J}from"fs";import{homedir as $}from"os";import{join as y}from"path";function Y(){return process.env.ZIBBY_CONFIG_DIR||y($(),".zibby")}function S(){return y(Y(),"config.json")}var Z=y($(),".zibby"),H=y(Z,"config.json");function T(){try{let n=S();if(O(n)){let o=D(n,"utf-8");return JSON.parse(o)}}catch{}return{}}function w(){return T().sessionToken||null}function C(){return T().user||null}z.config();function V(n,o){let d=o?.paths?.specs||"test-specs",s=o?.paths?.generated||"tests";return n.replace(d,s).replace(/\.txt$/,".spec.js")}async function lo(n,o){let d=R();console.log(e.bold.cyan(`
2
2
  \u{1F4E4} Zibby Cloud Upload
3
- `)),console.log(e.gray("\u2501".repeat(50))),console.log(e.white(`Environment: ${e.cyan(i.name)}
4
- `));const r=process.cwd(),$=t.project||process.env.ZIBBY_API_KEY;$||(console.log(e.red(`\u274C Error: Project API key required
3
+ `)),console.log(e.gray("\u2501".repeat(50))),console.log(e.white(`Environment: ${e.cyan(d.name)}
4
+ `));let s=process.cwd(),i=o.project||process.env.ZIBBY_API_KEY;i||(console.log(e.red(`\u274C Error: Project API key required
5
5
  `)),console.log(e.white("Provide via:")),console.log(e.gray(" --project <token>")),console.log(e.gray(` or ZIBBY_API_KEY in .env
6
- `)),process.exit(1));const U=process.env.ZIBBY_USER_TOKEN||k(),b=D();U||(console.log(e.red(`
6
+ `)),process.exit(1));let v=process.env.ZIBBY_USER_TOKEN||w(),u=C();v||(console.log(e.red(`
7
7
  \u274C Error: User authentication required
8
8
  `)),console.log(e.cyan("Option 1 (Local):")),console.log(e.gray(` zibby login
9
9
  `)),console.log(e.cyan("Option 2 (CI/CD):")),console.log(e.gray(` export ZIBBY_USER_TOKEN=zby_pat_xxxxx
10
- `)),process.exit(1)),console.log(b?e.gray(`Authenticated: ${b.email}
10
+ `)),process.exit(1)),console.log(u?e.gray(`Authenticated: ${u.email}
11
11
  `):e.gray(`Authenticated: Using Personal Access Token
12
- `));const I=P.sync("test-results/sessions/*",{cwd:r}).filter(o=>v(c(r,o)).isDirectory()&&!/session_/.test(o)).sort((o,p)=>{const l=v(c(r,o));return v(c(r,p)).mtimeMs-l.mtimeMs});I.length===0&&(console.log(e.red(`\u274C No test results found
12
+ `));let b=F.sync("test-results/sessions/*",{cwd:s}).filter(t=>P(p(s,t)).isDirectory()&&!/session_/.test(t)).sort((t,g)=>{let a=P(p(s,t));return P(p(s,g)).mtimeMs-a.mtimeMs});b.length===0&&(console.log(e.red(`\u274C No test results found
13
13
  `)),console.log(e.white(`Run a test first: zibby run <spec-path>
14
- `)),process.exit(1));const f=I[0],d=c(r,f,"execute_live");s(d)||(console.log(e.red(`\u274C No execution data found in ${f}
15
- `)),process.exit(1));const m=c(d,"recording.webm"),y=c(d,"events.json"),w=c(d,"title.txt"),x=C(a,{});console.log(e.white("Found artifacts:")),console.log(e.gray(` Session: ${E(f)}`)),console.log(e.gray(` Video: ${s(m)?"\u2713":"\u2717"}`)),console.log(e.gray(` Events: ${s(y)?"\u2713":"\u2717"}`)),console.log(e.gray(` Test: ${s(x)?"\u2713":"\u2717"}
16
- `));const h=T(`Uploading to ${i.name}...`).start();try{const o=new FormData;if(o.append("specPath",a),o.append("agent",t.agent||"cursor"),o.append("agentType",t.agent||"cursor"),t.collection&&o.append("collectionIdOrName",t.collection),t.folder&&o.append("folder",t.folder),s(m)){const n=new Blob([u(m)]);o.append("video",n,"recording.webm")}if(s(y)){const n=u(y,"utf-8");o.append("events",n)}if(s(x)){const n=u(x,"utf-8");o.append("testCode",n)}if(s(w)){const n=u(w,"utf-8").trim();o.append("title",n)}const p=S(),l=await fetch(`${p}/executions/upload`,{method:"POST",headers:{Authorization:`Bearer ${$}`,"X-User-Token":U},body:o});if(!l.ok){const n=await l.text();h.fail(`Upload failed: ${l.status}`),console.log(e.red(`${n}
17
- `)),process.exit(1)}const g=await l.json();h.succeed("Upload complete!"),console.log(e.green(`
18
- \u2713 Test uploaded successfully`)),console.log(e.gray(` Execution ID: ${g.executionId}`));const B=`${A()}/projects/${g.projectId}/runs/${g.executionId}`;console.log(e.cyan(`
19
- View results: ${B}
20
- `))}catch(o){h.fail("Upload failed"),console.log(e.red(`
21
- ${o.message}
22
- `)),process.exit(1)}}export{L as uploadCommand};
14
+ `)),process.exit(1));let I=b[0],f=p(s,I,"execute_live");l(f)||(console.log(e.red(`\u274C No execution data found in ${I}
15
+ `)),process.exit(1));let x=p(f,"recording.webm"),h=p(f,"events.json"),N=p(f,"title.txt"),_=V(n,{});console.log(e.white("Found artifacts:")),console.log(e.gray(` Session: ${j(I)}`)),console.log(e.gray(` Video: ${l(x)?"\u2713":"\u2717"}`)),console.log(e.gray(` Events: ${l(h)?"\u2713":"\u2717"}`)),console.log(e.gray(` Test: ${l(_)?"\u2713":"\u2717"}
16
+ `));let B=k(`Uploading to ${d.name}...`).start();try{let t=new FormData;if(t.append("specPath",n),t.append("agent",o.agent||"cursor"),t.append("agentType",o.agent||"cursor"),o.collection&&t.append("collectionIdOrName",o.collection),o.folder&&t.append("folder",o.folder),l(x)){let c=new Blob([m(x)]);t.append("video",c,"recording.webm")}if(l(h)){let c=m(h,"utf-8");t.append("events",c)}if(l(_)){let c=m(_,"utf-8");t.append("testCode",c)}if(l(N)){let c=m(N,"utf-8").trim();t.append("title",c)}let g=A(),a=await fetch(`${g}/executions/upload`,{method:"POST",headers:{Authorization:`Bearer ${i}`,"X-User-Token":v},body:t});if(!a.ok){let c=await a.text();B.fail(`Upload failed: ${a.status}`),console.log(e.red(`${c}
17
+ `)),process.exit(1)}let U=await a.json();B.succeed("Upload complete!"),console.log(e.green(`
18
+ \u2713 Test uploaded successfully`)),console.log(e.gray(` Execution ID: ${U.executionId}`));let L=`${E()}/projects/${U.projectId}/runs/${U.executionId}`;console.log(e.cyan(`
19
+ View results: ${L}
20
+ `))}catch(t){B.fail("Upload failed"),console.log(e.red(`
21
+ ${t.message}
22
+ `)),process.exit(1)}}export{lo as uploadCommand};
@@ -1,4 +1,4 @@
1
- import{organizeVideos as s}from"@zibby/core";import{resolve as i}from"path";import r from"chalk";import t from"ora";async function m(n){const e=t("Organizing test videos...").start();try{const o=await s({projectRoot:i(process.cwd()),verbose:!1});o.success?(e.succeed(`Organized ${o.movedCount} video(s)`),console.log(r.gray(`
1
+ import{organizeVideos as s}from"@zibby/core";import{resolve as i}from"path";import r from"chalk";import t from"ora";async function m(n){let e=t("Organizing test videos...").start();try{let o=await s({projectRoot:i(process.cwd()),verbose:!1});o.success?(e.succeed(`Organized ${o.movedCount} video(s)`),console.log(r.gray(`
2
2
  \u{1F4C2} Videos are now next to their test files in tests/
3
3
  `))):(e.fail("Failed to organize videos"),o.error&&console.log(r.red(`Error: ${o.error}
4
4
  `)))}catch(o){e.fail("Failed to organize videos"),console.log(r.red(`
@@ -1,45 +1,47 @@
1
- import{readFileSync as _,writeFileSync as j,existsSync as v,mkdirSync as z}from"fs";import{resolve as A,join as y}from"path";import o from"chalk";import k from"ora";import P from"dotenv";import{getApiUrl as b,getCurrentEnvironment as E}from"../config/environments.js";import{validateGraphConfig as B}from"@zibby/core/framework/graph-compiler.js";import{generateWorkflowCode as J,generateNodeConfigsJson as N}from"@zibby/core/framework/code-generator.js";import"@zibby/core/templates/register-nodes.js";P.config();function x(s){const e=s.apiKey||process.env.ZIBBY_API_KEY;e||(console.log(o.red(`
1
+ import{readFileSync as N,writeFileSync as b,existsSync as B,mkdirSync as D}from"fs";import{resolve as T,join as m}from"path";import o from"chalk";import $ from"ora";import Y from"dotenv";import R from"inquirer";var w={local:{name:"Local Development",apiUrl:"http://localhost:3001",accountApiUrl:"http://localhost:3001",frontendUrl:"http://localhost:3000",description:"Local backend running on port 3001"},prod:{name:"Production",apiUrl:process.env.ZIBBY_PROD_API_URL||"https://api-prod.zibby.app",accountApiUrl:process.env.ZIBBY_PROD_ACCOUNT_API_URL||"https://account-api-prod.zibby.app",frontendUrl:process.env.ZIBBY_PROD_FRONTEND_URL||"https://studio.zibby.app",description:"Production environment"}};function _(){let t;if(process.env.ZIBBY_API_URL)t=process.env.ZIBBY_API_URL;else{let e=process.env.ZIBBY_ENV||"prod";w[e]?t=w[e].apiUrl:t=w.prod.apiUrl}try{let e=new URL(t);return e.protocol!=="http:"&&e.protocol!=="https:"?(console.error(`\u26A0\uFE0F Invalid API URL protocol: ${e.protocol} (only http/https allowed)`),w.prod.apiUrl):t}catch{return console.error(`\u26A0\uFE0F Invalid API URL: ${t}`),w.prod.apiUrl}}function j(){let t=process.env.ZIBBY_ENV||"prod";return w[t]||w.prod}import{validateGraphConfig as z}from"@zibby/core/framework/graph-compiler.js";import{generateWorkflowCode as S,generateNodeConfigsJson as F}from"@zibby/core/framework/code-generator.js";import"@zibby/core/templates/register-nodes.js";Y.config();async function L(t){let e=_(),l=$("Fetching projects...").start();try{let c=await fetch(`${e}/projects`,{method:"GET",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`}});c.ok||(l.fail("Failed to fetch projects"),process.exit(1));let n=await c.json();Array.isArray(n)||(n.projects&&Array.isArray(n.projects)?n=n.projects:n.data&&Array.isArray(n.data)?n=n.data:(l.fail("Unexpected response format"),process.exit(1))),(!n||n.length===0)&&(l.fail("No projects found"),process.exit(1)),l.stop();let i=n.map(r=>({name:`${r.name||"Unnamed"} (${r.id||"no-id"})`,value:r.id})),{projectId:g}=await R.prompt([{type:"list",name:"projectId",message:"Select a project:",choices:i}]);return g}catch(c){l.fail(`Error: ${c.message}`),process.exit(1)}}function x(t){let e=t.apiKey||process.env.ZIBBY_API_KEY;e||(console.log(o.red(`
2
2
  ZIBBY_API_KEY not set`)),console.log(o.gray(` Add to .env: ZIBBY_API_KEY=zby_xxx
3
- `)),process.exit(1));const f=s.project||process.env.ZIBBY_PROJECT_ID;return f||(console.log(o.red(`
3
+ `)),process.exit(1));let l=t.project||process.env.ZIBBY_PROJECT_ID;return l||(console.log(o.red(`
4
4
  --project or ZIBBY_PROJECT_ID is required`)),console.log(o.gray(` Example: zibby workflow download --project <id> --type analysis
5
- `)),process.exit(1)),{apiKey:e,projectId:f}}const C=["analysis","implementation","run_test"],U=/^[a-z][a-z0-9_-]{0,62}[a-z0-9]$/;function D(s){const e=s.type;return e||(console.log(o.red(`
6
- --type is required`)),console.log(o.gray(` Built-in types: ${C.join(", ")}`)),console.log(o.gray(` Custom workflows: any lowercase slug (e.g., ticket-triage)
7
- `)),process.exit(1)),!C.includes(e)&&!U.test(e)&&(console.log(o.red(`
8
- Invalid workflow type: "${e}"`)),console.log(o.gray(` Built-in: ${C.join(", ")}`)),console.log(o.gray(` Custom: lowercase letters, digits, hyphens (2\u201364 chars)
9
- `)),process.exit(1)),e}async function H(s){const e=E(),{apiKey:f,projectId:d}=x(s),n=D(s);console.log(o.bold.cyan(`
5
+ `)),process.exit(1)),{apiKey:e,projectId:l}}async function Z(t){let e=t.apiKey||process.env.ZIBBY_API_KEY;e||(console.log(o.red(`
6
+ ZIBBY_API_KEY not set`)),console.log(o.gray(` Add to .env: ZIBBY_API_KEY=zby_xxx
7
+ `)),process.exit(1));let l=t.project||process.env.ZIBBY_PROJECT_ID;return l||(l=await L(e)),{apiKey:e,projectId:l}}var U=["analysis","implementation","run_test"],O=/^[a-z][a-z0-9_-]{0,62}[a-z0-9]$/;function P(t){let e=t.type;return e||(console.log(o.red(`
8
+ --type is required`)),console.log(o.gray(` Built-in types: ${U.join(", ")}`)),console.log(o.gray(` Custom workflows: any lowercase slug (e.g., ticket-triage)
9
+ `)),process.exit(1)),!U.includes(e)&&!O.test(e)&&(console.log(o.red(`
10
+ Invalid workflow type: "${e}"`)),console.log(o.gray(` Built-in: ${U.join(", ")}`)),console.log(o.gray(` Custom: lowercase letters, digits, hyphens (2\u201364 chars)
11
+ `)),process.exit(1)),e}async function no(t){let e=j(),{apiKey:l,projectId:c}=x(t),n=P(t);console.log(o.bold.cyan(`
10
12
  Zibby Workflow Download
11
- `)),console.log(o.gray(" ".padEnd(52,"-"))),console.log(o.white(` Environment: ${o.cyan(e.name)}`)),console.log(o.white(` Project: ${o.cyan(d)}`)),console.log(o.white(` Type: ${o.cyan(n)}`)),console.log(o.gray(" ".padEnd(52,"-")));const l=k(" Fetching workflow from cloud...").start();try{const p=b(),r=await fetch(`${p}/projects/${d}/workflows/${n}`,{method:"GET",headers:{"Content-Type":"application/json",Authorization:`Bearer ${f}`}});if(!r.ok){const a=await r.text();l.fail(` API error: ${r.status}`),console.log(o.red(` ${a}
12
- `)),process.exit(1)}const t=await r.json();!t.graph&&t.isDefault?l.info(" No custom workflow saved -- downloading default graph"):l.succeed(` Fetched workflow (v${t.version||0})`);const i=t.graph||null;if(!i){console.log(o.yellow(`
13
+ `)),console.log(o.gray(" ".padEnd(52,"-"))),console.log(o.white(` Environment: ${o.cyan(e.name)}`)),console.log(o.white(` Project: ${o.cyan(c)}`)),console.log(o.white(` Type: ${o.cyan(n)}`)),console.log(o.gray(" ".padEnd(52,"-")));let i=$(" Fetching workflow from cloud...").start();try{let g=_(),r=await fetch(`${g}/projects/${c}/workflows/${n}`,{method:"GET",headers:{"Content-Type":"application/json",Authorization:`Bearer ${l}`}});if(!r.ok){let a=await r.text();i.fail(` API error: ${r.status}`),console.log(o.red(` ${a}
14
+ `)),process.exit(1)}let s=await r.json();!s.graph&&s.isDefault?i.info(" No custom workflow saved -- downloading default graph"):i.succeed(` Fetched workflow (v${s.version||0})`);let d=s.graph||null;if(!d){console.log(o.yellow(`
13
15
  No graph config available for this workflow.`)),console.log(o.gray(" The project is using the built-in default graph.")),console.log(o.gray(` Edit the graph in the UI first, or use --include-default to download the default.
14
- `)),s.includeDefault||process.exit(0),l.start(" Fetching default graph...");const{getDefaultGraph:a}=await import("@zibby/core/templates/graphs/index.js"),w=a(n);return w||(l.fail(` No default graph found for type "${n}"`),process.exit(1)),F(n,{graph:w,version:0,isDefault:!0,projectId:d,workflowType:n},s)}return F(n,{graph:i,version:t.version||0,isDefault:t.isDefault||!1,projectId:d,workflowType:n},s)}catch(p){l.fail(" Download failed"),console.log(o.red(`
15
- ${p.message}
16
- `)),process.exit(1)}}function F(s,e,f){const d=process.cwd(),n=f.output||y(d,".zibby");v(n)||z(n,{recursive:!0});const l={projectId:e.projectId,workflowType:e.workflowType,version:e.version,isDefault:e.isDefault},p=`workflow-${s}.js`,r=y(n,p),t=J(e.graph,l);j(r,t,"utf-8");const i=e.graph.nodeConfigs||{},a=N(i),w=`workflow-${s}.config.json`,m=y(n,w);j(m,`${JSON.stringify(a,null,2)}
17
- `,"utf-8");const $=`workflow-${s}.json`,c=y(n,$),g={_meta:{...l,downloadedAt:new Date().toISOString()},...e.graph};j(c,`${JSON.stringify(g,null,2)}
16
+ `)),t.includeDefault||process.exit(0),i.start(" Fetching default graph...");let{getDefaultGraph:a}=await import("@zibby/core/templates/graphs/index.js"),u=a(n);return u||(i.fail(` No default graph found for type "${n}"`),process.exit(1)),k(n,{graph:u,version:0,isDefault:!0,projectId:c,workflowType:n},t)}return k(n,{graph:d,version:s.version||0,isDefault:s.isDefault||!1,projectId:c,workflowType:n},t)}catch(g){i.fail(" Download failed"),console.log(o.red(`
17
+ ${g.message}
18
+ `)),process.exit(1)}}function k(t,e,l){let c=process.cwd(),n=l.output||m(c,".zibby");B(n)||D(n,{recursive:!0});let i={projectId:e.projectId,workflowType:e.workflowType,version:e.version,isDefault:e.isDefault},g=`workflow-${t}.js`,r=m(n,g),s=S(e.graph,i);b(r,s,"utf-8");let d=e.graph.nodeConfigs||{},a=F(d),u=`workflow-${t}.config.json`,v=m(n,u);b(v,`${JSON.stringify(a,null,2)}
19
+ `,"utf-8");let I=`workflow-${t}.json`,p=m(n,I),f={_meta:{...i,downloadedAt:new Date().toISOString()},...e.graph};b(p,`${JSON.stringify(f,null,2)}
18
20
  `,"utf-8"),console.log(o.green(`
19
- Generated workflow files:`)),console.log(o.white(` ${o.bold(r)}`)),console.log(o.gray(" Executable graph with inline tool bindings")),console.log(o.white(` ${o.bold(m)}`)),console.log(o.gray(" Extra prompt instructions & runtime config")),console.log(o.white(` ${o.bold(c)}`)),console.log(o.gray(" Raw JSON config (for upload back to cloud)")),console.log(""),console.log(o.gray(` Version: ${e.version}`)),console.log(o.gray(` Nodes: ${e.graph.nodes?.length||0}`)),console.log(o.gray(` Edges: ${e.graph.edges?.length||0}
21
+ Generated workflow files:`)),console.log(o.white(` ${o.bold(r)}`)),console.log(o.gray(" Executable graph with inline tool bindings")),console.log(o.white(` ${o.bold(v)}`)),console.log(o.gray(" Extra prompt instructions & runtime config")),console.log(o.white(` ${o.bold(p)}`)),console.log(o.gray(" Raw JSON config (for upload back to cloud)")),console.log(""),console.log(o.gray(` Version: ${e.version}`)),console.log(o.gray(` Nodes: ${e.graph.nodes?.length||0}`)),console.log(o.gray(` Edges: ${e.graph.edges?.length||0}
20
22
  `)),console.log(o.white(" To run locally:")),console.log(o.cyan(` zibby analyze --workflow ${r}
21
- `)),console.log(o.white(" To upload changes back:")),console.log(o.cyan(` zibby workflow upload --project ${e.projectId} --type ${s}
22
- `))}async function M(s){const e=E(),{apiKey:f,projectId:d}=x(s),n=D(s);console.log(o.bold.cyan(`
23
+ `)),console.log(o.white(" To upload changes back:")),console.log(o.cyan(` zibby workflow upload --project ${e.projectId} --type ${t}
24
+ `))}async function to(t){let e=j(),{apiKey:l,projectId:c}=await Z(t),n=P(t);console.log(o.bold.cyan(`
23
25
  Zibby Workflow Upload
24
- `)),console.log(o.gray(" ".padEnd(52,"-"))),console.log(o.white(` Environment: ${o.cyan(e.name)}`)),console.log(o.white(` Project: ${o.cyan(d)}`)),console.log(o.white(` Type: ${o.cyan(n)}`)),console.log(o.gray(" ".padEnd(52,"-")));const l=process.cwd(),p=y(l,".zibby",`workflow-${n}.json`),r=y(l,".zibby",`workflow-${n}.js`),t=s.file||(v(r)?r:p);v(t)||(console.log(o.red(`
25
- File not found: ${t}`)),console.log(o.gray(` Download a workflow first: zibby workflow download --project <id> --type <type>
26
- `)),process.exit(1));const i=t.endsWith(".js")||t.endsWith(".mjs");let a;if(i){const c=k(" Loading JS workflow module...").start();try{const{pathToFileURL:g}=await import("url"),u=await import(g(A(t)).href),h=u.buildGraph();a=h.serialize();const I=u.nodeConfigs||{};if(Object.keys(I).length>0)for(const[S,T]of Object.entries(I))a.nodeConfigs[S]={...T,...a.nodeConfigs[S]};c.succeed(` Loaded JS module (${h.nodes.size} nodes)`)}catch(g){c.fail(" Failed to load JS module"),console.log(o.red(`
27
- ${g.message}
28
- `)),process.exit(1)}}else{let c;try{c=JSON.parse(_(t,"utf-8"))}catch(h){console.log(o.red(`
29
- Failed to parse ${t}: ${h.message}
30
- `)),process.exit(1)}const{_meta:g,...u}=c;a=u}(!a.nodes||!a.edges)&&(console.log(o.red(`
26
+ `)),console.log(o.gray(" ".padEnd(52,"-"))),console.log(o.white(` Environment: ${o.cyan(e.name)}`)),console.log(o.white(` Project: ${o.cyan(c)}`)),console.log(o.white(` Type: ${o.cyan(n)}`)),console.log(o.gray(" ".padEnd(52,"-")));let i=process.cwd(),g=m(i,".zibby",`workflow-${n}.json`),r=m(i,".zibby",`workflow-${n}.js`),s=t.file||(B(r)?r:g);B(s)||(console.log(o.red(`
27
+ File not found: ${s}`)),console.log(o.gray(` Download a workflow first: zibby workflow download --project <id> --type <type>
28
+ `)),process.exit(1));let d=s.endsWith(".js")||s.endsWith(".mjs"),a;if(d){let p=$(" Loading JS workflow module...").start();try{let{pathToFileURL:f}=await import("url"),h=await import(f(T(s)).href),y=h.buildGraph();a=y.serialize();let E=h.nodeConfigs||{};if(Object.keys(E).length>0)for(let[A,C]of Object.entries(E))a.nodeConfigs[A]={...C,...a.nodeConfigs[A]};p.succeed(` Loaded JS module (${y.nodes.size} nodes)`)}catch(f){p.fail(" Failed to load JS module"),console.log(o.red(`
29
+ ${f.message}
30
+ `)),process.exit(1)}}else{let p;try{p=JSON.parse(N(s,"utf-8"))}catch(y){console.log(o.red(`
31
+ Failed to parse ${s}: ${y.message}
32
+ `)),process.exit(1)}let{_meta:f,...h}=p;a=h}(!a.nodes||!a.edges)&&(console.log(o.red(`
31
33
  Invalid workflow file: missing nodes or edges`)),console.log(o.gray(` The file should contain { nodes: [...], edges: [...], nodeConfigs: {...} }
32
34
  `)),process.exit(1)),console.log(o.gray(`
33
- File: ${t}`)),console.log(o.gray(` Format: ${i?"JavaScript (serialized via graph.serialize())":"JSON"}`)),console.log(o.gray(` Nodes: ${a.nodes.length}`)),console.log(o.gray(` Edges: ${a.edges.length}`));const w=k(" Validating graph...").start(),m=B(a);if(!m.valid){w.fail(" Graph validation failed"),console.log("");for(const c of m.errors)console.log(o.red(` ${c}`));console.log(o.gray(`
35
+ File: ${s}`)),console.log(o.gray(` Format: ${d?"JavaScript (serialized via graph.serialize())":"JSON"}`)),console.log(o.gray(` Nodes: ${a.nodes.length}`)),console.log(o.gray(` Edges: ${a.edges.length}`));let u=$(" Validating graph...").start(),v=z(a);if(!v.valid){u.fail(" Graph validation failed"),console.log("");for(let p of v.errors)console.log(o.red(` ${p}`));console.log(o.gray(`
34
36
  Fix the errors above and try again.
35
- `)),process.exit(1)}w.succeed(" Graph is valid");const $=k(" Uploading to cloud...").start();try{const c=b(),g=await fetch(`${c}/projects/${d}/workflows/${n}`,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${f}`},body:JSON.stringify({graph:a})});if(!g.ok){const h=await g.text();$.fail(` API error: ${g.status}`),console.log(o.red(` ${h}
36
- `)),process.exit(1)}const u=await g.json();$.succeed(` Uploaded successfully (v${u.version})`),console.log(o.green(`
37
- Workflow "${n}" updated to version ${u.version}`)),console.log(o.gray(` Project: ${d}
38
- `))}catch(c){$.fail(" Upload failed"),console.log(o.red(`
39
- ${c.message}
40
- `)),process.exit(1)}}const O=["analysis","implementation","run_test"];async function Q(s){const e=E(),{apiKey:f,projectId:d}=x(s);console.log(o.bold.cyan(`
37
+ `)),process.exit(1)}u.succeed(" Graph is valid");let I=$(" Uploading to cloud...").start();try{let p=_(),f=await fetch(`${p}/projects/${c}/workflows/${n}`,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${l}`},body:JSON.stringify({graph:a})});if(!f.ok){let y=await f.text();I.fail(` API error: ${f.status}`),console.log(o.red(` ${y}
38
+ `)),process.exit(1)}let h=await f.json();I.succeed(` Uploaded successfully (v${h.version})`),console.log(o.green(`
39
+ Workflow "${n}" updated to version ${h.version}`)),console.log(o.gray(` Project: ${c}
40
+ `))}catch(p){I.fail(" Upload failed"),console.log(o.red(`
41
+ ${p.message}
42
+ `)),process.exit(1)}}var J=["analysis","implementation","run_test"];async function ro(t){let e=j(),{apiKey:l,projectId:c}=x(t);console.log(o.bold.cyan(`
41
43
  Zibby Workflows
42
- `));const n=k(" Fetching workflows...").start();try{const l=b(),p=[];for(const r of O){const t=await fetch(`${l}/projects/${d}/workflows/${r}`,{method:"GET",headers:{"Content-Type":"application/json",Authorization:`Bearer ${f}`}});if(t.ok){const i=await t.json();p.push({type:r,version:i.version||0,isDefault:i.isDefault!==!1&&!i.graph,nodes:i.graph?.nodes?.length||0,updatedAt:i.updatedAt||null})}}n.succeed(` Fetched workflows
43
- `),console.log(o.gray(" ".padEnd(70,"-"))),console.log(o.white(" Type".padEnd(20))+o.white("Version".padEnd(10))+o.white("Nodes".padEnd(10))+o.white("Status".padEnd(15))+o.white("Updated")),console.log(o.gray(" ".padEnd(70,"-")));for(const r of p){const t=r.isDefault?o.gray("default"):o.green("custom"),i=r.updatedAt?new Date(r.updatedAt).toLocaleDateString():o.gray("-");console.log(` ${o.cyan(r.type.padEnd(18))}${String(r.version).padEnd(10)}${String(r.nodes).padEnd(10)}${t.padEnd(15)}${i}`)}console.log(o.gray(" ".padEnd(70,"-"))),console.log("")}catch(l){n.fail(" Failed to fetch workflows"),console.log(o.red(`
44
- ${l.message}
45
- `)),process.exit(1)}}export{H as workflowDownloadCommand,Q as workflowListCommand,M as workflowUploadCommand};
44
+ `));let n=$(" Fetching workflows...").start();try{let i=_(),g=[];for(let r of J){let s=await fetch(`${i}/projects/${c}/workflows/${r}`,{method:"GET",headers:{"Content-Type":"application/json",Authorization:`Bearer ${l}`}});if(s.ok){let d=await s.json();g.push({type:r,version:d.version||0,isDefault:d.isDefault!==!1&&!d.graph,nodes:d.graph?.nodes?.length||0,updatedAt:d.updatedAt||null})}}n.succeed(` Fetched workflows
45
+ `),console.log(o.gray(" ".padEnd(70,"-"))),console.log(o.white(" Type".padEnd(20))+o.white("Version".padEnd(10))+o.white("Nodes".padEnd(10))+o.white("Status".padEnd(15))+o.white("Updated")),console.log(o.gray(" ".padEnd(70,"-")));for(let r of g){let s=r.isDefault?o.gray("default"):o.green("custom"),d=r.updatedAt?new Date(r.updatedAt).toLocaleDateString():o.gray("-");console.log(` ${o.cyan(r.type.padEnd(18))}${String(r.version).padEnd(10)}${String(r.nodes).padEnd(10)}${s.padEnd(15)}${d}`)}console.log(o.gray(" ".padEnd(70,"-"))),console.log("")}catch(i){n.fail(" Failed to fetch workflows"),console.log(o.red(`
46
+ ${i.message}
47
+ `)),process.exit(1)}}export{no as workflowDownloadCommand,ro as workflowListCommand,to as workflowUploadCommand};
@@ -1,24 +1,14 @@
1
- import{existsSync as m}from"fs";import{readFile as B,readdir as P}from"fs/promises";import{join as y,resolve as T,relative as x}from"path";import{pathToFileURL as z}from"url";import o from"chalk";import h from"ora";import C from"dotenv";import{getApiUrl as I,getCurrentEnvironment as E}from"../../config/environments.js";import{getSessionToken as S}from"../../config/config.js";C.config();function A(r){const t=r.apiKey||process.env.ZIBBY_API_KEY||S();t||(console.log(o.red(`
2
- Not authenticated`)),console.log(o.gray(` Run: zibby login
3
- `)),process.exit(1));const e=r.project||process.env.ZIBBY_PROJECT_ID;return e||(console.log(o.red(`
4
- --project or ZIBBY_PROJECT_ID is required`)),console.log(o.gray(` Example: zibby deploy my-workflow --project <id>
5
- `)),process.exit(1)),{apiKey:t,projectId:e}}function O(r){const t=process.cwd();return y(t,".zibby","workflows",r)}async function U(r,t){const e=y(r,"workflow.json");if(!m(e))return{name:t,triggers:{api:!0}};const l=await B(e,"utf-8");return JSON.parse(l)}async function D(r){const t={};async function e(l){const u=await P(l,{withFileTypes:!0});for(const g of u){const i=y(l,g.name);if(g.isDirectory())await e(i);else if(/\.(mjs|js|json)$/.test(g.name)){const f=x(r,i);t[f]=await B(i,"utf-8")}}}return await e(r),t}async function Z(r,t){r||(console.log(o.red(`
6
- Workflow name is required`)),console.log(o.gray(" Usage: zibby deploy <workflow-name>")),console.log(o.gray(` Example: zibby deploy ticket-triage
7
- `)),process.exit(1));const e=r.toLowerCase(),l=O(e);m(l)||(console.log(o.red(`
8
- Workflow not found: .zibby/workflows/${e}/`)),console.log(o.gray(" Create one first:")),console.log(o.cyan(` zibby g workflow ${e}
9
- `)),process.exit(1));const u=E(),{apiKey:g,projectId:i}=A(t);console.log(o.bold.cyan(`
10
- Zibby Workflow Deploy
11
- `)),console.log(o.gray(" ".padEnd(56,"-"))),console.log(o.white(` Environment: ${o.cyan(u.name)}`)),console.log(o.white(` Project: ${o.cyan(i)}`)),console.log(o.white(` Workflow: ${o.cyan(e)}`)),console.log(o.gray(" ".padEnd(56,"-")));const f=h(" Loading workflow module...").start();let b,$;try{$=await U(l,e);const n=y(l,"graph.mjs");if(!m(n))throw new Error(`graph.mjs not found in .zibby/workflows/${e}/`);const s=await import(z(T(n)).href),a=$.entryClass,c=a&&s[a]||s.default||Object.values(s).find(v=>typeof v=="function"&&v.prototype?.buildGraph);if(!c)throw new Error("No WorkflowAgent class found in graph.mjs");const j=new c().buildGraph();b=j.serialize(),f.succeed(` Loaded ${c.name} (${j.nodes.size} nodes)`)}catch(n){f.fail(" Failed to load workflow"),console.log(o.red(`
12
- ${n.message}
13
- `)),process.exit(1)}const k=h(" Bundling source files...").start();let p;try{p=await D(l);const n=Object.keys(p).length,s=(Buffer.byteLength(JSON.stringify(p),"utf-8")/1024).toFixed(1);k.succeed(` Bundled ${n} files (${s} KB)`)}catch(n){k.fail(" Failed to bundle sources"),console.log(o.red(`
14
- ${n.message}
15
- `)),process.exit(1)}const w=h(" Deploying to cloud...").start();try{const n=I(),s=await fetch(`${n}/projects/${i}/workflows/${e}`,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${g}`},body:JSON.stringify({graph:b,sources:p})});if(!s.ok){const d=await s.text();w.fail(` API error: ${s.status}`),console.log(o.red(` ${d}
16
- `)),process.exit(1)}const a=await s.json();w.succeed(` Deployed successfully (v${a.version})`);const c=`${n}/projects/${i}/workflows/${e}/trigger`;if(console.log(o.green(`
17
- Workflow "${e}" deployed to version ${a.version}`)),console.log(o.white(`
18
- Trigger URL (API):`)),console.log(o.cyan(` POST ${c}`)),a.subdomain){const d=`https://${a.subdomain}.workflows.zibby.app`;console.log(o.white(`
19
- Trigger URL (subdomain):`)),console.log(o.cyan(` POST ${d}`))}console.log(o.white(`
20
- Test with:`)),console.log(o.gray(` curl -X POST ${c} \\`)),console.log(o.gray(' -H "Authorization: Bearer $ZIBBY_API_KEY" \\')),console.log(o.gray(' -H "Content-Type: application/json" \\')),console.log(o.gray(` -d '{"input": {"key": "value"}}'`)),console.log(o.white(`
21
- Tail logs:`)),console.log(o.gray(` zibby logs <jobId> --project ${i}
22
- `))}catch(n){w.fail(" Deploy failed"),console.log(o.red(`
23
- ${n.message}
24
- `)),process.exit(1)}}export{Z as deployWorkflowCommand};
1
+ import{existsSync as f,readFileSync as E}from"fs";import{join as i}from"path";import a from"chalk";import R from"ora";import F from"dotenv";import{select as C}from"@inquirer/prompts";import{existsSync as Z}from"fs";import{join as O}from"path";import{pathToFileURL as T}from"url";async function x(e){let t=O(e,".zibby.config.mjs");if(!Z(t))throw new Error(".zibby.config.mjs not found");try{let n=await import(T(t).href);return n.default||n}catch(n){throw new Error(`Failed to load .zibby.config.mjs: ${n.message}`,{cause:n})}}var U={local:{name:"Local Development",apiUrl:"http://localhost:3001",accountApiUrl:"http://localhost:3001",frontendUrl:"http://localhost:3000",description:"Local backend running on port 3001"},prod:{name:"Production",apiUrl:process.env.ZIBBY_PROD_API_URL||"https://api-prod.zibby.app",accountApiUrl:process.env.ZIBBY_PROD_ACCOUNT_API_URL||"https://account-api-prod.zibby.app",frontendUrl:process.env.ZIBBY_PROD_FRONTEND_URL||"https://studio.zibby.app",description:"Production environment"}};function B(){let e;if(process.env.ZIBBY_API_URL)e=process.env.ZIBBY_API_URL;else{let t=process.env.ZIBBY_ENV||"prod";U[t]?e=U[t].apiUrl:e=U.prod.apiUrl}try{let t=new URL(e);return t.protocol!=="http:"&&t.protocol!=="https:"?(console.error(`\u26A0\uFE0F Invalid API URL protocol: ${t.protocol} (only http/https allowed)`),U.prod.apiUrl):e}catch{return console.error(`\u26A0\uFE0F Invalid API URL: ${e}`),U.prod.apiUrl}}F.config();async function V(e){let t=B(),n=R("Fetching projects...").start();try{let g=await fetch(`${t}/projects`,{method:"GET",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`}});g.ok||(n.fail("Failed to fetch projects"),process.exit(1));let o=await g.json();Array.isArray(o)||(o.projects&&Array.isArray(o.projects)?o=o.projects:o.data&&Array.isArray(o.data)?o=o.data:(n.fail("Unexpected response format"),process.exit(1))),(!o||o.length===0)&&(n.fail("No projects found"),process.exit(1)),n.succeed(`Found ${o.length} project${o.length===1?"":"s"}`),console.log("");let j=o.map(p=>{let k=p.projectId||p.id||p._id||"unknown";return{name:`${p.name||p.projectName||"Unnamed"} (${k})`,value:k}});return await C({message:"Select a project to deploy to:",choices:j})}catch(g){n.fail(`Error: ${g.message}`),process.exit(1)}}async function so(e,t={}){let n=process.cwd(),g=t.apiKey||process.env.ZIBBY_API_KEY;g||(console.log(`
2
+ Error: ZIBBY_API_KEY not set`),console.log(` Add to .env or use --api-key flag
3
+ `),process.exit(1));let o=".zibby/workflows";try{o=(await x(n))?.paths?.workflows||".zibby/workflows"}catch{}if(!e){let c=i(n,o);f(c)||(console.log(`
4
+ Error: No workflows found in ${o}/`),console.log(` Create one with: zibby g workflow <name>
5
+ `),process.exit(1));let{readdir:l,stat:y}=await import("fs/promises"),A=await l(c),w=[];for(let m of A){let d=i(c,m);if(!(await y(d)).isDirectory())continue;(f(i(d,"graph.mjs"))||f(i(d,"graph.js")))&&w.push(m)}w.length===0&&(console.log(`
6
+ Error: No workflows found in ${o}/`),console.log(` Create one with: zibby g workflow <name>
7
+ `),process.exit(1)),console.log(""),e=await C({message:"Select a workflow to deploy:",choices:w.map(m=>({name:m,value:m}))})}let j=t.project||process.env.ZIBBY_PROJECT_ID;j||(console.log(""),j=await V(g));let u=i(n,o,e);f(u)||(console.log(`
8
+ Error: Workflow not found: ${o}/${e}`),console.log(` Run: zibby workflow list
9
+ `),process.exit(1));let p=i(u,"graph.mjs"),k=i(u,"workflow.json");f(p)||(console.log(`
10
+ Error: graph.mjs not found in ${o}/${e}/`),process.exit(1)),console.log(`
11
+ Deploying Workflow
12
+ `),console.log(" ".padEnd(60,"-")),console.log(` Workflow: ${e}`),console.log(` Project: ${j}`),console.log(" ".padEnd(60,"-")),console.log("");let r=R("Validating workflow...").start(),I=E(p,"utf-8");if(I.includes("@zibby/core")||I.includes("@zibby/skills")){let c=i(u,"package.json");f(c)||(r.fail("Missing package.json"),console.log(""),console.log(a.red(" \u2717 graph.mjs imports @zibby packages but no package.json found")),console.log(""),console.log(a.yellow(" Create package.json with:")),console.log(""),console.log(a.gray(" {")),console.log(a.gray(' "type": "module",')),console.log(a.gray(' "dependencies": {')),console.log(a.gray(' "@zibby/core": "workspace:*"')),console.log(a.gray(" }")),console.log(a.gray(" }")),console.log(""),process.exit(1));let l=JSON.parse(E(c,"utf-8"));!{...l.dependencies,...l.devDependencies}["@zibby/core"]&&I.includes("@zibby/core")&&(r.fail("Missing @zibby/core dependency"),console.log(""),console.log(a.red(" \u2717 graph.mjs imports @zibby/core but it's not in package.json")),console.log(""),console.log(a.yellow(" Add to package.json dependencies:")),console.log(a.gray(' "@zibby/core": "workspace:*"')),console.log(""),process.exit(1))}r.text="Loading workflow graph...";try{let{pathToFileURL:c}=await import("url"),l=await import(c(p).href),y;if(l.default)if(typeof l.default=="function"){let s=l.default.prototype;s&&s.buildGraph?y=new l.default:y=l.default()}else r.fail("graph.mjs must export a class or factory function"),process.exit(1);else{let s=Object.values(l).find(b=>b.prototype&&b.prototype.buildGraph);s?y=new s:(r.fail("graph.mjs must export a WorkflowAgent class or factory function"),process.exit(1))}let w=y.buildGraph().serialize(),{readdir:m,readFile:d}=await import("fs/promises"),h={};r.text="Packaging workflow sources...";let $=await d(p,"utf-8");if(h["graph.mjs"]=$,f(k)){let s=await d(k,"utf-8");h["workflow.json"]=s}let v=i(u,"nodes");if(f(v)){let s=await m(v);for(let b of s)if(b.endsWith(".mjs")||b.endsWith(".js")){let N=i(v,b),Y=await d(N,"utf-8");h[`nodes/${b}`]=Y}}let P=i(u,"package.json");if(f(P)){let s=await d(P,"utf-8");h["package.json"]=s,r.text="Including package.json for dependencies..."}let z=i(u,"package-lock.json");if(f(z)){let s=await d(z,"utf-8");h["package-lock.json"]=s}r.text=`Uploading to cloud (${w.nodes.size||Object.keys(w.nodes||{}).length} nodes, ${Object.keys(h).length} files)...`;let L=B(),_=await fetch(`${L}/projects/${j}/workflows/${e}`,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${g}`},body:JSON.stringify({graph:w,sources:h,isDefault:!1})});if(!_.ok){let s=await _.json().catch(()=>({}));r.fail("Deploy failed"),console.log(` Error: ${s.message||_.statusText}
13
+ `),process.exit(1)}let D=await _.json();r.succeed(`Deployed successfully (v${D.version||1})`),console.log(""),console.log(" Next steps:"),console.log(` zibby start ${e} Run locally`),console.log(" zibby workflow list View all workflows"),console.log("")}catch(c){r.fail("Deploy failed"),console.log(` Error: ${c.message}
14
+ `),process.exit(1)}}export{so as deployWorkflowCommand};