runhuman 1.0.1 → 1.0.3

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 (2) hide show
  1. package/dist/index.js +105 -99
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,80 +1,85 @@
1
1
  #!/usr/bin/env node
2
- var Oe=Object.defineProperty;var De=(r=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(r,{get:(t,e)=>(typeof require<"u"?require:t)[e]}):r)(function(r){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+r+'" is not supported')});var H=(r,t)=>()=>(r&&(t=r(r=0)),t);var Ee=(r,t)=>{for(var e in t)Oe(r,e,{get:t[e],enumerable:!0})};function Ne(r){return r instanceof S}function p(r){return Ne(r)?{message:r.message,exitCode:r.exitCode,details:r.details}:r instanceof Error?{message:r.message,exitCode:1}:{message:String(r),exitCode:1}}var S,q,_,N,z,w=H(()=>{"use strict";S=class extends Error{constructor(e,n=1,o){super(e);this.exitCode=n;this.details=o;this.name="CliError"}},q=class extends S{constructor(t="Authentication failed",e){super(t,2,e),this.name="AuthenticationError"}},_=class extends S{constructor(t="Resource not found",e){super(t,3,e),this.name="NotFoundError"}},N=class extends S{constructor(t="Validation failed",e){super(t,4,e),this.name="ValidationError"}},z=class extends S{constructor(t="Operation timed out",e){super(t,5,e),this.name="TimeoutError"}}});import ve from"axios";var d,I=H(()=>{"use strict";w();d=class{client;config;constructor(t){this.config=t,this.client=ve.create({baseURL:t.apiUrl||"https://runhuman.com",timeout:3e4,headers:{"Content-Type":"application/json"}}),this.client.interceptors.request.use(e=>(this.config.apiKey&&(e.headers.Authorization=`Bearer ${this.config.apiKey}`),e)),this.client.interceptors.response.use(e=>e,e=>{throw this.handleError(e)})}handleError(t){if(t.response){let e=t.response.status,n=t.response.data,o=n?.error||n?.message||t.message;switch(e){case 401:case 403:return new q(o,n);case 404:return new _(o,n);case 400:case 422:return new N(o,n);default:return new S(o,1,n)}}return t.code==="ECONNABORTED"?new S("Request timeout",5):t.code==="ENOTFOUND"||t.code==="ECONNREFUSED"?new S("Cannot connect to Runhuman API",1):new S(t.message,1)}async createJob(t){return(await this.client.post("/jobs",t)).data}async getJob(t){return(await this.client.get(`/job/${t}`)).data}async listJobs(t){return(await this.client.get("/jobs",{params:t})).data}async cancelJob(t){await this.client.post(`/job/${t}/cancel`)}async deleteJob(t){await this.client.delete(`/job/${t}`)}async listProjects(t){return(await this.client.get("/projects",{params:t})).data}async getProject(t){return(await this.client.get(`/projects/${t}`)).data}async createProject(t){return(await this.client.post("/projects",t)).data}async updateProject(t,e){return(await this.client.put(`/projects/${t}`,e)).data}async deleteProject(t){await this.client.delete(`/projects/${t}`)}async listApiKeys(t){return(await this.client.get(`/projects/${t}/keys`)).data}async createApiKey(t,e){return(await this.client.post(`/projects/${t}/keys`,{name:e})).data}async deleteApiKey(t){await this.client.delete(`/keys/${t}`)}async listTemplates(t){return(await this.client.get(`/projects/${t}/templates`)).data}async getTemplate(t){return(await this.client.get(`/templates/${t}`)).data}async createTemplate(t,e){return(await this.client.post(`/projects/${t}/templates`,e)).data}async updateTemplate(t,e){return(await this.client.put(`/templates/${t}`,e)).data}async deleteTemplate(t){await this.client.delete(`/templates/${t}`)}async getCurrentUser(){return(await this.client.get("/auth/me")).data}async getTokenBalance(){return(await this.client.get("/auth/balance")).data}async linkGithubRepo(t,e,n){return(await this.client.post(`/projects/${t}/github/link`,{owner:e,repo:n})).data}async listGithubRepos(t){let e=t?{projectId:t}:void 0;return(await this.client.get("/github/repos",{params:e})).data}async listGithubIssues(t,e,n){return(await this.client.get(`/github/${t}/${e}/issues`,{params:n})).data}async getGithubIssue(t,e,n){return(await this.client.get(`/github/${t}/${e}/issues/${n}`)).data}}});import{cosmiconfig as Te}from"cosmiconfig";import{homedir as Me}from"os";import{join as O}from"path";import{readFileSync as $,writeFileSync as x,existsSync as A,mkdirSync as Y,chmodSync as Je}from"fs";function Ke(r){return Le.includes(r)}var $e,R,v,L,Le,m,b=H(()=>{"use strict";$e="runhuman",R=O(Me(),".config","runhuman"),v=O(R,"config.json"),L=O(R,"credentials.json"),Le=["pretty","json","compact"];m=class{constructor(t=process.cwd()){this.cwd=t}projectConfig=null;globalConfig=null;envConfig=null;async loadConfig(t={}){return this.envConfig=this.loadEnvConfig(),this.projectConfig=await this.loadProjectConfig(),this.globalConfig=this.loadGlobalConfig(),{...this.getDefaults(),...this.globalConfig,...this.projectConfig,...this.envConfig,...t}}getDefaults(){return{apiUrl:"https://runhuman.com",outputFormat:"pretty",color:!0,autoOpenBrowser:!0,defaultDuration:5,defaultScreenSize:"desktop"}}loadEnvConfig(){let t={};return process.env.RUNHUMAN_API_KEY&&(t.apiKey=process.env.RUNHUMAN_API_KEY),process.env.RUNHUMAN_API_URL&&(t.apiUrl=process.env.RUNHUMAN_API_URL),process.env.RUNHUMAN_PROJECT&&(t.project=process.env.RUNHUMAN_PROJECT),process.env.RUNHUMAN_DEFAULT_URL&&(t.defaultUrl=process.env.RUNHUMAN_DEFAULT_URL),process.env.RUNHUMAN_DEFAULT_DURATION&&(t.defaultDuration=parseInt(process.env.RUNHUMAN_DEFAULT_DURATION,10)),process.env.RUNHUMAN_DEFAULT_SCREEN_SIZE&&(t.defaultScreenSize=process.env.RUNHUMAN_DEFAULT_SCREEN_SIZE),process.env.RUNHUMAN_OUTPUT_FORMAT&&Ke(process.env.RUNHUMAN_OUTPUT_FORMAT)&&(t.outputFormat=process.env.RUNHUMAN_OUTPUT_FORMAT),process.env.RUNHUMAN_NO_COLOR==="1"&&(t.color=!1),t}async loadProjectConfig(){try{return(await Te($e).search(this.cwd))?.config||null}catch{return null}}loadGlobalConfig(){try{if(!A(v))return null;let t=$(v,"utf-8");return JSON.parse(t)}catch{return null}}async get(t){return(await this.loadConfig())[t]}async set(t,e,n=!1){n?await this.setGlobalConfig(t,e):await this.setProjectConfig(t,e)}async setGlobalConfig(t,e){A(R)||Y(R,{recursive:!0});let n={};if(A(v)){let o=$(v,"utf-8");n=JSON.parse(o)}n[t]=e,x(v,JSON.stringify(n,null,2))}async setProjectConfig(t,e){let n=O(this.cwd,".runhumanrc"),o={};if(A(n)){let s=$(n,"utf-8");o=JSON.parse(s)}o[t]=e,x(n,JSON.stringify(o,null,2))}async saveProjectConfig(t){let e=O(this.cwd,".runhumanrc"),n={};if(A(e)){let s=$(e,"utf-8");n=JSON.parse(s)}let o={...n,...t};x(e,JSON.stringify(o,null,2))}async list(){let t=await this.loadConfig();return{global:this.globalConfig,project:this.projectConfig,env:this.envConfig,effective:t}}async reset(t){if((t==="global"||t==="all")&&A(v)&&x(v,"{}"),t==="project"||t==="all"){let e=O(this.cwd,".runhumanrc");A(e)&&x(e,"{}")}}saveCredentials(t){A(R)||Y(R,{recursive:!0}),x(L,JSON.stringify(t,null,2));try{process.platform!=="win32"&&Je(L,384)}catch{}}loadCredentials(){try{if(!A(L))return null;let t=$(L,"utf-8");return JSON.parse(t)}catch{return null}}clearCredentials(){A(L)&&x(L,"{}")}saveUserInfo(t){let e=O(R,"user.json");A(R)||Y(R,{recursive:!0}),x(e,JSON.stringify(t,null,2))}loadUserInfo(){try{let t=O(R,"user.json");if(!A(t))return null;let e=$(t,"utf-8");return JSON.parse(e)}catch{return null}}clearUserInfo(){let t=O(R,"user.json");A(t)&&x(t,"{}")}}});import T from"chalk";import Fe from"cli-table3";var a,j=H(()=>{"use strict";a=class{constructor(t={}){this.options=t}output(t){this.options.json?console.log(JSON.stringify(t,null,2)):t.success?t.data&&!this.options.quiet&&this.outputPretty(t.data):this.outputError(t.error?.message||"An error occurred")}outputPretty(t){typeof t=="string"?console.log(t):(Array.isArray(t),console.log(JSON.stringify(t,null,2)))}outputError(t,e){if(this.options.json){let n={success:!1,error:{message:t,details:e},timestamp:new Date().toISOString()};console.error(JSON.stringify(n,null,2))}else console.error(this.color("red",`
2
+ var st=Object.defineProperty;var it=(s=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(s,{get:(t,e)=>(typeof require<"u"?require:t)[e]}):s)(function(s){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+s+'" is not supported')});var b=(s,t)=>()=>(s&&(t=s(s=0)),t);var at=(s,t)=>{for(var e in t)st(s,e,{get:t[e],enumerable:!0})};function ct(s){return s instanceof R}function m(s){return ct(s)?{message:s.message,exitCode:s.exitCode,details:s.details}:s instanceof Error?{message:s.message,exitCode:1}:{message:String(s),exitCode:1}}var R,W,q,D,V,I=b(()=>{"use strict";R=class extends Error{constructor(e,n=1,o){super(e);this.exitCode=n;this.details=o;this.name="CliError"}},W=class extends R{constructor(t="Authentication failed",e){super(t,2,e),this.name="AuthenticationError"}},q=class extends R{constructor(t="Resource not found",e){super(t,3,e),this.name="NotFoundError"}},D=class extends R{constructor(t="Validation failed",e){super(t,4,e),this.name="ValidationError"}},V=class extends R{constructor(t="Operation timed out",e){super(t,5,e),this.name="TimeoutError"}}});var ie=b(()=>{"use strict"});var ee=b(()=>{"use strict"});var ae=b(()=>{"use strict"});var pe=b(()=>{"use strict";ie();ee();ae()});var me=b(()=>{"use strict"});var de=b(()=>{"use strict";me()});var ge=b(()=>{"use strict"});var fe=b(()=>{"use strict";ee()});var he=b(()=>{"use strict";ge();fe()});function p(s){return{pattern:s,build:e=>{if(!e)return s;let n=s;for(let[o,r]of Object.entries(e))r!==void 0&&(n=n.replace(`:${o}?`,r),n=n.replace(`:${o}`,r));return n=n.replace(/\/:[^/]+\?/g,""),n=n.replace(/\/+/g,"/"),n.length>1&&n.endsWith("/")&&(n=n.slice(0,-1)),n}}}var B=b(()=>{"use strict"});var ye,be=b(()=>{"use strict";B();ye={dashboard:p("/dashboard"),onboarding:p("/dashboard/onboarding"),projects:p("/dashboard/projects"),usage:p("/dashboard/usage"),managePlan:p("/dashboard/manage-plan"),admin:p("/dashboard/admin"),settings:p("/dashboard/settings"),settingsAccount:p("/dashboard/settings/account"),settingsGitHub:p("/dashboard/settings/github"),settingsApiKeys:p("/dashboard/settings/api-keys"),tester:p("/tester"),testerJobs:p("/tester/jobs"),testerSettings:p("/tester/settings"),project:p("/dashboard/:projectId"),playground:p("/dashboard/:projectId/playground"),jobs:p("/dashboard/:projectId/jobs"),job:p("/dashboard/:projectId/jobs/:jobId"),templates:p("/dashboard/:projectId/templates"),template:p("/dashboard/:projectId/templates/:templateId"),issues:p("/dashboard/:projectId/issues"),issue:p("/dashboard/:projectId/issues/:issueNumber"),issueSessions:p("/dashboard/:projectId/issue-sessions"),issueSession:p("/dashboard/:projectId/issue-sessions/:sessionId"),projectSettings:p("/dashboard/:projectId/settings"),quickStart:p("/start"),publicJob:p("/j/:jobId/:token"),docs:p("/docs/quick-start"),pricing:p("/pricing"),home:p("/")}});var w,we=b(()=>{"use strict";B();w={jobs:p("/jobs"),job:p("/jobs/:jobId"),jobStatus:p("/jobs/:jobId/status"),run:p("/run"),publicJob:p("/public/jobs/:jobId/:token"),keys:p("/keys"),key:p("/keys/:keyId"),keyRevoke:p("/keys/:keyId/revoke"),projects:p("/projects"),project:p("/projects/:projectId"),projectJobs:p("/projects/:projectId/jobs"),projectApiKeys:p("/projects/:projectId/api-keys"),projectTemplates:p("/projects/:projectId/templates"),projectTemplate:p("/projects/:projectId/templates/:templateId"),bulkCreateProjects:p("/projects/bulk"),githubOAuthAuthorize:p("/github/oauth/authorize"),githubCallback:p("/github/auth/callback"),githubInstallations:p("/github/installations"),githubInstallation:p("/github/installations/:installationId"),githubInstallationRefresh:p("/github/installations/:installationId/refresh"),githubRepos:p("/github/repos"),githubRepoCheckAccess:p("/github/repos/check-access"),githubRepoFindUrl:p("/github/repos/find-url"),githubIssuesByRepo:p("/github/issues/:owner/:repo"),githubIssues:p("/github/issues"),githubIssue:p("/github/issues/:issueNumber"),githubIssueComments:p("/github/issues/:issueNumber/comments"),githubIssueLabels:p("/github/issues/labels"),githubIssueAssignees:p("/github/issues/assignees"),githubIssueTest:p("/github/issues/test"),githubIssuesBulkTest:p("/github/issues/bulk-test"),githubTestSessions:p("/github/issues/test-sessions"),githubTestSession:p("/github/issues/test-sessions/:sessionId"),githubTestSessionSeen:p("/github/issues/test-sessions/:sessionId/seen"),githubTestSessionsCounts:p("/github/issues/test-sessions/counts"),githubBulkTest:p("/github/bulk-test"),githubWebhooks:p("/github/webhooks"),authSync:p("/auth/sync"),authMe:p("/auth/me"),authStartup:p("/auth/startup"),authDeleteAccount:p("/auth/account"),tokensBalance:p("/tokens/balance"),tokensTransactions:p("/tokens/transactions"),tokensContact:p("/tokens/contact"),health:p("/health"),templates:p("/templates"),issueAnalyzer:p("/issue-analyzer"),prAnalyzer:p("/pr-analyzer"),logs:p("/logs"),relevantIssuesDiscover:p("/relevant-issues/discover"),relevantIssuesLatest:p("/relevant-issues/latest"),onboarding:p("/onboarding"),onboardingComplete:p("/onboarding/complete"),onboardingCheck:p("/onboarding/check"),search:p("/search"),testerApply:p("/tester/apply"),testerProfile:p("/tester/profile"),testerPublicProfile:p("/testers/:testerId/profile"),repoTemplates:p("/repos/:owner/:repo/templates"),repoTemplate:p("/repos/:owner/:repo/templates/:templateName")}});var je=b(()=>{"use strict";B();be();we()});var Ce=b(()=>{"use strict"});var Ie=b(()=>{"use strict"});var Se=b(()=>{"use strict";Ce();Ie()});var Ae=b(()=>{"use strict"});var ke=b(()=>{"use strict"});var Ue=b(()=>{"use strict";Ae();ke()});var xe=b(()=>{"use strict"});var Pe=b(()=>{"use strict"});var Te=b(()=>{"use strict";xe();Pe()});var Re=b(()=>{"use strict";pe();de();he();je();Se();Ue();Te()});import Lt from"axios";var g,x=b(()=>{"use strict";I();Re();g=class{client;config;constructor(t){if(this.config=t,!t.apiUrl)throw new Error("apiUrl is required in CLI config");this.client=Lt.create({baseURL:t.apiUrl+"/api",timeout:3e4,headers:{"Content-Type":"application/json"}}),this.client.interceptors.request.use(e=>(this.config.apiKey&&(e.headers.Authorization=`Bearer ${this.config.apiKey}`),e)),this.client.interceptors.response.use(e=>e,e=>{throw this.handleError(e)})}handleError(t){if(t.response){let e=t.response.status,n=t.response.data,o=n?.error||n?.message||t.message;switch(e){case 401:case 403:return new W(o,n);case 404:return new q(o,n);case 400:case 422:return new D(o,n);default:return new R(o,1,n)}}return t.code==="ECONNABORTED"?new R("Request timeout",5):t.code==="ENOTFOUND"||t.code==="ECONNREFUSED"?new R("Cannot connect to Runhuman API",1):new R(t.message,1)}async createJob(t){return(await this.client.post(w.jobs.build(),t)).data}async getJob(t){return(await this.client.get(w.jobStatus.build({jobId:t}))).data}async listJobs(t){return(await this.client.get(w.jobs.build(),{params:t})).data}async cancelJob(t){await this.client.post(`${w.job.build({jobId:t})}/cancel`)}async deleteJob(t){await this.client.delete(w.job.build({jobId:t}))}async listProjects(t){return(await this.client.get(w.projects.build(),{params:t})).data}async getProject(t){return(await this.client.get(w.project.build({projectId:t}))).data}async createProject(t){return(await this.client.post(w.projects.build(),t)).data}async updateProject(t,e){return(await this.client.put(w.project.build({projectId:t}),e)).data}async deleteProject(t){await this.client.delete(w.project.build({projectId:t}))}async listApiKeys(t){return(await this.client.get(w.projectApiKeys.build({projectId:t}))).data}async createApiKey(t,e){return(await this.client.post(w.projectApiKeys.build({projectId:t}),{name:e})).data}async deleteApiKey(t){await this.client.delete(w.key.build({keyId:t}))}async listTemplates(t){return(await this.client.get(w.projectTemplates.build({projectId:t}))).data}async getTemplate(t,e){return(await this.client.get(w.projectTemplate.build({projectId:t,templateId:e}))).data}async createTemplate(t,e){return(await this.client.post(w.projectTemplates.build({projectId:t}),e)).data}async updateTemplate(t,e,n){return(await this.client.put(w.projectTemplate.build({projectId:t,templateId:e}),n)).data}async deleteTemplate(t,e){await this.client.delete(w.projectTemplate.build({projectId:t,templateId:e}))}async getCurrentUser(){return(await this.client.get(w.authMe.build())).data}async getTokenBalance(){return(await this.client.get(w.tokensBalance.build())).data}async linkGithubRepo(t,e,n){return(await this.client.post(`${w.project.build({projectId:t})}/github/link`,{owner:e,repo:n})).data}async listGithubRepos(t){let e=t?{projectId:t}:void 0;return(await this.client.get(w.githubRepos.build(),{params:e})).data}async listGithubIssues(t,e,n){return(await this.client.get(w.githubIssuesByRepo.build({owner:t,repo:e}),{params:n})).data}async getGithubIssue(t,e,n){return(await this.client.get(`${w.githubIssuesByRepo.build({owner:t,repo:e})}/${n}`)).data}}});import{cosmiconfig as Jt}from"cosmiconfig";import{homedir as _t}from"os";import{join as v}from"path";import{readFileSync as F,writeFileSync as O,existsSync as P,mkdirSync as te,chmodSync as Ft}from"fs";function zt(s){return Ht.includes(s)}var Kt,E,M,K,Ht,d,k=b(()=>{"use strict";Kt="runhuman",E=v(_t(),".config","runhuman"),M=v(E,"config.json"),K=v(E,"credentials.json"),Ht=["pretty","json","compact"];d=class{constructor(t=process.cwd()){this.cwd=t}projectConfig=null;globalConfig=null;envConfig=null;async loadConfig(t={}){this.envConfig=this.loadEnvConfig(),this.projectConfig=await this.loadProjectConfig(),this.globalConfig=this.loadGlobalConfig();let e=this.loadCredentials(),n=e?.accessToken?{apiKey:e.accessToken}:{},o=Object.fromEntries(Object.entries(t).filter(([,l])=>l!==void 0));return{...this.getDefaults(),...this.globalConfig,...this.projectConfig,...n,...this.envConfig,...o}}getDefaults(){return{apiUrl:"https://runhuman.com",outputFormat:"pretty",color:!0,autoOpenBrowser:!0,defaultDuration:5,defaultScreenSize:"desktop"}}loadEnvConfig(){let t={};return process.env.RUNHUMAN_API_KEY&&(t.apiKey=process.env.RUNHUMAN_API_KEY),process.env.RUNHUMAN_API_URL&&(t.apiUrl=process.env.RUNHUMAN_API_URL),process.env.RUNHUMAN_PROJECT&&(t.project=process.env.RUNHUMAN_PROJECT),process.env.RUNHUMAN_DEFAULT_URL&&(t.defaultUrl=process.env.RUNHUMAN_DEFAULT_URL),process.env.RUNHUMAN_DEFAULT_DURATION&&(t.defaultDuration=parseInt(process.env.RUNHUMAN_DEFAULT_DURATION,10)),process.env.RUNHUMAN_DEFAULT_SCREEN_SIZE&&(t.defaultScreenSize=process.env.RUNHUMAN_DEFAULT_SCREEN_SIZE),process.env.RUNHUMAN_OUTPUT_FORMAT&&zt(process.env.RUNHUMAN_OUTPUT_FORMAT)&&(t.outputFormat=process.env.RUNHUMAN_OUTPUT_FORMAT),process.env.RUNHUMAN_NO_COLOR==="1"&&(t.color=!1),t}async loadProjectConfig(){try{return(await Jt(Kt).search(this.cwd))?.config||null}catch{return null}}loadGlobalConfig(){try{if(!P(M))return null;let t=F(M,"utf-8");return JSON.parse(t)}catch{return null}}async get(t){return(await this.loadConfig())[t]}async set(t,e,n=!1){n?await this.setGlobalConfig(t,e):await this.setProjectConfig(t,e)}async setGlobalConfig(t,e){P(E)||te(E,{recursive:!0});let n={};if(P(M)){let o=F(M,"utf-8");n=JSON.parse(o)}n[t]=e,O(M,JSON.stringify(n,null,2))}async setProjectConfig(t,e){let n=v(this.cwd,".runhumanrc"),o={};if(P(n)){let r=F(n,"utf-8");o=JSON.parse(r)}o[t]=e,O(n,JSON.stringify(o,null,2))}async saveProjectConfig(t){let e=v(this.cwd,".runhumanrc"),n={};if(P(e)){let r=F(e,"utf-8");n=JSON.parse(r)}let o={...n,...t};O(e,JSON.stringify(o,null,2))}async list(){let t=await this.loadConfig();return{global:this.globalConfig,project:this.projectConfig,env:this.envConfig,effective:t}}async reset(t){if((t==="global"||t==="all")&&P(M)&&O(M,"{}"),t==="project"||t==="all"){let e=v(this.cwd,".runhumanrc");P(e)&&O(e,"{}")}}saveCredentials(t){P(E)||te(E,{recursive:!0}),O(K,JSON.stringify(t,null,2));try{process.platform!=="win32"&&Ft(K,384)}catch{}}loadCredentials(){try{if(!P(K))return null;let t=F(K,"utf-8");return JSON.parse(t)}catch{return null}}clearCredentials(){P(K)&&O(K,"{}")}saveUserInfo(t){let e=v(E,"user.json");P(E)||te(E,{recursive:!0}),O(e,JSON.stringify(t,null,2))}loadUserInfo(){try{let t=v(E,"user.json");if(!P(t))return null;let e=F(t,"utf-8");return JSON.parse(e)}catch{return null}}clearUserInfo(){let t=v(E,"user.json");P(t)&&O(t,"{}")}}});import L from"chalk";import Gt from"cli-table3";var a,S=b(()=>{"use strict";a=class{constructor(t={}){this.options=t}output(t){this.options.json?console.log(JSON.stringify(t,null,2)):t.success?t.data&&!this.options.quiet&&this.outputPretty(t.data):this.outputError(t.error?.message||"An error occurred")}outputPretty(t){typeof t=="string"?console.log(t):(Array.isArray(t),console.log(JSON.stringify(t,null,2)))}outputError(t,e){if(this.options.json){let n={success:!1,error:{message:t,details:e},timestamp:new Date().toISOString()};console.error(JSON.stringify(n,null,2))}else console.error(this.color("red",`
3
3
  Error: ${t}
4
4
  `)),e&&console.error(this.color("gray",JSON.stringify(e,null,2)))}success(t){!this.options.json&&!this.options.quiet&&console.log(this.color("green",`
5
5
  ${t}
6
6
  `))}info(t){!this.options.json&&!this.options.quiet&&console.log(this.color("blue",t))}warn(t){!this.options.json&&!this.options.quiet&&console.warn(this.color("yellow",`Warning: ${t}`))}formatJobList(t){if(this.options.format==="compact")return t.map(n=>`${n.id} ${n.status} ${n.url}`).join(`
7
- `);let e=new Fe({head:["Job ID","Status","URL","Created","Duration","Cost"].map(n=>this.color("cyan",n)),colWidths:[15,12,30,14,10,10]});return t.forEach(n=>{e.push([n.id,this.formatStatus(n.status),this.truncate(n.url,28),this.formatDate(n.createdAt),n.testDurationSeconds?this.formatDuration(n.testDurationSeconds):"-",n.costUsd?`$${n.costUsd.toFixed(3)}`:"-"])}),e.toString()}formatStatus(t){let e={pending:"yellow",waiting:"blue",working:"blue",creating_issues:"cyan",completed:"green",incomplete:"yellow",abandoned:"red",rejected:"gray",error:"red",failed:"red"},n=t.replace("_"," ");return this.color(e[t]||"white",n)}formatDuration(t){if(t<60)return`${t}s`;let e=Math.floor(t/60),n=t%60;if(e<60)return`${e}m ${n}s`;let o=Math.floor(e/60),s=e%60;return`${o}h ${s}m ${n}s`}formatDate(t){let e=new Date(t),o=new Date().getTime()-e.getTime(),s=Math.floor(o/1e3),u=Math.floor(s/60),i=Math.floor(u/60),c=Math.floor(i/24);return s<60?`${s}s ago`:u<60?`${u}m ago`:i<24?`${i}h ago`:c<30?`${c}d ago`:e.toLocaleDateString()}truncate(t,e){return t.length<=e?t:t.substring(0,e-3)+"..."}color(t,e){if(this.options.color===!1)return e;let o={red:T.red,green:T.green,blue:T.blue,yellow:T.yellow,cyan:T.cyan,gray:T.gray,white:T.white}[t];return o?o(e):e}static result(t){return{success:!0,data:t,timestamp:new Date().toISOString()}}static error(t,e,n){return{success:!1,error:{message:t,code:e,details:n},timestamp:new Date().toISOString()}}}});var X={};Ee(X,{waitCommand:()=>Z,waitForJob:()=>oe});import{Command as He}from"commander";import Ge from"ora";async function oe(r,t,e,n=600){let o=Date.now(),s=n*1e3,u=1e4,i=null;for(e.options.json||(i=Ge("\u23F3 Waiting for job completion...").start());;){let c=Date.now()-o;if(c>=s)throw i&&i.fail("Timeout waiting for job completion"),new z(`Job did not complete within ${n} seconds`);let l=await t.getJob(r);if(i){let g=e.formatDuration(Math.floor(c/1e3));i.text=`\u23F3 Waiting for job completion... (${g} elapsed, status: ${l.status})`}if(l.status==="completed"){if(i&&i.succeed("\u2705 Test Completed!"),!e.options.json){if(console.log(`
8
- Duration: `+(l.testDurationSeconds?e.formatDuration(l.testDurationSeconds):"N/A")),console.log(" Cost: $"+(l.costUsd||0).toFixed(3)),l.testerName&&console.log(" Tester: "+l.testerName),l.result){console.log(`
9
- \u{1F4CB} Results Summary:`);for(let[g,f]of Object.entries(l.result))console.log(` ${g}: ${f}`)}console.log(`
10
- \u{1F4BE} Full results:`),console.log(` runhuman results ${r}
11
- `)}return}if(l.status==="error"||l.status==="incomplete"||l.status==="abandoned")throw i&&i.fail(`Test ${l.status}`),new Error(`Job ${l.status}: ${l.testerResponse||"No details available"}`);if(l.status==="rejected")throw i&&i.fail("Test was rejected"),new Error("Job was rejected");await new Promise(g=>setTimeout(g,u))}}function Z(){let r=new He("wait");return r.description("Wait for a job to complete and display results").argument("<jobId>","Job ID to wait for").option("--timeout <seconds>","Max wait time (default: 600)",parseInt).option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=new a({json:e.json,color:o.color}),u=new d(o);if(await oe(t,u,s,e.timeout||600),e.json){let i=await u.getJob(t),c=a.result(i);s.output(c)}}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r}var W=H(()=>{"use strict";I();b();j();w()});import{Command as vt}from"commander";I();b();j();w();import{Command as qe}from"commander";import _e from"ora";function ne(){let r=new qe("create");return r.description("Create a new QA test job").argument("[url]","URL to test").option("-d, --description <text>","Test instructions for the human tester").option("-t, --template <name>","Use a template as base configuration").option("--duration <minutes>","Target duration (1-60 minutes)",parseInt).option("--screen-size <preset>","Screen size: desktop|laptop|tablet|mobile").option("--schema <file>","Path to JSON schema file for structured output").option("--schema-inline <json>","Inline JSON schema").option("--metadata <json>","Metadata for tracking (JSON string)").option("--github-repo <owner/repo>","GitHub repo for context").option("--create-issues","Auto-create GitHub issues from findings").option("--sync","Wait for result before exiting (synchronous mode)").option("--wait <seconds>","Max wait time in sync mode (default: 300)",parseInt).option("--project <id>","Project ID (or use default from config)").option("--api-key <key>","API key (or use from config/env)").option("--json","Output as JSON (for scripting)").option("--quiet","Minimal output (only job ID)").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,project:e.project}),s=new a({json:e.json,quiet:e.quiet,color:o.color}),u=new d(o);if(!t&&!e.template&&!o.defaultUrl)throw new N("URL is required (provide as argument, via --template, or set defaultUrl in config)");if(!e.description&&!e.template)throw new N("Description is required (use -d flag or --template)");let i={url:t||o.defaultUrl||"",description:e.description||"",duration:e.duration||o.defaultDuration,screenSize:e.screenSize||o.defaultScreenSize};if(e.schema){let f=await(await import("fs/promises")).readFile(e.schema,"utf-8");i.schema=JSON.parse(f)}else e.schemaInline&&(i.schema=JSON.parse(e.schemaInline));e.metadata&&(i.metadata=JSON.parse(e.metadata)),e.githubRepo&&(i.githubRepo=e.githubRepo),e.createIssues&&(i.createIssues=!0),e.template&&(i.templateId=e.template);let c=e.json?null:_e("Creating QA test job...").start(),l=await u.createJob(i);if(c&&c.succeed("Job created successfully!"),e.json){let g=a.result({jobId:l.jobId,status:l.status,message:l.message,dashboardUrl:`${o.apiUrl}/jobs/${l.jobId}`,estimatedCompletionTime:l.estimatedCompletionTime});s.output(g)}else e.quiet?console.log(l.jobId):(console.log(`
12
- `+"=".repeat(60)),console.log(" Job ID: "+l.jobId),console.log(" Status: "+l.status),console.log(` Dashboard: ${o.apiUrl}/jobs/${l.jobId}`),console.log("=".repeat(60)+`
13
- `),console.log("\u{1F4A1} Track progress:"),console.log(` runhuman status ${l.jobId}`),console.log(` runhuman wait ${l.jobId}
14
- `));if(e.sync){let{waitForJob:g}=await Promise.resolve().then(()=>(W(),X));await g(l.jobId,u,s,e.wait||300)}}catch(n){let o=p(n);new a({json:e.json,quiet:!1}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r}I();b();j();w();import{Command as ze}from"commander";function re(){let r=new ze("status");return r.description("Check the current status of a job").argument("<jobId>","Job ID to check").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=new a({json:e.json,color:o.color}),i=await new d(o).getJob(t);if(e.json){let c=a.result(i);s.output(c)}else console.log(`
7
+ `);let e=new Gt({head:["Job ID","Status","URL","Created","Duration","Cost"].map(n=>this.color("cyan",n)),colWidths:[15,12,30,14,10,10]});return t.forEach(n=>{e.push([n.id,this.formatStatus(n.status),this.truncate(n.url,28),this.formatDate(n.createdAt),n.testDurationSeconds?this.formatDuration(n.testDurationSeconds):"-",n.costUsd?`$${n.costUsd.toFixed(3)}`:"-"])}),e.toString()}formatStatus(t){let e={pending:"yellow",waiting:"blue",working:"blue",creating_issues:"cyan",completed:"green",incomplete:"yellow",abandoned:"red",rejected:"gray",error:"red",failed:"red"},n=t.replace("_"," ");return this.color(e[t]||"white",n)}formatDuration(t){if(t<60)return`${t}s`;let e=Math.floor(t/60),n=t%60;if(e<60)return`${e}m ${n}s`;let o=Math.floor(e/60),r=e%60;return`${o}h ${r}m ${n}s`}formatDate(t){let e=new Date(t),o=new Date().getTime()-e.getTime(),r=Math.floor(o/1e3),l=Math.floor(r/60),i=Math.floor(l/60),c=Math.floor(i/24);return r<60?`${r}s ago`:l<60?`${l}m ago`:i<24?`${i}h ago`:c<30?`${c}d ago`:e.toLocaleDateString()}truncate(t,e){return t.length<=e?t:t.substring(0,e-3)+"..."}color(t,e){if(this.options.color===!1)return e;let o={red:L.red,green:L.green,blue:L.blue,yellow:L.yellow,cyan:L.cyan,gray:L.gray,white:L.white}[t];return o?o(e):e}static result(t){return{success:!0,data:t,timestamp:new Date().toISOString()}}static error(t,e,n){return{success:!1,error:{message:t,code:e,details:n},timestamp:new Date().toISOString()}}}});var ne={};at(ne,{waitCommand:()=>oe,waitForJob:()=>Ee});import{Command as Wt}from"commander";import qt from"ora";async function Ee(s,t,e,n=600){let o=Date.now(),r=n*1e3,l=1e4,i=null;for(e.options.json||(i=qt("\u23F3 Waiting for job completion...").start());;){let c=Date.now()-o;if(c>=r)throw i&&i.fail("Timeout waiting for job completion"),new V(`Job did not complete within ${n} seconds`);let u=await t.getJob(s);if(i){let f=e.formatDuration(Math.floor(c/1e3));i.text=`\u23F3 Waiting for job completion... (${f} elapsed, status: ${u.status})`}if(u.status==="completed"){if(i&&i.succeed("\u2705 Test Completed!"),!e.options.json){if(console.log(`
8
+ Duration: `+(u.testDurationSeconds?e.formatDuration(u.testDurationSeconds):"N/A")),console.log(" Cost: $"+(u.costUsd||0).toFixed(3)),u.testerName&&console.log(" Tester: "+u.testerName),u.result){console.log(`
9
+ \u{1F4CB} Results Summary:`);for(let[f,h]of Object.entries(u.result))console.log(` ${f}: ${h}`)}console.log(`
10
+ \u{1F4BE} Full results:`),console.log(` runhuman results ${s}
11
+ `)}return}if(u.status==="error"||u.status==="incomplete"||u.status==="abandoned")throw i&&i.fail(`Test ${u.status}`),new Error(`Job ${u.status}: ${u.testerResponse||"No details available"}`);if(u.status==="rejected")throw i&&i.fail("Test was rejected"),new Error("Job was rejected");await new Promise(f=>setTimeout(f,l))}}function oe(){let s=new Wt("wait");return s.description("Wait for a job to complete and display results").argument("<jobId>","Job ID to wait for").option("--timeout <seconds>","Max wait time (default: 600)",parseInt).option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new a({json:e.json,color:o.color}),l=new g(o);if(await Ee(t,l,r,e.timeout||600),e.json){let i=await l.getJob(t),c=a.result(i);r.output(c)}}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}var Q=b(()=>{"use strict";x();k();S();I()});import{Command as Jo}from"commander";x();k();S();I();import{Command as Vt}from"commander";import Bt from"ora";function Oe(){let s=new Vt("create");return s.description("Create a new QA test job").argument("[url]","URL to test").option("-d, --description <text>","Test instructions for the human tester").option("-t, --template <name>","Use a template as base configuration").option("--duration <minutes>","Target duration (1-60 minutes)",parseInt).option("--screen-size <preset>","Screen size: desktop|laptop|tablet|mobile").option("--schema <file>","Path to JSON schema file for structured output").option("--schema-inline <json>","Inline JSON schema").option("--metadata <json>","Metadata for tracking (JSON string)").option("--github-repo <owner/repo>","GitHub repo for context").option("--create-issues","Auto-create GitHub issues from findings").option("--sync","Wait for result before exiting (synchronous mode)").option("--wait <seconds>","Max wait time in sync mode (default: 300)",parseInt).option("--project <id>","Project ID (or use default from config)").option("--api-key <key>","API key (or use from config/env)").option("--json","Output as JSON (for scripting)").option("--quiet","Minimal output (only job ID)").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,project:e.project}),r=new a({json:e.json,quiet:e.quiet,color:o.color}),l=new g(o);if(!t&&!e.template&&!o.defaultUrl)throw new D("URL is required (provide as argument, via --template, or set defaultUrl in config)");if(!e.description&&!e.template)throw new D("Description is required (use -d flag or --template)");if(!o.project)throw new D(`Project ID is required. Set a default project with:
12
+ runhuman projects switch <project-id>
13
+ Or use the --project flag`);let i={projectId:o.project,url:t||o.defaultUrl||"",description:e.description||"",duration:e.duration||o.defaultDuration,screenSize:e.screenSize||o.defaultScreenSize};if(e.schema){let h=await(await import("fs/promises")).readFile(e.schema,"utf-8");i.schema=JSON.parse(h)}else e.schemaInline&&(i.schema=JSON.parse(e.schemaInline));e.metadata&&(i.metadata=JSON.parse(e.metadata)),e.githubRepo&&(i.githubRepo=e.githubRepo),e.createIssues&&(i.createIssues=!0),e.template&&(i.templateId=e.template);let c=e.json?null:Bt("Creating QA test job...").start(),u=await l.createJob(i);if(c&&c.succeed("Job created successfully!"),e.json){let f=a.result({jobId:u.jobId,status:u.status,message:u.message,dashboardUrl:`${o.apiUrl}/jobs/${u.jobId}`,estimatedCompletionTime:u.estimatedCompletionTime});r.output(f)}else e.quiet?console.log(u.jobId):(console.log(`
14
+ `+"=".repeat(60)),console.log(" Job ID: "+u.jobId),console.log(" Status: "+u.status),console.log(` Dashboard: ${o.apiUrl}/jobs/${u.jobId}`),console.log("=".repeat(60)+`
15
+ `),console.log("\u{1F4A1} Track progress:"),console.log(` runhuman status ${u.jobId}`),console.log(` runhuman wait ${u.jobId}
16
+ `));if(e.sync){let{waitForJob:f}=await Promise.resolve().then(()=>(Q(),ne));await f(u.jobId,l,r,e.wait||300)}}catch(n){let o=m(n);new a({json:e.json,quiet:!1}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}x();k();S();I();import{Command as Qt}from"commander";function ve(){let s=new Qt("status");return s.description("Check the current status of a job").argument("<jobId>","Job ID to check").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new a({json:e.json,color:o.color}),i=await new g(o).getJob(t);if(e.json){let c=a.result(i);r.output(c)}else console.log(`
15
17
  \u{1F4CA} Job Status: `+t+`
16
- `),console.log(" Status: "+s.formatStatus(i.status)),i.testerName&&console.log(" Tester: "+i.testerName),console.log(" URL: "+i.url),console.log(" Description: "+i.description),console.log(" Created: "+i.createdAt),i.completedAt&&console.log(" Completed: "+i.completedAt),i.testDurationSeconds&&console.log(" Duration: "+s.formatDuration(i.testDurationSeconds)),i.costUsd&&console.log(" Cost: $"+i.costUsd.toFixed(3)),console.log(`
18
+ `),console.log(" Status: "+r.formatStatus(i.status)),i.testerName&&console.log(" Tester: "+i.testerName),console.log(" URL: "+i.url),console.log(" Description: "+i.description),console.log(" Created: "+i.createdAt),i.completedAt&&console.log(" Completed: "+i.completedAt),i.testDurationSeconds&&console.log(" Duration: "+r.formatDuration(i.testDurationSeconds)),i.costUsd&&console.log(" Cost: $"+i.costUsd.toFixed(3)),console.log(`
17
19
  Dashboard: ${o.apiUrl}/jobs/${t}
18
20
  `),i.status==="pending"||i.status==="waiting"||i.status==="working"?(console.log("\u{1F4A1} Wait for completion:"),console.log(` runhuman wait ${t}
19
21
  `)):i.status==="completed"&&(console.log("\u{1F4A1} View results:"),console.log(` runhuman results ${t}
20
- `))}catch(n){let o=new a({json:e.json}),s=p(n);o.outputError(s.message,s.details),process.exit(s.exitCode)}}),r}W();I();b();j();w();import{Command as We}from"commander";function se(){let r=new We("results");return r.description("Display detailed results for a completed job").argument("<jobId>","Job ID to show results for").option("--json","Output as JSON").option("--schema-only","Show only extracted schema data").option("--raw","Show raw tester response (no processing)").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=new a({json:e.json,color:o.color}),i=await new d(o).getJob(t);if(e.json){let c=a.result(i);s.output(c)}else{if(console.log(`
22
+ `))}catch(n){let o=new a({json:e.json}),r=m(n);o.outputError(r.message,r.details),process.exit(r.exitCode)}}),s}Q();x();k();S();I();import{Command as Zt}from"commander";function Ne(){let s=new Zt("results");return s.description("Display detailed results for a completed job").argument("<jobId>","Job ID to show results for").option("--json","Output as JSON").option("--schema-only","Show only extracted schema data").option("--raw","Show raw tester response (no processing)").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new a({json:e.json,color:o.color}),i=await new g(o).getJob(t);if(e.json){let c=a.result(i);r.output(c)}else{if(console.log(`
21
23
  \u{1F4CA} Test Results: `+t+`
22
24
  `),console.log("=".repeat(80)),console.log("Job Information"),console.log("=".repeat(80)+`
23
- `),console.log(" Job ID: "+i.id),console.log(" Status: "+i.status),console.log(" URL: "+i.url),console.log(" Description: "+i.description),console.log(" Started: "+i.createdAt),i.completedAt&&console.log(" Completed: "+i.completedAt),i.testDurationSeconds&&console.log(" Duration: "+s.formatDuration(i.testDurationSeconds)),i.costUsd&&console.log(" Cost: $"+i.costUsd.toFixed(3)),i.testerName&&console.log(" Tester: "+i.testerName),i.result&&Object.keys(i.result).length>0&&!e.raw){console.log(`
25
+ `),console.log(" Job ID: "+i.id),console.log(" Status: "+i.status),console.log(" URL: "+i.url),console.log(" Description: "+i.description),console.log(" Started: "+i.createdAt),i.completedAt&&console.log(" Completed: "+i.completedAt),i.testDurationSeconds&&console.log(" Duration: "+r.formatDuration(i.testDurationSeconds)),i.costUsd&&console.log(" Cost: $"+i.costUsd.toFixed(3)),i.testerName&&console.log(" Tester: "+i.testerName),i.result&&Object.keys(i.result).length>0&&!e.raw){console.log(`
24
26
  `+"=".repeat(80)),console.log("Structured Results (Extracted)"),console.log("=".repeat(80)+`
25
- `);for(let[c,l]of Object.entries(i.result)){let g=typeof l=="object"?JSON.stringify(l,null,2):String(l);console.log(` ${c}:`.padEnd(30)+g)}}i.testerResponse&&!e.schemaOnly&&(console.log(`
27
+ `);for(let[c,u]of Object.entries(i.result)){let f=typeof u=="object"?JSON.stringify(u,null,2):String(u);console.log(` ${c}:`.padEnd(30)+f)}}i.testerResponse&&!e.schemaOnly&&(console.log(`
26
28
  `+"=".repeat(80)),console.log("Tester Feedback"),console.log("=".repeat(80)+`
27
29
  `),console.log(" "+i.testerResponse.split(`
28
30
  `).join(`
29
31
  `))),console.log(`
30
32
  `+"=".repeat(80)+`
31
- `)}}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r}I();b();j();w();import{Command as Be}from"commander";function ie(){let r=new Be("list");return r.description("List all jobs with optional filtering").argument("[filter]","Status filter: all|pending|claimed|in_progress|completed|failed|timeout").option("--project <id>","Filter by project").option("--limit <number>","Number of results (default: 20)",parseInt).option("--offset <number>","Pagination offset (default: 0)",parseInt).option("--json","Output as JSON").option("--format <type>","Output format: table|json|compact").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=new a({json:e.json,format:e.format,color:o.color}),u=new d(o),i={limit:e.limit||20,offset:e.offset||0};t&&t!=="all"&&(i.status=t),e.project&&(i.projectId=e.project);let{jobs:c,total:l}=await u.listJobs(i);if(e.json){let g=a.result({jobs:c,total:l});s.output(g)}else console.log(`
32
- \u{1F4CB} Recent Jobs (${c.length} of ${l})
33
- `),console.log(s.formatJobList(c)),console.log(`
33
+ `)}}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}x();k();S();I();import{Command as Xt}from"commander";function De(){let s=new Xt("list");return s.description("List all jobs with optional filtering").argument("[filter]","Status filter: all|pending|claimed|in_progress|completed|failed|timeout").option("--project <id>","Filter by project").option("--limit <number>","Number of results (default: 20)",parseInt).option("--offset <number>","Pagination offset (default: 0)",parseInt).option("--json","Output as JSON").option("--format <type>","Output format: table|json|compact").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new a({json:e.json,format:e.format,color:o.color}),l=new g(o),i={limit:e.limit||20,offset:e.offset||0};t&&t!=="all"&&(i.status=t),e.project&&(i.projectId=e.project);let{jobs:c,total:u}=await l.listJobs(i);if(e.json){let f=a.result({jobs:c,total:u});r.output(f)}else console.log(`
34
+ \u{1F4CB} Recent Jobs (${c.length} of ${u})
35
+ `),console.log(r.formatJobList(c)),console.log(`
34
36
  \u{1F4A1} View details: runhuman status <jobId>`),console.log(` View results: runhuman results <jobId>
35
- `)}catch(n){let o=new a({json:e.json}),s=p(n);o.outputError(s.message,s.details),process.exit(s.exitCode)}}),r}I();b();j();w();import{Command as Ve}from"commander";function ae(){let r=new Ve("delete");return r.description("Delete a job permanently").argument("<jobId>","Job ID to delete").option("--confirm","Skip confirmation prompt").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=new a({json:e.json,color:o.color});if(!e.json&&!e.confirm)throw console.log(`
37
+ `)}catch(n){let o=new a({json:e.json}),r=m(n);o.outputError(r.message,r.details),process.exit(r.exitCode)}}),s}x();k();S();I();import{Command as Yt}from"commander";function $e(){let s=new Yt("delete");return s.description("Delete a job permanently").argument("<jobId>","Job ID to delete").option("--confirm","Skip confirmation prompt").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new a({json:e.json,color:o.color});if(!e.json&&!e.confirm)throw console.log(`
36
38
  \u26A0\uFE0F Warning: This will permanently delete job ${t}`),console.log(` This action cannot be undone.
37
- `),new Error("Please use --confirm flag to delete the job");if(await new d(o).deleteJob(t),e.json){let i=a.result({success:!0,message:"Job deleted successfully",jobId:t});s.output(i)}else console.log(`
39
+ `),new Error("Please use --confirm flag to delete the job");if(await new g(o).deleteJob(t),e.json){let i=a.result({success:!0,message:"Job deleted successfully",jobId:t});r.output(i)}else console.log(`
38
40
  \u2705 Job deleted successfully
39
41
  `),console.log(` Job ID: ${t}`),console.log(` Status: Permanently deleted
40
- `)}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r}I();b();j();w();import{Command as Qe}from"commander";import Ye from"chokidar";import{existsSync as B,readFileSync as ee,writeFileSync as Ze,unlinkSync as Xe}from"fs";import{join as ce}from"path";import{homedir as le}from"os";var E=ce(le(),".config","runhuman","watch.pid");function ue(){let r=new Qe("watch");return r.description("Watch files and auto-create QA test jobs on changes").argument("[patterns...]",'File patterns to watch (e.g., "src/**/*.tsx")').option("--url <url>","URL to test (required)").option("--template <name>","Template to use for tests").option("--description <text>","Test description").option("--debounce <ms>","Debounce delay in milliseconds",parseInt).option("--ignore <patterns...>","Patterns to ignore").option("--stop","Stop existing watch process").option("--status","Check if watch is running").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{if(e.stop){await tt();return}if(e.status){ot();return}pe()&&(console.log(`
42
+ `)}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}x();k();S();I();import{Command as eo}from"commander";import to from"chokidar";import{existsSync as Z,readFileSync as re,writeFileSync as oo,unlinkSync as no}from"fs";import{join as Me}from"path";import{homedir as Le}from"os";var $=Me(Le(),".config","runhuman","watch.pid");function Je(){let s=new eo("watch");return s.description("Watch files and auto-create QA test jobs on changes").argument("[patterns...]",'File patterns to watch (e.g., "src/**/*.tsx")').option("--url <url>","URL to test (required)").option("--template <name>","Template to use for tests").option("--description <text>","Test description").option("--debounce <ms>","Debounce delay in milliseconds",parseInt).option("--ignore <patterns...>","Patterns to ignore").option("--stop","Stop existing watch process").option("--status","Check if watch is running").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{if(e.stop){await so();return}if(e.status){io();return}_e()&&(console.log(`
41
43
  \u26A0\uFE0F Watch mode is already running`),console.log(` Use --stop to stop it first
42
- `),process.exit(1));let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=o.watch||{},u=t.length>0?t:s.patterns||["src/**/*"],i=e.ignore||s.ignore||["**/node_modules/**","**/dist/**","**/build/**","**/.git/**"],c=e.debounce||s.debounce||2e3,l=e.url||s.url||o.defaultUrl,g=e.description||s.description||"Auto-test from watch mode",f=e.template||s.template;l||(console.log(`
44
+ `),process.exit(1));let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=o.watch||{},l=t.length>0?t:r.patterns||["src/**/*"],i=e.ignore||r.ignore||["**/node_modules/**","**/dist/**","**/build/**","**/.git/**"],c=e.debounce||r.debounce||2e3,u=e.url||r.url||o.defaultUrl,f=e.description||r.description||"Auto-test from watch mode",h=e.template||r.template;u||(console.log(`
43
45
  \u274C Error: URL required
44
46
  `),console.log(`Provide via --url flag or set in .runhumanrc:
45
47
  `),console.log(' runhuman watch --url "https://myapp.com"'),console.log(` OR add to .runhumanrc: { "watch": { "url": "..." } }
46
- `),process.exit(1));let h=new a({color:o.color});console.log(`
48
+ `),process.exit(1)),o.project||(console.log(`
49
+ \u274C Error: No project selected
50
+ `),console.log(`Run: runhuman projects switch <project-id>
51
+ `),process.exit(1));let y=new a({color:o.color});console.log(`
47
52
  \u{1F440} Starting watch mode
48
- `),console.log(` Watching: ${u.join(", ")}`),console.log(` Ignoring: ${i.join(", ")}`),console.log(` Debounce: ${c}ms`),console.log(` URL: ${l}`),f&&console.log(` Template: ${f}`),console.log(`
53
+ `),console.log(` Watching: ${l.join(", ")}`),console.log(` Ignoring: ${i.join(", ")}`),console.log(` Debounce: ${c}ms`),console.log(` URL: ${u}`),h&&console.log(` Template: ${h}`),console.log(`
49
54
  Press Ctrl+C to stop
50
- `),et(process.pid);let U=new d(o),y=null,D=new Set,P=Ye.watch(u,{ignored:i,persistent:!0,ignoreInitial:!0});P.on("change",J=>{D.add(J),console.log(` \u{1F4DD} Changed: ${J}`),y&&clearTimeout(y),y=setTimeout(async()=>{let te=Array.from(D);D.clear(),console.log(`
51
- \u{1F680} Creating test job for ${te.length} file(s)...
52
- `);try{let Q={url:l,description:`${g}
55
+ `),ro(process.pid);let j=new g(o),C=null,N=new Set,T=to.watch(l,{ignored:i,persistent:!0,ignoreInitial:!0});T.on("change",_=>{N.add(_),console.log(` \u{1F4DD} Changed: ${_}`),C&&clearTimeout(C),C=setTimeout(async()=>{let se=Array.from(N);N.clear(),console.log(`
56
+ \u{1F680} Creating test job for ${se.length} file(s)...
57
+ `);try{let Y={projectId:o.project,url:u,description:`${f}
53
58
 
54
59
  Changed files:
55
- ${te.map(xe=>`- ${xe}`).join(`
56
- `)}`,templateId:f},F=await U.createJob(Q);console.log(` \u2705 Job created: ${F.jobId}`),console.log(` \u{1F4CA} Dashboard: https://runhuman.com/jobs/${F.jobId}
60
+ ${se.map(rt=>`- ${rt}`).join(`
61
+ `)}`,templateId:h},z=await j.createJob(Y);console.log(` \u2705 Job created: ${z.jobId}`),console.log(` \u{1F4CA} Dashboard: https://runhuman.com/jobs/${z.jobId}
57
62
  `),console.log(` Watching for more changes...
58
- `)}catch(Q){let F=p(Q);h.outputError(F.message,F.details),console.log(`
63
+ `)}catch(Y){let z=m(Y);y.outputError(z.message,z.details),console.log(`
59
64
  Watching for more changes...
60
- `)}},c)}),P.on("error",J=>{console.error(`
61
- \u274C Watch error: ${J instanceof Error?J.message:String(J)}
62
- `)});let K=()=>{console.log(`
65
+ `)}},c)}),T.on("error",_=>{console.error(`
66
+ \u274C Watch error: ${_ instanceof Error?_.message:String(_)}
67
+ `)});let H=()=>{console.log(`
63
68
 
64
69
  \u{1F44B} Stopping watch mode...
65
- `),P.close(),G(),process.exit(0)};process.on("SIGINT",K),process.on("SIGTERM",K)}catch(n){let o=p(n);new a({}).outputError(o.message,o.details),G(),process.exit(o.exitCode)}}),r}function et(r){let t=ce(le(),".config","runhuman");B(t)||De("fs").mkdirSync(t,{recursive:!0}),Ze(E,r.toString())}function G(){B(E)&&Xe(E)}function pe(){if(!B(E))return!1;try{let r=parseInt(ee(E,"utf-8"));return process.kill(r,0),!0}catch{return G(),!1}}async function tt(){if(!B(E)){console.log(`
70
+ `),T.close(),G(),process.exit(0)};process.on("SIGINT",H),process.on("SIGTERM",H)}catch(n){let o=m(n);new a({}).outputError(o.message,o.details),G(),process.exit(o.exitCode)}}),s}function ro(s){let t=Me(Le(),".config","runhuman");Z(t)||it("fs").mkdirSync(t,{recursive:!0}),oo($,s.toString())}function G(){Z($)&&no($)}function _e(){if(!Z($))return!1;try{let s=parseInt(re($,"utf-8"));return process.kill(s,0),!0}catch{return G(),!1}}async function so(){if(!Z($)){console.log(`
66
71
  \u2139\uFE0F Watch mode is not running
67
- `);return}try{let r=parseInt(ee(E,"utf-8"));process.kill(r,"SIGTERM"),G(),console.log(`
72
+ `);return}try{let s=parseInt(re($,"utf-8"));process.kill(s,"SIGTERM"),G(),console.log(`
68
73
  \u2705 Watch mode stopped
69
74
  `)}catch{G(),console.log(`
70
75
  \u2139\uFE0F Watch process not found (already stopped)
71
- `)}}function ot(){if(pe()){let r=parseInt(ee(E,"utf-8"));console.log(`
72
- \u2705 Watch mode is running`),console.log(` \u{1F4DD} PID: ${r}`),console.log(`
76
+ `)}}function io(){if(_e()){let s=parseInt(re($,"utf-8"));console.log(`
77
+ \u2705 Watch mode is running`),console.log(` \u{1F4DD} PID: ${s}`),console.log(`
73
78
  Stop with: runhuman watch --stop
74
79
  `)}else console.log(`
75
80
  \u2139\uFE0F Watch mode is not running
76
- `)}I();b();w();j();import{Command as at}from"commander";import nt from"http";import{URL as rt}from"url";import st from"open";async function V(r){let{apiUrl:t,autoOpenBrowser:e=!0}=r;return new Promise((n,o)=>{let s=nt.createServer((i,c)=>{if(!i.url){c.writeHead(400),c.end("Bad request");return}let l=new rt(i.url,"http://localhost");if(l.pathname==="/callback"){let g=l.searchParams.get("token"),f=l.searchParams.get("email"),h=l.searchParams.get("error");if(h){c.writeHead(200,{"Content-Type":"text/html"}),c.end(me(h)),s.close(),o(new Error(h));return}if(g&&f){c.writeHead(200,{"Content-Type":"text/html"}),c.end(it(f)),s.close(),n({token:g,email:f});return}c.writeHead(400,{"Content-Type":"text/html"}),c.end(me("Missing token or email in callback")),s.close(),o(new Error("Missing token or email in callback"));return}c.writeHead(404),c.end("Not found")});s.listen(0,"127.0.0.1",async()=>{let i=s.address();if(!i||typeof i=="string"){o(new Error("Failed to start local server"));return}let l=`http://127.0.0.1:${i.port}/callback`,g=`${t}/cli/auth?callback=${encodeURIComponent(l)}`;if(console.log(""),e){console.log("Opening browser for authentication...");try{await st(g)}catch{console.log("Could not open browser automatically."),console.log(`
77
- Please visit: ${g}`)}}else console.log(`Please visit: ${g}`);console.log(""),console.log("Waiting for authentication...")});let u=setTimeout(()=>{s.close(),o(new Error("Authentication timed out. Please try again."))},300*1e3);s.on("close",()=>clearTimeout(u)),s.on("error",i=>{clearTimeout(u),o(new Error(`Failed to start local server: ${i.message}`))})})}function it(r){return`<!DOCTYPE html>
81
+ `)}x();k();I();S();import{Command as po}from"commander";import ao from"http";import{URL as co}from"url";import lo from"open";async function X(s){let{apiUrl:t,autoOpenBrowser:e=!0}=s;return new Promise((n,o)=>{let r=ao.createServer((i,c)=>{if(!i.url){c.writeHead(400),c.end("Bad request");return}let u=new co(i.url,"http://localhost");if(u.pathname==="/callback"){let f=u.searchParams.get("token"),h=u.searchParams.get("email"),y=u.searchParams.get("projectId"),j=u.searchParams.get("error");if(j){c.writeHead(200,{"Content-Type":"text/html"}),c.end(Fe(j)),r.close(),o(new Error(j));return}if(f&&h&&y){c.writeHead(200,{"Content-Type":"text/html"}),c.end(uo(h)),r.close(),n({token:f,email:h,projectId:y});return}c.writeHead(400,{"Content-Type":"text/html"}),c.end(Fe("Missing token, email, or projectId in callback")),r.close(),o(new Error("Missing token, email, or projectId in callback"));return}c.writeHead(404),c.end("Not found")});r.listen(0,"127.0.0.1",async()=>{let i=r.address();if(!i||typeof i=="string"){o(new Error("Failed to start local server"));return}let u=`http://127.0.0.1:${i.port}/callback`,f=`${t}/cli/auth?callback=${encodeURIComponent(u)}`;if(console.log(""),e){console.log("Opening browser for authentication...");try{await lo(f)}catch{console.log("Could not open browser automatically."),console.log(`
82
+ Please visit: ${f}`)}}else console.log(`Please visit: ${f}`);console.log(""),console.log("Waiting for authentication...")});let l=setTimeout(()=>{r.close(),o(new Error("Authentication timed out. Please try again."))},300*1e3);r.on("close",()=>clearTimeout(l)),r.on("error",i=>{clearTimeout(l),o(new Error(`Failed to start local server: ${i.message}`))})})}function uo(s){return`<!DOCTYPE html>
78
83
  <html>
79
84
  <head>
80
85
  <title>Runhuman CLI - Authenticated</title>
@@ -105,11 +110,11 @@ Please visit: ${g}`)}}else console.log(`Please visit: ${g}`);console.log(""),con
105
110
  <body>
106
111
  <div class="container">
107
112
  <h1>Successfully authenticated!</h1>
108
- <p>Logged in as <span class="email">${r}</span></p>
113
+ <p>Logged in as <span class="email">${s}</span></p>
109
114
  <p class="close-note">You can close this tab and return to the terminal.</p>
110
115
  </div>
111
116
  </body>
112
- </html>`}function me(r){return`<!DOCTYPE html>
117
+ </html>`}function Fe(s){return`<!DOCTYPE html>
113
118
  <html>
114
119
  <head>
115
120
  <title>Runhuman CLI - Authentication Failed</title>
@@ -139,98 +144,99 @@ Please visit: ${g}`)}}else console.log(`Please visit: ${g}`);console.log(""),con
139
144
  <div class="container">
140
145
  <h1>Authentication failed</h1>
141
146
  <p>Please try again from the terminal.</p>
142
- <div class="error">${r}</div>
147
+ <div class="error">${s}</div>
143
148
  </div>
144
149
  </body>
145
- </html>`}function ge(){let r=new at("login");return r.description("Authenticate with Runhuman").option("--token <token>","Login with API key (skip browser)").option("--no-browser","Print auth URL instead of opening browser").option("--json","Output as JSON").action(async t=>{try{let e=new m,n=await e.loadConfig(),o=new a({json:t.json,color:n.color});if(t.token)await ct(e,o,t.token,t.json);else{let s=t.browser!==!1&&n.autoOpenBrowser!==!1;await lt(e,o,n.apiUrl,s,t.json)}}catch(e){let n=p(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),r}async function ct(r,t,e,n){r.saveCredentials({accessToken:e});let o=await r.loadConfig({apiKey:e}),u=await new d(o).getCurrentUser();r.saveUserInfo(u),n?t.output(a.result({success:!0,user:u})):(t.success("Successfully logged in!"),console.log(` User: ${u.email}`),console.log(` Account ID: ${u.accountId}
146
- `))}async function lt(r,t,e,n,o){o||console.log("Logging in to Runhuman...");let s=await V({apiUrl:e||"https://runhuman.com",autoOpenBrowser:n});r.saveCredentials({accessToken:s.token});let u=await r.loadConfig({apiKey:s.token}),c=await new d(u).getCurrentUser();r.saveUserInfo(c),o?t.output(a.result({success:!0,user:c})):(console.log(""),t.success("Successfully logged in!"),console.log(` User: ${c.email}`),console.log(` Account ID: ${c.accountId}
147
- `))}b();w();j();import{Command as ut}from"commander";function de(){let r=new ut("logout");return r.description("Log out and clear stored credentials").option("--force","Skip confirmation prompt").option("--json","Output as JSON").action(async t=>{try{let e=new m,n=new a({json:t.json});if(e.clearCredentials(),e.clearUserInfo(),t.json){let o=a.result({success:!0,message:"Logged out successfully"});n.output(o)}else n.success("Logged out successfully!"),console.log(`Credentials have been cleared.
148
- `)}catch(e){let n=p(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),r}I();b();w();j();import{Command as pt}from"commander";function fe(){let r=new pt("whoami");return r.description("Display current user information").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let e=new m,n=await e.loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new a({json:t.json}),s=new d(n),u=await s.getCurrentUser(),i;try{i=(await s.getTokenBalance()).balance}catch{i=null}let c=e.loadUserInfo();if(t.json){let l=a.result({user:u,balance:i});o.output(l)}else console.log(`
150
+ </html>`}function Ke(){let s=new po("login");return s.description("Authenticate with Runhuman").option("--token <token>","Login with API key (skip browser)").option("--no-browser","Print auth URL instead of opening browser").option("--json","Output as JSON").action(async t=>{try{let e=new d,n=await e.loadConfig(),o=new a({json:t.json,color:n.color});if(t.token)await mo(e,o,t.token,t.json);else{let r=t.browser!==!1&&n.autoOpenBrowser!==!1;await go(e,o,n.apiUrl,r,t.json)}}catch(e){let n=m(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),s}async function mo(s,t,e,n){s.saveCredentials({accessToken:e});let o=await s.loadConfig({apiKey:e}),l=await new g(o).getCurrentUser();s.saveUserInfo(l),n?t.output(a.result({success:!0,user:l})):(t.success("Successfully logged in!"),console.log(` User: ${l.email}`),console.log(` Account ID: ${l.accountId}
151
+ `))}async function go(s,t,e,n,o){o||console.log("Logging in to Runhuman...");let r=await X({apiUrl:e||"https://runhuman.com",autoOpenBrowser:n});s.saveCredentials({accessToken:r.token}),await s.set("project",r.projectId,!0);let l=await s.loadConfig({apiKey:r.token}),c=await new g(l).getCurrentUser();s.saveUserInfo(c),o?t.output(a.result({success:!0,user:c,projectId:r.projectId})):(console.log(""),t.success("Successfully logged in!"),console.log(` User: ${c.email}`),console.log(` Account ID: ${c.accountId}`),console.log(` Default Project: ${r.projectId}
152
+ `)),process.exit(0)}k();I();S();import{Command as fo}from"commander";function He(){let s=new fo("logout");return s.description("Log out and clear stored credentials").option("--force","Skip confirmation prompt").option("--json","Output as JSON").action(async t=>{try{let e=new d,n=new a({json:t.json});if(e.clearCredentials(),e.clearUserInfo(),t.json){let o=a.result({success:!0,message:"Logged out successfully"});n.output(o)}else n.success("Logged out successfully!"),console.log(`Credentials have been cleared.
153
+ `)}catch(e){let n=m(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),s}x();k();I();S();import{Command as ho}from"commander";function ze(){let s=new ho("whoami");return s.description("Display current user information").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let e=new d,n=await e.loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new a({json:t.json}),r=new g(n),l=await r.getCurrentUser(),i;try{i=(await r.getTokenBalance()).balance}catch{i=null}let c=e.loadUserInfo();if(t.json){let u=a.result({user:l,balance:i});o.output(u)}else console.log(`
149
154
  \u{1F464} Current User
150
- `),console.log(" Email: "+u.email),console.log(" Account ID: "+u.accountId),i!==null&&console.log(" Token Balance: $"+i.toFixed(2)),n.project&&console.log(`
151
- Default Project: `+n.project),console.log()}catch(e){let n=p(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),r}I();b();w();j();import{Command as mt}from"commander";function he(){let r=new mt("tokens");return r.description("View token balance and usage"),r.command("balance").description("Check remaining token/credit balance").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let n=await new m().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new a({json:t.json,color:n.color}),s=new d(n),{balance:u}=await s.getTokenBalance();if(t.json){let i=a.result({balance:u});o.output(i)}else console.log(`
155
+ `),console.log(" Email: "+l.email),console.log(" Account ID: "+l.accountId),i!==null&&console.log(" Token Balance: $"+i.toFixed(2)),n.project&&console.log(`
156
+ Default Project: `+n.project),console.log()}catch(e){let n=m(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),s}x();k();I();S();import{Command as yo}from"commander";function Ge(){let s=new yo("tokens");return s.description("View token balance and usage"),s.command("balance").description("Check remaining token/credit balance").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let n=await new d().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new a({json:t.json,color:n.color}),r=new g(n),{balance:l}=await r.getTokenBalance();if(t.json){let i=a.result({balance:l});o.output(i)}else console.log(`
152
157
  \u{1F4B0} Token Balance
153
- `),console.log(` Current Balance: ${u.toLocaleString()} tokens`),console.log(` Estimated Tests: ~${Math.floor(u/100)} tests
158
+ `),console.log(` Current Balance: ${l.toLocaleString()} tokens`),console.log(` Estimated Tests: ~${Math.floor(l/100)} tests
154
159
  `),console.log(`\u{1F4A1} Purchase more tokens: https://runhuman.com/billing
155
- `)}catch(e){let n=p(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),r.command("history").description("View token usage history").option("--limit <number>","Number of records (default: 20)",parseInt).option("--offset <number>","Pagination offset (default: 0)",parseInt).option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let n=await new m().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new a({json:t.json,color:n.color});if(t.json){let s=a.result({message:"Token history endpoint not yet available",history:[]});o.output(s)}else console.log(`
160
+ `)}catch(e){let n=m(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),s.command("history").description("View token usage history").option("--limit <number>","Number of records (default: 20)",parseInt).option("--offset <number>","Pagination offset (default: 0)",parseInt).option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let n=await new d().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new a({json:t.json,color:n.color});if(t.json){let r=a.result({message:"Token history endpoint not yet available",history:[]});o.output(r)}else console.log(`
156
161
  \u{1F4CA} Token Usage History
157
162
  `),console.log(` This feature is coming soon!
158
163
  `),console.log(" For now, view usage in the dashboard:"),console.log(` https://runhuman.com/dashboard/usage
159
- `)}catch(e){let n=p(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),r}b();w();j();import{Command as gt}from"commander";function we(){let r=new gt("config");return r.description("Manage CLI configuration"),r.command("get").description("Get a configuration value").argument("<key>","Configuration key").option("--json","Output as JSON").action(async(t,e)=>{try{let o=await new m().get(t),s=new a({json:e.json});if(e.json){let u=a.result({[t]:o});s.output(u)}else console.log(o!==void 0?o:"(not set)")}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r.command("set").description("Set a configuration value").argument("<key>","Configuration key").argument("<value>","Configuration value").option("--global","Set globally (not project-specific)").option("--json","Output as JSON").action(async(t,e,n)=>{try{await new m().set(t,e,n.global);let s=new a({json:n.json});if(n.json){let u=a.result({success:!0,key:t,value:e,scope:n.global?"global":"project"});s.output(u)}else s.success(`Set ${t} = ${e}`+(n.global?" (global)":" (project)"))}catch(o){let s=p(o);new a({json:n.json}).outputError(s.message,s.details),process.exit(s.exitCode)}}),r.command("list").description("List all configuration values").option("--show-secrets","Show API keys (default: masked)").option("--json","Output as JSON").action(async t=>{try{let n=await new m().list(),o=new a({json:t.json});if(t.json){let s=a.result(n);o.output(s)}else{if(console.log(`
164
+ `)}catch(e){let n=m(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),s}k();I();S();import{Command as bo}from"commander";function qe(){let s=new bo("config");return s.description("Manage CLI configuration"),s.command("get").description("Get a configuration value").argument("<key>","Configuration key").option("--json","Output as JSON").action(async(t,e)=>{try{let o=await new d().get(t),r=new a({json:e.json});if(e.json){let l=a.result({[t]:o});r.output(l)}else console.log(o!==void 0?o:"(not set)")}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("set").description("Set a configuration value").argument("<key>","Configuration key").argument("<value>","Configuration value").option("--global","Set globally (not project-specific)").option("--json","Output as JSON").action(async(t,e,n)=>{try{await new d().set(t,e,n.global);let r=new a({json:n.json});if(n.json){let l=a.result({success:!0,key:t,value:e,scope:n.global?"global":"project"});r.output(l)}else r.success(`Set ${t} = ${e}`+(n.global?" (global)":" (project)"))}catch(o){let r=m(o);new a({json:n.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),s.command("list").description("List all configuration values").option("--show-secrets","Show API keys (default: masked)").option("--json","Output as JSON").action(async t=>{try{let n=await new d().list(),o=new a({json:t.json});if(t.json){let r=a.result(n);o.output(r)}else{if(console.log(`
160
165
  \u2699\uFE0F Configuration
161
- `),n.global&&Object.keys(n.global).length>0){console.log("Global (~/.config/runhuman/config.json):");for(let[s,u]of Object.entries(n.global)){let i=s==="apiKey"&&!t.showSecrets?ye(String(u)):u;console.log(` ${s}:`.padEnd(20)+i)}console.log()}if(n.project&&Object.keys(n.project).length>0){console.log("Project (.runhumanrc):");for(let[s,u]of Object.entries(n.project)){let i=s==="apiKey"&&!t.showSecrets?ye(String(u)):u;console.log(` ${s}:`.padEnd(20)+i)}console.log()}console.log("\u{1F4A1} Set value: runhuman config set <key> <value>"),console.log(` Get value: runhuman config get <key>
162
- `)}}catch(e){let n=p(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),r.command("reset").description("Reset configuration to defaults").option("--global","Reset global config").option("--project","Reset project config").option("--all","Reset all config").option("--force","Skip confirmation prompt").option("--json","Output as JSON").action(async t=>{try{if(!t.global&&!t.project&&!t.all)throw new Error("Specify --global, --project, or --all");let e=t.all?"all":t.global?"global":"project";t.force||(console.log(`\u26A0\uFE0F This will reset ${e} configuration to defaults.`),console.log(`Use --force to skip this prompt.
163
- `),process.exit(0)),await new m().reset(e);let o=new a({json:t.json});if(t.json){let s=a.result({success:!0,scope:e});o.output(s)}else o.success(`Reset ${e} configuration`)}catch(e){let n=p(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),r}function ye(r){return r.length<=8?"****":r.substring(0,4)+"*".repeat(r.length-8)+r.substring(r.length-4)}w();j();import{Command as dt}from"commander";import ft from"inquirer";import{writeFileSync as ht}from"fs";import{join as yt}from"path";function je(){let r=new dt("init");return r.description("Initialize a new Runhuman project with configuration").option("--name <text>","Project name").option("--url <url>","Default URL").option("--github-repo <owner/repo>","GitHub repository").option("--yes","Skip all prompts (use defaults)").option("--json","Output as JSON").action(async t=>{try{let e=new a({json:t.json});!t.json&&!t.yes&&(console.log(`\u{1F680} Welcome to Runhuman!
166
+ `),n.global&&Object.keys(n.global).length>0){console.log("Global (~/.config/runhuman/config.json):");for(let[r,l]of Object.entries(n.global)){let i=r==="apiKey"&&!t.showSecrets?We(String(l)):l;console.log(` ${r}:`.padEnd(20)+i)}console.log()}if(n.project&&Object.keys(n.project).length>0){console.log("Project (.runhumanrc):");for(let[r,l]of Object.entries(n.project)){let i=r==="apiKey"&&!t.showSecrets?We(String(l)):l;console.log(` ${r}:`.padEnd(20)+i)}console.log()}console.log("\u{1F4A1} Set value: runhuman config set <key> <value>"),console.log(` Get value: runhuman config get <key>
167
+ `)}}catch(e){let n=m(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),s.command("reset").description("Reset configuration to defaults").option("--global","Reset global config").option("--project","Reset project config").option("--all","Reset all config").option("--force","Skip confirmation prompt").option("--json","Output as JSON").action(async t=>{try{if(!t.global&&!t.project&&!t.all)throw new Error("Specify --global, --project, or --all");let e=t.all?"all":t.global?"global":"project";t.force||(console.log(`\u26A0\uFE0F This will reset ${e} configuration to defaults.`),console.log(`Use --force to skip this prompt.
168
+ `),process.exit(0)),await new d().reset(e);let o=new a({json:t.json});if(t.json){let r=a.result({success:!0,scope:e});o.output(r)}else o.success(`Reset ${e} configuration`)}catch(e){let n=m(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),s}function We(s){return s.length<=8?"****":s.substring(0,4)+"*".repeat(s.length-8)+s.substring(s.length-4)}I();S();import{Command as wo}from"commander";import jo from"inquirer";import{writeFileSync as Co}from"fs";import{join as Io}from"path";function Ve(){let s=new wo("init");return s.description("Initialize a new Runhuman project with configuration").option("--name <text>","Project name").option("--url <url>","Default URL").option("--github-repo <owner/repo>","GitHub repository").option("--yes","Skip all prompts (use defaults)").option("--json","Output as JSON").action(async t=>{try{let e=new a({json:t.json});!t.json&&!t.yes&&(console.log(`\u{1F680} Welcome to Runhuman!
164
169
  `),console.log("Let's set up your project. This will create:"),console.log(" \u2022 A configuration file (.runhumanrc)"),console.log(` \u2022 Setup your project defaults
165
170
  `),console.log("=".repeat(60)+`
166
- `));let n=t.name,o=t.url,s=t.githubRepo;if(!t.yes&&!t.json){let c=await ft.prompt([{type:"input",name:"projectName",message:"Project name:",default:"My Project",when:!n},{type:"input",name:"defaultUrl",message:"Default URL (optional):",when:!o},{type:"input",name:"githubRepo",message:"Link to GitHub repository (optional, e.g., owner/repo):",when:!s}]);n=n||c.projectName,o=o||c.defaultUrl,s=s||c.githubRepo}let u={defaultUrl:o||void 0,githubRepo:s||void 0,defaultDuration:5,defaultScreenSize:"desktop"},i=yt(process.cwd(),".runhumanrc");if(ht(i,JSON.stringify(u,null,2)),t.json){let c=a.result({success:!0,configPath:i,config:u});e.output(c)}else console.log(`
171
+ `));let n=t.name,o=t.url,r=t.githubRepo;if(!t.yes&&!t.json){let c=await jo.prompt([{type:"input",name:"projectName",message:"Project name:",default:"My Project",when:!n},{type:"input",name:"defaultUrl",message:"Default URL (optional):",when:!o},{type:"input",name:"githubRepo",message:"Link to GitHub repository (optional, e.g., owner/repo):",when:!r}]);n=n||c.projectName,o=o||c.defaultUrl,r=r||c.githubRepo}let l={defaultUrl:o||void 0,githubRepo:r||void 0,defaultDuration:5,defaultScreenSize:"desktop"},i=Io(process.cwd(),".runhumanrc");if(Co(i,JSON.stringify(l,null,2)),t.json){let c=a.result({success:!0,configPath:i,config:l});e.output(c)}else console.log(`
167
172
  `+"=".repeat(60)),e.success("Project initialized successfully!"),console.log(`\u{1F4C1} Configuration saved to: .runhumanrc
168
173
  `),console.log(`\u{1F389} All set! Try creating your first test:
169
174
  `),console.log(` runhuman create https://example.com -d "Test homepage"
170
175
  `),console.log(`\u{1F4DA} Learn more: https://runhuman.com/docs/cli
171
- `)}catch(e){let n=p(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),r}I();b();w();j();import{Command as wt}from"commander";import jt from"cli-table3";function Ce(){let r=new wt("projects");return r.description("Manage projects"),r.command("list").alias("ls").description("List all projects").option("--limit <number>","Number of results (default: 20)",parseInt).option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let n=await new m().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new a({json:t.json,color:n.color}),s=new d(n),{projects:u,total:i}=await s.listProjects({limit:t.limit||20});if(t.json){let c=a.result({projects:u,total:i});o.output(c)}else{console.log(`
172
- \u{1F4C1} Your Projects (${u.length})
173
- `);let c=new jt({head:["Project ID","Name","Created"].map(l=>l),colWidths:[20,30,20]});u.forEach(l=>{c.push([l.id,l.name,new Date(l.createdAt).toLocaleDateString()])}),console.log(c.toString()),console.log(`
176
+ `)}catch(e){let n=m(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),s}x();k();I();S();import{Command as So}from"commander";import Ao from"cli-table3";function Be(){let s=new So("projects");return s.description("Manage projects"),s.command("list").alias("ls").description("List all projects").option("--limit <number>","Number of results (default: 20)",parseInt).option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let n=await new d().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new a({json:t.json,color:n.color}),r=new g(n),{projects:l,total:i}=await r.listProjects({limit:t.limit||20});if(t.json){let c=a.result({projects:l,total:i});o.output(c)}else{console.log(`
177
+ \u{1F4C1} Your Projects (${l.length})
178
+ `);let c=new Ao({head:["Project ID","Name","Created"].map(u=>u),colWidths:[20,30,20]});l.forEach(u=>{c.push([u.id,u.name,new Date(u.createdAt).toLocaleDateString()])}),console.log(c.toString()),console.log(`
174
179
  \u{1F4A1} Set default project: runhuman config set project <id>
175
- `)}}catch(e){let n=p(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),r.command("create").alias("new").description("Create a new project").argument("<name>","Project name").option("--description <text>","Project description").option("--default-url <url>","Default URL for tests").option("--github-repo <owner/repo>","Link to GitHub repository").option("--set-default","Set as default project").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let n=new m,o=await n.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=new a({json:e.json,color:o.color}),i=await new d(o).createProject({name:t,description:e.description,defaultUrl:e.defaultUrl,githubRepo:e.githubRepo});if(e.setDefault&&await n.set("project",i.id,!1),e.json){let c=a.result(i);s.output(c)}else s.success("Project created successfully!"),console.log(" Project ID: "+i.id),console.log(" Name: "+i.name),e.setDefault&&console.log(" Set as default project"),console.log()}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r.command("show").alias("info").alias("get").description("Show detailed project information").argument("<projectId>","Project ID to show").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=new a({json:e.json,color:o.color}),i=await new d(o).getProject(t);if(e.json){let c=a.result(i);s.output(c)}else console.log(`
180
+ `)}}catch(e){let n=m(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),s.command("create").alias("new").description("Create a new project").argument("<name>","Project name").option("--description <text>","Project description").option("--default-url <url>","Default URL for tests").option("--github-repo <owner/repo>","Link to GitHub repository").option("--set-default","Set as default project").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let n=new d,o=await n.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new a({json:e.json,color:o.color}),i=await new g(o).createProject({name:t,description:e.description,defaultUrl:e.defaultUrl,githubRepo:e.githubRepo});if(e.setDefault&&await n.set("project",i.id,!1),e.json){let c=a.result(i);r.output(c)}else r.success("Project created successfully!"),console.log(" Project ID: "+i.id),console.log(" Name: "+i.name),e.setDefault&&console.log(" Set as default project"),console.log()}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("show").alias("info").alias("get").description("Show detailed project information").argument("<projectId>","Project ID to show").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new a({json:e.json,color:o.color}),i=await new g(o).getProject(t);if(e.json){let c=a.result(i);r.output(c)}else console.log(`
176
181
  \u{1F4C1} Project Details
177
- `),console.log(" Project ID: "+i.id),console.log(" Name: "+i.name),i.description&&console.log(" Description: "+i.description),i.defaultUrl&&console.log(" Default URL: "+i.defaultUrl),i.githubRepo&&console.log(" GitHub Repo: "+i.githubRepo),console.log(" Created: "+new Date(i.createdAt).toLocaleString()),console.log()}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r.command("update").description("Update project settings").argument("<projectId>","Project ID to update").option("--name <text>","Update name").option("--description <text>","Update description").option("--default-url <url>","Update default URL").option("--github-repo <owner/repo>","Update GitHub repo").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=new a({json:e.json,color:o.color}),u={name:e.name,description:e.description,defaultUrl:e.defaultUrl,githubRepo:e.githubRepo},c=await new d(o).updateProject(t,u);if(e.json){let l=a.result(c);s.output(l)}else s.success("Project updated successfully!")}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r.command("delete").alias("rm").description("Delete a project permanently").argument("<projectId>","Project ID to delete").option("--force","Skip confirmation prompt").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{e.force||(console.log("\u26A0\uFE0F This will permanently delete the project and all its data."),console.log(`Use --force to skip this prompt.
178
- `),process.exit(0));let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=new a({json:e.json,color:o.color});if(await new d(o).deleteProject(t),e.json){let i=a.result({success:!0});s.output(i)}else s.success("Project deleted successfully!")}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r}I();b();w();j();import{Command as Ct}from"commander";import bt from"cli-table3";function be(){let r=new Ct("keys");return r.description("Manage API keys"),r.command("list").alias("ls").description("List all API keys").option("--project <id>","Filter by project (required)").option("--show-keys","Show full API keys (default: masked)").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let n=await new m().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=t.project||n.project;if(!o)throw new Error("Project ID required (use --project or set default project)");let s=new a({json:t.json,color:n.color}),i=await new d(n).listApiKeys(o);if(t.json){let c=a.result({keys:i});s.output(c)}else{console.log(`
182
+ `),console.log(" Project ID: "+i.id),console.log(" Name: "+i.name),i.description&&console.log(" Description: "+i.description),i.defaultUrl&&console.log(" Default URL: "+i.defaultUrl),i.githubRepo&&console.log(" GitHub Repo: "+i.githubRepo),console.log(" Created: "+new Date(i.createdAt).toLocaleString()),console.log()}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("switch").alias("use").description("Set default project for CLI commands").argument("<projectId>","Project ID to use as default").option("--global","Set as global default instead of local").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let n=new d,o=await n.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new a({json:e.json,color:o.color}),i=await new g(o).getProject(t);if(await n.set("project",t,e.global!==!1),e.json){let c=a.result({success:!0,projectId:t,projectName:i.name,scope:e.global!==!1?"global":"local"});r.output(c)}else r.success(`Default project set to: ${i.name} (${t})`),e.global!==!1?console.log(" Scope: global (all directories)"):console.log(" Scope: local (current directory only)"),console.log()}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("update").description("Update project settings").argument("<projectId>","Project ID to update").option("--name <text>","Update name").option("--description <text>","Update description").option("--default-url <url>","Update default URL").option("--github-repo <owner/repo>","Update GitHub repo").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new a({json:e.json,color:o.color}),l={name:e.name,description:e.description,defaultUrl:e.defaultUrl,githubRepo:e.githubRepo},c=await new g(o).updateProject(t,l);if(e.json){let u=a.result(c);r.output(u)}else r.success("Project updated successfully!")}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("delete").alias("rm").description("Delete a project permanently").argument("<projectId>","Project ID to delete").option("--force","Skip confirmation prompt").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{e.force||(console.log("\u26A0\uFE0F This will permanently delete the project and all its data."),console.log(`Use --force to skip this prompt.
183
+ `),process.exit(0));let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new a({json:e.json,color:o.color});if(await new g(o).deleteProject(t),e.json){let i=a.result({success:!0});r.output(i)}else r.success("Project deleted successfully!")}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}x();k();I();S();import{Command as ko}from"commander";import Uo from"cli-table3";function Qe(){let s=new ko("keys");return s.description("Manage API keys"),s.command("list").alias("ls").description("List all API keys").option("--project <id>","Filter by project (required)").option("--show-keys","Show full API keys (default: masked)").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let n=await new d().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=t.project||n.project;if(!o)throw new Error("Project ID required (use --project or set default project)");let r=new a({json:t.json,color:n.color}),i=await new g(n).listApiKeys(o);if(t.json){let c=a.result({keys:i});r.output(c)}else{console.log(`
179
184
  \u{1F511} API Keys (${i.length})
180
- `);let c=new bt({head:["Key ID","Name","Key","Last Used","Created"].map(l=>l),colWidths:[20,25,20,20,20]});i.forEach(l=>{let g=t.showKeys?l.key:Ut(l.key),f=l.lastUsedAt?new Date(l.lastUsedAt).toLocaleDateString():"Never";c.push([l.id,l.name,g,f,new Date(l.createdAt).toLocaleDateString()])}),console.log(c.toString()),console.log(`
185
+ `);let c=new Uo({head:["Key ID","Name","Key","Last Used","Created"].map(u=>u),colWidths:[20,25,20,20,20]});i.forEach(u=>{let f=t.showKeys?u.key:xo(u.key),h=u.lastUsedAt?new Date(u.lastUsedAt).toLocaleDateString():"Never";c.push([u.id,u.name,f,h,new Date(u.createdAt).toLocaleDateString()])}),console.log(c.toString()),console.log(`
181
186
  \u{1F4A1} Create new key: runhuman keys create "Key Name" --project <id>`),console.log(` Show full key: runhuman keys show <keyId>
182
- `)}}catch(e){let n=p(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),r.command("create").alias("new").description("Create a new API key").argument("<name>","API key name").option("--project <id>","Project ID (required)").option("--copy","Copy key to clipboard").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=e.project||o.project;if(!s)throw new Error("Project ID required (use --project or set default project)");let u=new a({json:e.json,color:o.color}),c=await new d(o).createApiKey(s,t);if(e.json){let l=a.result(c);u.output(l)}else u.success("API Key created successfully!"),console.log(`
187
+ `)}}catch(e){let n=m(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),s.command("create").alias("new").description("Create a new API key").argument("<name>","API key name").option("--project <id>","Project ID (required)").option("--copy","Copy key to clipboard").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=e.project||o.project;if(!r)throw new Error("Project ID required (use --project or set default project)");let l=new a({json:e.json,color:o.color}),c=await new g(o).createApiKey(r,t);if(e.json){let u=a.result(c);l.output(u)}else l.success("API Key created successfully!"),console.log(`
183
188
  Key ID: `+c.id),console.log(" Name: "+c.name),console.log(`
184
189
  API Key: `+c.key),console.log(" "+"^".repeat(c.key.length)),console.log(` \u26A0\uFE0F Save this key! It won't be shown again.
185
190
  `),console.log("\u{1F4A1} Use this key:"),console.log(" export RUNHUMAN_API_KEY="+c.key),console.log(` runhuman create https://myapp.com -d "Test"
186
191
  `),console.log("\u{1F512} Store securely:"),console.log(" - Use environment variables (recommended)"),console.log(" - Use secret management tools"),console.log(` - Never commit to git!
187
- `)}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r.command("show").alias("info").alias("get").description("Show details of a specific API key").argument("<keyId>","Key ID to show").option("--show-key","Show full API key (default: masked)").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl});new a({json:e.json,color:o.color}).warn("Note: keys show requires listing all keys first"),console.log(`Use: runhuman keys list --show-keys
188
- `)}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r.command("delete").aliases(["rm","revoke"]).description("Delete an API key permanently").argument("<keyId>","Key ID to delete").option("--force","Skip confirmation prompt").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{e.force||(console.log("\u26A0\uFE0F This will permanently delete the API key."),console.log(`Use --force to skip this prompt.
189
- `),process.exit(0));let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=new a({json:e.json,color:o.color});if(await new d(o).deleteApiKey(t),e.json){let i=a.result({success:!0});s.output(i)}else s.success("API key deleted successfully!")}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r}function Ut(r){return r.length<=12?"****":r.substring(0,8)+"*".repeat(r.length-12)+r.substring(r.length-4)}I();b();w();j();import{Command as kt}from"commander";import It from"cli-table3";function Ue(){let r=new kt("templates");return r.description("Manage test templates"),r.command("list").alias("ls").description("List all test templates").option("--project <id>","Filter by project (required)").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let n=await new m().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=t.project||n.project;if(!o)throw new Error("Project ID required (use --project or set default project)");let s=new a({json:t.json,color:n.color}),i=await new d(n).listTemplates(o);if(t.json){let c=a.result({templates:i});s.output(c)}else{console.log(`
192
+ `)}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("show").alias("info").alias("get").description("Show details of a specific API key").argument("<keyId>","Key ID to show").option("--show-key","Show full API key (default: masked)").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl});new a({json:e.json,color:o.color}).warn("Note: keys show requires listing all keys first"),console.log(`Use: runhuman keys list --show-keys
193
+ `)}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("delete").aliases(["rm","revoke"]).description("Delete an API key permanently").argument("<keyId>","Key ID to delete").option("--force","Skip confirmation prompt").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{e.force||(console.log("\u26A0\uFE0F This will permanently delete the API key."),console.log(`Use --force to skip this prompt.
194
+ `),process.exit(0));let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new a({json:e.json,color:o.color});if(await new g(o).deleteApiKey(t),e.json){let i=a.result({success:!0});r.output(i)}else r.success("API key deleted successfully!")}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}function xo(s){return s.length<=12?"****":s.substring(0,8)+"*".repeat(s.length-12)+s.substring(s.length-4)}x();k();I();S();import{Command as Po}from"commander";import To from"cli-table3";function Ze(){let s=new Po("templates");return s.description("Manage test templates"),s.command("list").alias("ls").description("List all test templates").option("--project <id>","Filter by project (required)").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let n=await new d().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=t.project||n.project;if(!o)throw new Error("Project ID required (use --project or set default project)");let r=new a({json:t.json,color:n.color}),i=await new g(n).listTemplates(o);if(t.json){let c=a.result({templates:i});r.output(c)}else{console.log(`
190
195
  \u{1F4CB} Test Templates (${i.length})
191
- `);let c=new It({head:["ID","Name","Description","Created"].map(l=>l),colWidths:[30,25,35,20]});i.forEach(l=>{let g=l.description&&l.description.length>30?l.description.substring(0,27)+"...":l.description||"-";c.push([l.id,l.name,g,new Date(l.createdAt).toLocaleDateString()])}),console.log(c.toString()),console.log(`
196
+ `);let c=new To({head:["ID","Name","Description","Created"].map(u=>u),colWidths:[30,25,35,20]});i.forEach(u=>{let f=u.description&&u.description.length>30?u.description.substring(0,27)+"...":u.description||"-";c.push([u.id,u.name,f,new Date(u.createdAt).toLocaleDateString()])}),console.log(c.toString()),console.log(`
192
197
  \u{1F4A1} Create new template: runhuman templates create "Template Name" --project <id>`),console.log(` View template: runhuman templates show <templateId>
193
- `)}}catch(e){let n=p(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),r.command("create").alias("new").description("Create a new test template").argument("<name>","Template name").option("--project <id>","Project ID (required)").option("-d, --description <text>","Template description").option("--duration <seconds>","Target test duration in seconds").option("--screen-size <size>","Default screen size (desktop/mobile/tablet)").option("--schema <path>","Path to JSON schema file").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=e.project||o.project;if(!s)throw new Error("Project ID required (use --project or set default project)");let u=new a({json:e.json,color:o.color}),i;if(e.schema){let{readFileSync:f}=await import("fs"),h=f(e.schema,"utf-8");i=JSON.parse(h)}let c={name:t,description:e.description,targetDurationMinutes:e.duration?Math.floor(parseInt(e.duration)/60):void 0,defaultScreenSize:e.screenSize,outputSchema:i},g=await new d(o).createTemplate(s,c);if(e.json){let f=a.result(g);u.output(f)}else u.success("Template created successfully!"),console.log(`
194
- Template ID: `+g.id),console.log(" Name: "+g.name),g.description&&console.log(" Description: "+g.description),console.log(`
195
- \u{1F4A1} Use this template:`),console.log(" runhuman create https://myapp.com --template "+g.id+`
196
- `)}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r.command("show").alias("info").alias("get").description("Show details of a specific template").argument("<templateId>","Template ID to show").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=new a({json:e.json,color:o.color}),i=await new d(o).getTemplate(t);if(e.json){let c=a.result(i);s.output(c)}else console.log(`
198
+ `)}}catch(e){let n=m(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),s.command("create").alias("new").description("Create a new test template").argument("<name>","Template name").option("--project <id>","Project ID (required)").option("-d, --description <text>","Template description").option("--duration <seconds>","Target test duration in seconds").option("--screen-size <size>","Default screen size (desktop/mobile/tablet)").option("--schema <path>","Path to JSON schema file").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=e.project||o.project;if(!r)throw new Error("Project ID required (use --project or set default project)");let l=new a({json:e.json,color:o.color}),i;if(e.schema){let{readFileSync:h}=await import("fs"),y=h(e.schema,"utf-8");i=JSON.parse(y)}let c={name:t,description:e.description,targetDurationMinutes:e.duration?Math.floor(parseInt(e.duration)/60):void 0,defaultScreenSize:e.screenSize,outputSchema:i},f=await new g(o).createTemplate(r,c);if(e.json){let h=a.result(f);l.output(h)}else l.success("Template created successfully!"),console.log(`
199
+ Template ID: `+f.id),console.log(" Name: "+f.name),f.description&&console.log(" Description: "+f.description),console.log(`
200
+ \u{1F4A1} Use this template:`),console.log(" runhuman create https://myapp.com --template "+f.id+`
201
+ `)}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("show").alias("info").alias("get").description("Show details of a specific template").argument("<templateId>","Template ID to show").option("--project <id>","Project ID (required)").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=e.project||o.project;if(!r)throw new Error("Project ID required (use --project or set default project)");let l=new a({json:e.json,color:o.color}),c=await new g(o).getTemplate(r,t);if(e.json){let u=a.result(c);l.output(u)}else console.log(`
197
202
  \u{1F4CB} Template Details
198
- `),console.log("ID: "+i.id),console.log("Name: "+i.name),console.log("Description: "+(i.description||"-")),console.log("Project: "+i.projectId),i.targetDurationMinutes&&console.log("Duration: "+i.targetDurationMinutes+" minutes"),i.defaultScreenSize&&console.log("Screen Size: "+i.defaultScreenSize),console.log("Created: "+new Date(i.createdAt).toLocaleString()),i.outputSchema&&(console.log(`
199
- Output Schema:`),console.log(JSON.stringify(i.outputSchema,null,2))),console.log(`
200
- \u{1F4A1} Use this template:`),console.log(" runhuman create https://myapp.com --template "+i.id+`
201
- `)}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r.command("update").alias("edit").description("Update a template").argument("<templateId>","Template ID to update").option("--name <name>","New template name").option("-d, --description <text>","New description").option("--duration <seconds>","New target duration in seconds").option("--screen-size <size>","New default screen size").option("--schema <path>","Path to new JSON schema file").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=new a({json:e.json,color:o.color}),u;if(e.schema){let{readFileSync:f}=await import("fs"),h=f(e.schema,"utf-8");u=JSON.parse(h)}let i={name:e.name,description:e.description,targetDurationMinutes:e.duration?Math.floor(parseInt(e.duration)/60):void 0,defaultScreenSize:e.screenSize,outputSchema:u},c=Object.fromEntries(Object.entries(i).filter(([,f])=>f!==void 0));if(Object.keys(c).length===0)throw new Error("No updates provided. Use --name, --description, --duration, --screen-size, or --schema");let g=await new d(o).updateTemplate(t,i);if(e.json){let f=a.result(g);s.output(f)}else s.success("Template updated successfully!"),console.log(`
202
- Template ID: `+g.id),console.log(" Name: "+g.name+`
203
- `)}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r.command("delete").alias("rm").description("Delete a template permanently").argument("<templateId>","Template ID to delete").option("--force","Skip confirmation prompt").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{e.force||(console.log("\u26A0\uFE0F This will permanently delete the template."),console.log(`Use --force to skip this prompt.
204
- `),process.exit(0));let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=new a({json:e.json,color:o.color});if(await new d(o).deleteTemplate(t),e.json){let i=a.result({success:!0});s.output(i)}else s.success("Template deleted successfully!")}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r}I();b();w();j();import{Command as At}from"commander";import ke from"cli-table3";function Ie(){let r=new At("github");return r.alias("gh"),r.description("GitHub integration commands"),r.command("link").description("Link a GitHub repository to your project").argument("<repo>","Repository in format owner/repo").option("--project <id>","Project ID (required)").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let n=new m,o=await n.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=e.project||o.project;if(!s)throw new Error("Project ID required (use --project or set default project)");let u=new a({json:e.json,color:o.color}),i=t.match(/^([^/]+)\/([^/]+)$/);if(!i)throw new Error("Invalid repository format. Use: owner/repo");let[,c,l]=i,f=await new d(o).linkGithubRepo(s,c,l);if(e.json){let h=a.result(f);u.output(h)}else u.success("GitHub repository linked successfully!"),console.log(`
205
- Repository: `+t),console.log(" Project: "+s),console.log(`
203
+ `),console.log("ID: "+c.id),console.log("Name: "+c.name),console.log("Description: "+(c.description||"-")),console.log("Project: "+c.projectId),c.targetDurationMinutes&&console.log("Duration: "+c.targetDurationMinutes+" minutes"),c.defaultScreenSize&&console.log("Screen Size: "+c.defaultScreenSize),console.log("Created: "+new Date(c.createdAt).toLocaleString()),c.outputSchema&&(console.log(`
204
+ Output Schema:`),console.log(JSON.stringify(c.outputSchema,null,2))),console.log(`
205
+ \u{1F4A1} Use this template:`),console.log(" runhuman create https://myapp.com --template "+c.id+`
206
+ `)}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("update").alias("edit").description("Update a template").argument("<templateId>","Template ID to update").option("--project <id>","Project ID (required)").option("--name <name>","New template name").option("-d, --description <text>","New description").option("--duration <seconds>","New target duration in seconds").option("--screen-size <size>","New default screen size").option("--schema <path>","Path to new JSON schema file").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=e.project||o.project;if(!r)throw new Error("Project ID required (use --project or set default project)");let l=new a({json:e.json,color:o.color}),i;if(e.schema){let{readFileSync:y}=await import("fs"),j=y(e.schema,"utf-8");i=JSON.parse(j)}let c={name:e.name,description:e.description,targetDurationMinutes:e.duration?Math.floor(parseInt(e.duration)/60):void 0,defaultScreenSize:e.screenSize,outputSchema:i},u=Object.fromEntries(Object.entries(c).filter(([,y])=>y!==void 0));if(Object.keys(u).length===0)throw new Error("No updates provided. Use --name, --description, --duration, --screen-size, or --schema");let h=await new g(o).updateTemplate(r,t,c);if(e.json){let y=a.result(h);l.output(y)}else l.success("Template updated successfully!"),console.log(`
207
+ Template ID: `+h.id),console.log(" Name: "+h.name+`
208
+ `)}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("delete").alias("rm").description("Delete a template permanently").argument("<templateId>","Template ID to delete").option("--project <id>","Project ID (required)").option("--force","Skip confirmation prompt").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{e.force||(console.log("\u26A0\uFE0F This will permanently delete the template."),console.log(`Use --force to skip this prompt.
209
+ `),process.exit(0));let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=e.project||o.project;if(!r)throw new Error("Project ID required (use --project or set default project)");let l=new a({json:e.json,color:o.color});if(await new g(o).deleteTemplate(r,t),e.json){let c=a.result({success:!0});l.output(c)}else l.success("Template deleted successfully!")}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}x();k();I();S();import{Command as Ro}from"commander";import Xe from"cli-table3";function Ye(){let s=new Ro("github");return s.alias("gh"),s.description("GitHub integration commands"),s.command("link").description("Link a GitHub repository to your project").argument("<repo>","Repository in format owner/repo").option("--project <id>","Project ID (required)").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let n=new d,o=await n.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=e.project||o.project;if(!r)throw new Error("Project ID required (use --project or set default project)");let l=new a({json:e.json,color:o.color}),i=t.match(/^([^/]+)\/([^/]+)$/);if(!i)throw new Error("Invalid repository format. Use: owner/repo");let[,c,u]=i,h=await new g(o).linkGithubRepo(r,c,u);if(e.json){let y=a.result(h);l.output(y)}else l.success("GitHub repository linked successfully!"),console.log(`
210
+ Repository: `+t),console.log(" Project: "+r),console.log(`
206
211
  \u{1F4A1} Now you can:`),console.log(" - List issues: runhuman github issues "+t),console.log(" - Test an issue: runhuman github test <issueNumber> --repo "+t),console.log(" - Bulk test: runhuman github bulk-test --repo "+t+`
207
212
  `),await n.saveProjectConfig({githubRepo:t}),console.log(`\u2713 Repository saved to project config (.runhumanrc)
208
- `)}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r.command("repos").alias("repositories").description("List linked GitHub repositories").option("--project <id>","Filter by project").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let n=await new m().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=t.project||n.project,s=new a({json:t.json,color:n.color}),i=await new d(n).listGithubRepos(o);if(t.json){let c=a.result({repositories:i});s.output(c)}else{console.log(`
213
+ `)}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("repos").alias("repositories").description("List linked GitHub repositories").option("--project <id>","Filter by project").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let n=await new d().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=t.project||n.project,r=new a({json:t.json,color:n.color}),i=await new g(n).listGithubRepos(o);if(t.json){let c=a.result({repositories:i});r.output(c)}else{console.log(`
209
214
  \u{1F517} Linked GitHub Repositories (${i.length})
210
- `);let c=new ke({head:["Repository","Project","Linked"].map(l=>l),colWidths:[35,30,20]});i.forEach(l=>{c.push([l.fullName,l.projectId,new Date(l.linkedAt).toLocaleDateString()])}),console.log(c.toString()),console.log(`
215
+ `);let c=new Xe({head:["Repository","Project","Linked"].map(u=>u),colWidths:[35,30,20]});i.forEach(u=>{c.push([u.fullName,u.projectId,new Date(u.linkedAt).toLocaleDateString()])}),console.log(c.toString()),console.log(`
211
216
  \u{1F4A1} Test an issue: runhuman github test <issueNumber> --repo owner/repo
212
- `)}}catch(e){let n=p(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),r.command("issues").description("List GitHub issues for a repository").argument("<repo>","Repository in format owner/repo").option("--state <state>","Filter by state (open/closed/all)","open").option("--labels <labels>","Filter by comma-separated labels").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=new a({json:e.json,color:o.color}),u=t.match(/^([^/]+)\/([^/]+)$/);if(!u)throw new Error("Invalid repository format. Use: owner/repo");let[,i,c]=u,g=await new d(o).listGithubIssues(i,c,{state:e.state,labels:e.labels?.split(",")});if(e.json){let f=a.result({issues:g});s.output(f)}else{console.log(`
213
- \u{1F41B} GitHub Issues for ${t} (${g.length})
214
- `);let f=new ke({head:["#","Title","State","Labels","Created"].map(h=>h),colWidths:[8,40,10,20,15]});g.forEach(h=>{let U=h.labels?.join(", ")||"-",y=U.length>18?U.substring(0,15)+"...":U;f.push(["#"+h.number,h.title.length>38?h.title.substring(0,35)+"...":h.title,h.state,y,new Date(h.createdAt).toLocaleDateString()])}),console.log(f.toString()),console.log(`
217
+ `)}}catch(e){let n=m(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),s.command("issues").description("List GitHub issues for a repository").argument("<repo>","Repository in format owner/repo").option("--state <state>","Filter by state (open/closed/all)","open").option("--labels <labels>","Filter by comma-separated labels").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new a({json:e.json,color:o.color}),l=t.match(/^([^/]+)\/([^/]+)$/);if(!l)throw new Error("Invalid repository format. Use: owner/repo");let[,i,c]=l,f=await new g(o).listGithubIssues(i,c,{state:e.state,labels:e.labels?.split(",")});if(e.json){let h=a.result({issues:f});r.output(h)}else{console.log(`
218
+ \u{1F41B} GitHub Issues for ${t} (${f.length})
219
+ `);let h=new Xe({head:["#","Title","State","Labels","Created"].map(y=>y),colWidths:[8,40,10,20,15]});f.forEach(y=>{let j=y.labels?.join(", ")||"-",C=j.length>18?j.substring(0,15)+"...":j;h.push(["#"+y.number,y.title.length>38?y.title.substring(0,35)+"...":y.title,y.state,C,new Date(y.createdAt).toLocaleDateString()])}),console.log(h.toString()),console.log(`
215
220
  \u{1F4A1} Test an issue: runhuman github test <issueNumber> --repo `+t+`
216
- `)}}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r.command("test").description("Create a QA test job for a GitHub issue").argument("<issueNumber>","Issue number to test").option("--repo <owner/repo>","Repository (or use default from config)").option("--url <url>","URL to test (required)").option("--template <id>","Template ID to use").option("--sync","Wait for result before exiting").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new m().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=e.repo||o.githubRepo;if(!s)throw new Error("Repository required (use --repo or set default with: runhuman github link)");if(!e.url)throw new Error("URL required (use --url)");let u=new a({json:e.json,color:o.color}),i=s.match(/^([^/]+)\/([^/]+)$/);if(!i)throw new Error("Invalid repository format. Use: owner/repo");let[,c,l]=i,g=new d(o),f=await g.getGithubIssue(c,l,parseInt(t)),h={url:e.url,description:`Test GitHub issue #${t}: ${f.title}
221
+ `)}}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("test").description("Create a QA test job for a GitHub issue").argument("<issueNumber>","Issue number to test").option("--repo <owner/repo>","Repository (or use default from config)").option("--url <url>","URL to test (required)").option("--template <id>","Template ID to use").option("--sync","Wait for result before exiting").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new d().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=e.repo||o.githubRepo;if(!r)throw new Error("Repository required (use --repo or set default with: runhuman github link)");if(!e.url)throw new Error("URL required (use --url)");let l=new a({json:e.json,color:o.color}),i=r.match(/^([^/]+)\/([^/]+)$/);if(!i)throw new Error("Invalid repository format. Use: owner/repo");let[,c,u]=i,f=new g(o),h=await f.getGithubIssue(c,u,parseInt(t));if(!o.project)throw new Error("No project selected. Run: runhuman projects switch <project-id>");let y={projectId:o.project,url:e.url,description:`Test GitHub issue #${t}: ${h.title}
217
222
 
218
- ${f.body}`,metadata:{githubIssue:{owner:c,repo:l,number:parseInt(t),url:f.url}},templateId:e.template},U=await g.createJob(h);if(e.json){let y=a.result(U);u.output(y)}else if(u.success("QA test job created for issue #"+t),console.log(`
219
- Job ID: `+U.jobId),console.log(" Issue: #"+t+" - "+f.title),console.log(" Status: "+U.status),console.log(" URL: "+e.url),console.log(`
220
- \u{1F4A1} Check status: runhuman status `+U.jobId+`
221
- `),e.sync){let{waitForJob:y}=await Promise.resolve().then(()=>(W(),X));await y(U.jobId,g,u,600)}}catch(n){let o=p(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),r.command("bulk-test").description("Create QA test jobs for multiple GitHub issues").option("--repo <owner/repo>","Repository (or use default from config)").option("--url <url>","URL to test (required)").option("--labels <labels>","Filter issues by comma-separated labels").option("--state <state>","Filter by state (open/closed/all)","open").option("--template <id>","Template ID to use").option("--limit <number>","Maximum number of jobs to create","10").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let n=await new m().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=t.repo||n.githubRepo;if(!o)throw new Error("Repository required (use --repo or set default with: runhuman github link)");if(!t.url)throw new Error("URL required (use --url)");let s=new a({json:t.json,color:n.color}),u=o.match(/^([^/]+)\/([^/]+)$/);if(!u)throw new Error("Invalid repository format. Use: owner/repo");let[,i,c]=u,l=new d(n),g=await l.listGithubIssues(i,c,{state:t.state,labels:t.labels?.split(",")}),f=parseInt(t.limit),h=g.slice(0,f);if(h.length===0){console.log(`No issues found matching the criteria.
222
- `);return}console.log(`
223
- \u{1F680} Creating ${h.length} test jobs...
224
- `);let U=[];for(let y of h){let D={url:t.url,description:`Test GitHub issue #${y.number}: ${y.title}
223
+ ${h.body}`,metadata:{githubIssue:{owner:c,repo:u,number:parseInt(t),url:h.url}},templateId:e.template},j=await f.createJob(y);if(e.json){let C=a.result(j);l.output(C)}else if(l.success("QA test job created for issue #"+t),console.log(`
224
+ Job ID: `+j.jobId),console.log(" Issue: #"+t+" - "+h.title),console.log(" Status: "+j.status),console.log(" URL: "+e.url),console.log(`
225
+ \u{1F4A1} Check status: runhuman status `+j.jobId+`
226
+ `),e.sync){let{waitForJob:C}=await Promise.resolve().then(()=>(Q(),ne));await C(j.jobId,f,l,600)}}catch(n){let o=m(n);new a({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("bulk-test").description("Create QA test jobs for multiple GitHub issues").option("--repo <owner/repo>","Repository (or use default from config)").option("--url <url>","URL to test (required)").option("--labels <labels>","Filter issues by comma-separated labels").option("--state <state>","Filter by state (open/closed/all)","open").option("--template <id>","Template ID to use").option("--limit <number>","Maximum number of jobs to create","10").option("--json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let n=await new d().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=t.repo||n.githubRepo;if(!o)throw new Error("Repository required (use --repo or set default with: runhuman github link)");if(!t.url)throw new Error("URL required (use --url)");let r=new a({json:t.json,color:n.color}),l=o.match(/^([^/]+)\/([^/]+)$/);if(!l)throw new Error("Invalid repository format. Use: owner/repo");let[,i,c]=l,u=new g(n),f=await u.listGithubIssues(i,c,{state:t.state,labels:t.labels?.split(",")}),h=parseInt(t.limit),y=f.slice(0,h);if(y.length===0){console.log(`No issues found matching the criteria.
227
+ `);return}if(!n.project)throw new Error("No project selected. Run: runhuman projects switch <project-id>");console.log(`
228
+ \u{1F680} Creating ${y.length} test jobs...
229
+ `);let j=[];for(let C of y){let N={projectId:n.project,url:t.url,description:`Test GitHub issue #${C.number}: ${C.title}
225
230
 
226
- ${y.body}`,metadata:{githubIssue:{owner:i,repo:c,number:y.number,url:y.url}},templateId:t.template};try{let P=await l.createJob(D);U.push({issue:y.number,jobId:P.jobId,status:"created"}),console.log(` \u2713 Issue #${y.number} \u2192 Job ${P.jobId}`)}catch(P){let K=p(P);U.push({issue:y.number,error:K.message,status:"failed"}),console.log(` \u2717 Issue #${y.number} \u2192 Failed: ${K.message}`)}}if(t.json){let y=a.result({jobs:U});s.output(y)}else{let y=U.filter(P=>P.status==="created").length,D=U.filter(P=>P.status==="failed").length;console.log(`
227
- \u2713 Created ${y} jobs`),D>0&&console.log(`\u2717 Failed ${D} jobs`),console.log(`
231
+ ${C.body}`,metadata:{githubIssue:{owner:i,repo:c,number:C.number,url:C.url}},templateId:t.template};try{let T=await u.createJob(N);j.push({issue:C.number,jobId:T.jobId,status:"created"}),console.log(` \u2713 Issue #${C.number} \u2192 Job ${T.jobId}`)}catch(T){let H=m(T);j.push({issue:C.number,error:H.message,status:"failed"}),console.log(` \u2717 Issue #${C.number} \u2192 Failed: ${H.message}`)}}if(t.json){let C=a.result({jobs:j});r.output(C)}else{let C=j.filter(T=>T.status==="created").length,N=j.filter(T=>T.status==="failed").length;console.log(`
232
+ \u2713 Created ${C} jobs`),N>0&&console.log(`\u2717 Failed ${N} jobs`),console.log(`
228
233
  \u{1F4A1} Check all jobs: runhuman list
229
- `)}}catch(e){let n=p(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),r}b();I();import{existsSync as Pt}from"fs";import{join as St}from"path";import{execSync as Rt}from"child_process";import M from"inquirer";import k from"chalk";import Pe from"ora";j();async function Se(){let r=new m,t=new a({color:!0});console.log(""),console.log(k.bold("Runhuman")+" - Human QA testing for your apps"),console.log("");let e=await Ae(r);if(!e.isAuthenticated){let{shouldLogin:n}=await M.prompt([{type:"confirm",name:"shouldLogin",message:"You're not logged in. Would you like to sign in?",default:!0}]);if(!n){console.log(`
230
- Run `+k.cyan("runhuman login")+` when you're ready to sign in.
231
- `);return}await xt(r,t),Object.assign(e,await Ae(r))}console.log(k.green("Logged in as "+e.userEmail)+`
232
- `),e.isRunhumanProject?await Ot(r,e):await Dt(r,e)}async function Ae(r){let t={isAuthenticated:!1,isGitRepo:!1,isRunhumanProject:!1},e=r.loadCredentials();if(e?.accessToken)try{let o=await r.loadConfig({apiKey:e.accessToken}),u=await new d(o).getCurrentUser();t.isAuthenticated=!0,t.userEmail=u.email}catch{t.isAuthenticated=!1}try{Rt("git rev-parse --is-inside-work-tree",{stdio:"ignore"}),t.isGitRepo=!0}catch{t.isGitRepo=!1}let n=St(process.cwd(),".runhumanrc");if(Pt(n)){t.isRunhumanProject=!0;try{let o=await r.loadConfig();t.projectConfig={defaultUrl:o.defaultUrl,githubRepo:o.githubRepo}}catch{}}return t}async function xt(r,t){let e=await r.loadConfig(),n=e.apiUrl||"https://runhuman.com";console.log("");let o=Pe("Opening browser for authentication...").start();try{let s=await V({apiUrl:n,autoOpenBrowser:e.autoOpenBrowser!==!1});o.stop(),r.saveCredentials({accessToken:s.token});let u=await r.loadConfig({apiKey:s.token}),c=await new d(u).getCurrentUser();r.saveUserInfo(c),t.success("Successfully logged in!"),console.log("")}catch(s){throw o.stop(),s}}async function Ot(r,t){console.log(k.dim("Runhuman project detected")),t.projectConfig?.defaultUrl&&console.log(k.dim("URL: "+t.projectConfig.defaultUrl)),console.log("");let{action:e}=await M.prompt([{type:"list",name:"action",message:"What would you like to do?",choices:[{name:"Quick test a URL",value:"quick-test"},{name:"Run a template",value:"run-template"},{name:"View recent jobs",value:"list-jobs"},new M.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await Re(r,t);break;case"run-template":console.log(`
233
- Run `+k.cyan("runhuman templates")+" to see available templates."),console.log("Then run "+k.cyan("runhuman create --template <name>")+` to use one.
234
+ `)}}catch(e){let n=m(e);new a({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),s}k();x();import{existsSync as Eo}from"fs";import{join as Oo}from"path";import{execSync as vo}from"child_process";import J from"inquirer";import U from"chalk";import tt from"ora";S();async function ot(){let s=new d,t=new a({color:!0});console.log(""),console.log(U.bold("Runhuman")+" - Human QA testing for your apps"),console.log("");let e=await et(s);if(!e.isAuthenticated){let{shouldLogin:n}=await J.prompt([{type:"confirm",name:"shouldLogin",message:"You're not logged in. Would you like to sign in?",default:!0}]);if(!n){console.log(`
235
+ Run `+U.cyan("runhuman login")+` when you're ready to sign in.
236
+ `);return}await No(s,t),Object.assign(e,await et(s))}console.log(U.green("Logged in as "+e.userEmail)+`
237
+ `),e.isRunhumanProject?await Do(s,e):await $o(s,e)}async function et(s){let t={isAuthenticated:!1,isGitRepo:!1,isRunhumanProject:!1},e=s.loadCredentials();if(e?.accessToken)try{let o=await s.loadConfig({apiKey:e.accessToken}),l=await new g(o).getCurrentUser();t.isAuthenticated=!0,t.userEmail=l.email}catch{t.isAuthenticated=!1}try{vo("git rev-parse --is-inside-work-tree",{stdio:"ignore"}),t.isGitRepo=!0}catch{t.isGitRepo=!1}let n=Oo(process.cwd(),".runhumanrc");if(Eo(n)){t.isRunhumanProject=!0;try{let o=await s.loadConfig();t.projectConfig={defaultUrl:o.defaultUrl,githubRepo:o.githubRepo}}catch{}}return t}async function No(s,t){let e=await s.loadConfig(),n=e.apiUrl||"https://runhuman.com";console.log("");let o=tt("Opening browser for authentication...").start();try{let r=await X({apiUrl:n,autoOpenBrowser:e.autoOpenBrowser!==!1});o.stop(),s.saveCredentials({accessToken:r.token});let l=await s.loadConfig({apiKey:r.token}),c=await new g(l).getCurrentUser();s.saveUserInfo(c),t.success("Successfully logged in!"),console.log("")}catch(r){throw o.stop(),r}}async function Do(s,t){console.log(U.dim("Runhuman project detected")),t.projectConfig?.defaultUrl&&console.log(U.dim("URL: "+t.projectConfig.defaultUrl)),console.log("");let{action:e}=await J.prompt([{type:"list",name:"action",message:"What would you like to do?",choices:[{name:"Quick test a URL",value:"quick-test"},{name:"Run a template",value:"run-template"},{name:"View recent jobs",value:"list-jobs"},new J.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await nt(s,t);break;case"run-template":console.log(`
238
+ Run `+U.cyan("runhuman templates")+" to see available templates."),console.log("Then run "+U.cyan("runhuman create --template <name>")+` to use one.
234
239
  `);break;case"list-jobs":console.log(`
235
- Run `+k.cyan("runhuman list")+` to see your recent jobs.
236
- `);break;case"exit":break}}async function Dt(r,t){t.isGitRepo?console.log(k.dim("Git repository detected (not yet set up with Runhuman)")):console.log(k.dim("Not in a git repository")),console.log("");let{action:e}=await M.prompt([{type:"list",name:"action",message:"What would you like to do?",choices:[{name:"Quick test a URL",value:"quick-test"},{name:"Set up this repo with Runhuman",value:"setup-repo",disabled:!t.isGitRepo},{name:"Connect a GitHub repo",value:"connect-github"},new M.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await Re(r,t);break;case"setup-repo":await Et(r);break;case"connect-github":await Nt(r);break;case"exit":break}}async function Re(r,t){let e=t.projectConfig?.defaultUrl||"",n=await M.prompt([{type:"input",name:"url",message:"URL to test:",default:e||void 0,validate:s=>{if(!s.trim())return"URL is required";try{return new URL(s),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"description",message:"What should we test? (describe in plain English):",validate:s=>s.trim()?!0:"Description is required"}]),o=Pe("Creating test job...").start();try{let s=r.loadCredentials(),u=await r.loadConfig({apiKey:s?.accessToken}),c=await new d(u).createJob({url:n.url,description:n.description});o.succeed("Test job created!"),console.log(""),console.log(" Job ID: "+k.cyan(c.jobId)),console.log(" Status: "+c.status),console.log(""),console.log("A human tester will test your app shortly."),console.log("Run "+k.cyan(`runhuman wait ${c.jobId}`)+" to wait for results."),console.log("")}catch(s){throw o.fail("Failed to create test job"),s}}async function Et(r){console.log(""),console.log("Setting up Runhuman in the current repository..."),console.log("");let t=await M.prompt([{type:"input",name:"defaultUrl",message:"Default URL to test (optional):"}]);await r.saveProjectConfig({defaultUrl:t.defaultUrl||void 0,defaultDuration:5,defaultScreenSize:"desktop"}),console.log(""),console.log(k.green("Created .runhumanrc")),console.log(""),console.log("Next steps:"),console.log(" 1. Install the GitHub App to enable @runhuman comments"),console.log(" "+k.cyan("runhuman github connect")),console.log(""),console.log(" 2. Create your first test:"),console.log(" "+k.cyan('runhuman create https://your-app.com -d "Test login flow"')),console.log("")}async function Nt(r){let n=`${(await r.loadConfig()).apiUrl||"https://runhuman.com"}/dashboard/settings/github`;console.log(""),console.log("To connect your GitHub repos, install the Runhuman GitHub App:"),console.log(""),console.log(" "+k.cyan(n)),console.log(""),console.log("After installation, you can comment "+k.bold("@runhuman")+" on any issue"),console.log("to trigger a QA test."),console.log("")}import{readFileSync as Tt}from"fs";import{join as Mt,dirname as Jt}from"path";import{fileURLToPath as $t}from"url";var Lt=$t(import.meta.url),Kt=Jt(Lt),Ft=Mt(Kt,"../package.json"),Ht=JSON.parse(Tt(Ft,"utf-8")),Gt=Ht.version,C=new vt;C.name("runhuman").description("CLI for Runhuman - AI-orchestrated human QA testing").version(Gt);C.addCommand(ne());C.addCommand(re());C.addCommand(Z());C.addCommand(se());C.addCommand(ie());C.addCommand(ae());C.addCommand(ue());C.addCommand(ge());C.addCommand(de());C.addCommand(fe());C.addCommand(he());C.addCommand(we());C.addCommand(je());C.addCommand(Ce());C.addCommand(be());C.addCommand(Ue());C.addCommand(Ie());process.argv.slice(2).length?C.parse(process.argv):Se().catch(r=>{console.error(r.message),process.exit(1)});
240
+ Run `+U.cyan("runhuman list")+` to see your recent jobs.
241
+ `);break;case"exit":break}}async function $o(s,t){t.isGitRepo?console.log(U.dim("Git repository detected (not yet set up with Runhuman)")):console.log(U.dim("Not in a git repository")),console.log("");let{action:e}=await J.prompt([{type:"list",name:"action",message:"What would you like to do?",choices:[{name:"Quick test a URL",value:"quick-test"},{name:"Set up this repo with Runhuman",value:"setup-repo",disabled:!t.isGitRepo},{name:"Connect a GitHub repo",value:"connect-github"},new J.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await nt(s,t);break;case"setup-repo":await Mo(s);break;case"connect-github":await Lo(s);break;case"exit":break}}async function nt(s,t){let e=t.projectConfig?.defaultUrl||"",n=await J.prompt([{type:"input",name:"url",message:"URL to test:",default:e||void 0,validate:r=>{if(!r.trim())return"URL is required";try{return new URL(r),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"description",message:"What should we test? (describe in plain English):",validate:r=>r.trim()?!0:"Description is required"}]),o=tt("Creating test job...").start();try{let r=s.loadCredentials(),l=await s.loadConfig({apiKey:r?.accessToken});if(!l.project){o.fail("No project selected"),console.log(`
242
+ Run: runhuman projects switch <project-id>`);return}let c=await new g(l).createJob({projectId:l.project,url:n.url,description:n.description});o.succeed("Test job created!"),console.log(""),console.log(" Job ID: "+U.cyan(c.jobId)),console.log(" Status: "+c.status),console.log(""),console.log("A human tester will test your app shortly."),console.log("Run "+U.cyan(`runhuman wait ${c.jobId}`)+" to wait for results."),console.log("")}catch(r){throw o.fail("Failed to create test job"),r}}async function Mo(s){console.log(""),console.log("Setting up Runhuman in the current repository..."),console.log("");let t=await J.prompt([{type:"input",name:"defaultUrl",message:"Default URL to test (optional):"}]);await s.saveProjectConfig({defaultUrl:t.defaultUrl||void 0,defaultDuration:5,defaultScreenSize:"desktop"}),console.log(""),console.log(U.green("Created .runhumanrc")),console.log(""),console.log("Next steps:"),console.log(" 1. Install the GitHub App to enable @runhuman comments"),console.log(" "+U.cyan("runhuman github connect")),console.log(""),console.log(" 2. Create your first test:"),console.log(" "+U.cyan('runhuman create https://your-app.com -d "Test login flow"')),console.log("")}async function Lo(s){let n=`${(await s.loadConfig()).apiUrl||"https://runhuman.com"}/dashboard/settings/github`;console.log(""),console.log("To connect your GitHub repos, install the Runhuman GitHub App:"),console.log(""),console.log(" "+U.cyan(n)),console.log(""),console.log("After installation, you can comment "+U.bold("@runhuman")+" on any issue"),console.log("to trigger a QA test."),console.log("")}import{readFileSync as _o}from"fs";import{join as Fo,dirname as Ko}from"path";import{fileURLToPath as Ho}from"url";var zo=Ho(import.meta.url),Go=Ko(zo),Wo=Fo(Go,"../package.json"),qo=JSON.parse(_o(Wo,"utf-8")),Vo=qo.version,A=new Jo;A.name("runhuman").description("CLI for Runhuman - AI-orchestrated human QA testing").version(Vo);A.addCommand(Oe());A.addCommand(ve());A.addCommand(oe());A.addCommand(Ne());A.addCommand(De());A.addCommand($e());A.addCommand(Je());A.addCommand(Ke());A.addCommand(He());A.addCommand(ze());A.addCommand(Ge());A.addCommand(qe());A.addCommand(Ve());A.addCommand(Be());A.addCommand(Qe());A.addCommand(Ze());A.addCommand(Ye());process.argv.slice(2).length?A.parse(process.argv):ot().catch(s=>{console.error(s.message),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runhuman",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "CLI for Runhuman - AI-orchestrated human QA testing",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",