runhuman 1.5.0 → 1.6.0

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 +86 -86
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,96 +1,96 @@
1
1
  #!/usr/bin/env node
2
- var Ct=Object.defineProperty;var L=(s,t)=>()=>(s&&(t=s(s=0)),t);var It=(s,t)=>{for(var e in t)Ct(s,e,{get:t[e],enumerable:!0})};function At(s){return s instanceof k}function f(s){return At(s)?{message:s.message,exitCode:s.exitCode,details:s.details}:s instanceof Error?{message:s.message,exitCode:1}:{message:String(s),exitCode:1}}var k,te,oe,M,ne,re,I=L(()=>{"use strict";k=class extends Error{constructor(e,r=1,o){super(e);this.exitCode=r;this.details=o;this.name="CliError"}},te=class extends k{constructor(t="Authentication failed",e){super(t,2,e),this.name="AuthenticationError"}},oe=class extends k{constructor(t="Resource not found",e){super(t,3,e),this.name="NotFoundError"}},M=class extends k{constructor(t="Validation failed",e){super(t,4,e),this.name="ValidationError"}},ne=class extends k{constructor(t="Operation timed out",e){super(t,5,e),this.name="TimeoutError"}},re=class extends k{constructor(t="Insufficient balance",e){super(t,6,e),this.name="InsufficientBalanceError"}}});function a(s){return{pattern:s,build:e=>{if(!e)return s;let r=s;for(let[o,n]of Object.entries(e))n!==void 0&&(r=r.replace(`:${o}?`,n),r=r.replace(`:${o}`,n));return r=r.replace(/\/:[^/]+\?/g,""),r=r.replace(/\/+/g,"/"),r.length>1&&r.endsWith("/")&&(r=r.slice(0,-1)),r}}}var se=L(()=>{"use strict"});var _,Pe=L(()=>{"use strict";se();_={dashboard:a("/dashboard"),managePlan:a("/dashboard/manage-plan"),plans:a("/dashboard/plans"),jobSimple:a("/dashboard/jobs/:jobId"),admin:a("/dashboard/admin"),organizations:a("/dashboard/organizations"),organization:a("/dashboard/organizations/:organizationId"),organizationMembers:a("/dashboard/organizations/:organizationId/members"),organizationSettings:a("/dashboard/organizations/:organizationId/settings"),organizationUsage:a("/dashboard/organizations/:organizationId/usage"),organizationProjects:a("/dashboard/organizations/:organizationId/projects"),organizationJobs:a("/dashboard/organizations/:organizationId/jobs"),organizationApiKeys:a("/dashboard/organizations/:organizationId/api-keys"),organizationIntegrations:a("/dashboard/organizations/:organizationId/integrations"),organizationGitHub:a("/dashboard/organizations/:organizationId/github"),organizationSso:a("/dashboard/organizations/:organizationId/sso"),organizationSchedules:a("/dashboard/organizations/:organizationId/schedules"),organizationImportGitHub:a("/dashboard/organizations/:organizationId/import"),settings:a("/dashboard/settings"),settingsAccount:a("/dashboard/settings/account"),tester:a("/tester"),testerJobs:a("/tester/jobs"),testerSettings:a("/tester/settings"),testerTest:a("/tester/test/:jobId"),project:a("/dashboard/:projectId"),playground:a("/dashboard/:projectId/playground"),jobs:a("/dashboard/:projectId/jobs"),job:a("/dashboard/:projectId/jobs/:jobId"),templates:a("/dashboard/:projectId/templates"),template:a("/dashboard/:projectId/templates/:templateId"),issues:a("/dashboard/:projectId/issues"),issue:a("/dashboard/:projectId/issues/:issueNumber"),issueSessions:a("/dashboard/:projectId/issue-sessions"),issueSession:a("/dashboard/:projectId/issue-sessions/:sessionId"),projectSettings:a("/dashboard/:projectId/settings"),schedules:a("/dashboard/:projectId/schedules"),flowCharts:a("/dashboard/:projectId/flowcharts"),flowChart:a("/dashboard/:projectId/flowcharts/:flowchartId"),flowChartView:a("/view/flowchart/:projectId/:flowchartId"),invite:a("/invite/:token"),quickStart:a("/start"),publicJob:a("/j/:jobId/:token"),statuspage:a("/statuspage"),docs:a("/docs/setup"),pricing:a("/pricing"),home:a("/"),learn:a("/learn"),learnLesson:a("/learn/:lessonSlug")}});var w,Ue=L(()=>{"use strict";se();w={jobs:a("/jobs"),job:a("/jobs/:jobId"),jobCancel:a("/jobs/:jobId/cancel"),jobCreateIssue:a("/jobs/:jobId/create-issue"),jobStatus:a("/jobs/:jobId/status"),jobArtifact:a("/jobs/:jobId/artifacts/:artifactType"),jobFeedback:a("/jobs/:jobId/feedback"),jobShare:a("/jobs/:jobId/share"),jobClaim:a("/jobs/:jobId/claim"),run:a("/run"),publicJob:a("/public/jobs/:jobId/:token"),testerJob:a("/tester/jobs/:jobId"),testerJobUploadUrls:a("/tester/jobs/:jobId/upload-urls"),testerJobProcessResults:a("/tester/jobs/:jobId/process-results"),testerJobProcessingStatus:a("/tester/jobs/:jobId/processing/:processingJobId"),testerJobExtractFrames:a("/tester/jobs/:jobId/extract-frames"),testerJobAbort:a("/tester/jobs/:jobId/abort"),testerJobInvalid:a("/tester/jobs/:jobId/invalid"),testerJobPhase:a("/tester/jobs/:jobId/phase"),livekitTesterToken:a("/tester/jobs/:jobId/livekit-token"),livekitEndSession:a("/tester/jobs/:jobId/livekit-end"),livekitTranscript:a("/tester/jobs/:jobId/livekit-transcript"),livekitViewerToken:a("/jobs/:jobId/livekit-viewer-token"),livekitStatus:a("/jobs/:jobId/livekit-status"),livekitRecording:a("/jobs/:jobId/livekit-recording"),keys:a("/keys"),key:a("/keys/:keyId"),keyRevoke:a("/keys/:keyId/revoke"),keyInfo:a("/key-info"),pats:a("/pats"),pat:a("/pats/:patId"),patRevoke:a("/pats/:patId/revoke"),patInfo:a("/pat-info"),projects:a("/projects"),project:a("/projects/:projectId"),projectJobs:a("/projects/:projectId/jobs"),projectApiKeys:a("/projects/:projectId/api-keys"),projectTransfer:a("/projects/:projectId/transfer"),projectTemplates:a("/projects/:projectId/templates"),projectTemplate:a("/projects/:projectId/templates/:templateId"),projectFlowCharts:a("/projects/:projectId/flowcharts"),projectFlowChartsUpload:a("/projects/:projectId/flowcharts/upload"),projectFlowChart:a("/projects/:projectId/flowcharts/:flowchartId"),projectFlowChartData:a("/projects/:projectId/flowcharts/:flowchartId/data"),projectFlowChartChat:a("/projects/:projectId/flowcharts/:flowchartId/chat"),projectFlowChartShare:a("/projects/:projectId/flowcharts/:flowchartId/share"),projectSchedules:a("/projects/:projectId/schedules"),projectSchedule:a("/projects/:projectId/schedules/:scheduleId"),projectScheduleExecutions:a("/projects/:projectId/schedules/:scheduleId/executions"),bulkCreateProjects:a("/projects/bulk"),githubOAuthAuthorize:a("/github/oauth/authorize"),githubCallback:a("/github/oauth/callback"),githubLink:a("/github/link"),githubIssuesByRepo:a("/github/issues/:owner/:repo"),githubIssueByRepo:a("/github/issues/:owner/:repo/:issueNumber"),githubIssues:a("/github/issues"),githubIssue:a("/github/issues/:issueNumber"),githubIssueComments:a("/github/issues/:issueNumber/comments"),githubIssueLabels:a("/github/issues/labels"),githubIssueAssignees:a("/github/issues/assignees"),githubIssueTest:a("/github/issues/test"),githubIssuesBulkTest:a("/github/issues/bulk-test"),githubTestSessions:a("/github/issues/test-sessions"),githubTestSession:a("/github/issues/test-sessions/:sessionId"),githubTestSessionSeen:a("/github/issues/test-sessions/:sessionId/seen"),githubTestSessionsCounts:a("/github/issues/test-sessions/counts"),githubBulkTest:a("/github/bulk-test"),githubWebhooks:a("/github/webhooks"),authSync:a("/auth/sync"),authMe:a("/auth/me"),authStartup:a("/auth/startup"),authDeleteAccount:a("/auth/account"),authDeletionPreview:a("/auth/account/deletion-preview"),billingBalance:a("/billing/balance"),billingHasCredits:a("/billing/has-credits"),billingPortal:a("/billing/portal"),billingCheckout:a("/billing/checkout"),billingSubscription:a("/billing/subscription"),billingChangePlan:a("/billing/change-plan"),health:a("/health"),status:a("/status"),templates:a("/templates"),issueAnalyzer:a("/issue-analyzer"),prAnalyzer:a("/pr-analyzer"),logs:a("/logs"),relevantIssuesDiscover:a("/relevant-issues/discover"),relevantIssuesLatest:a("/relevant-issues/latest"),onboarding:a("/onboarding"),onboardingComplete:a("/onboarding/complete"),onboardingCheck:a("/onboarding/check"),search:a("/search"),testerApply:a("/tester/apply"),testerProfile:a("/tester/profile"),testerPublicProfile:a("/testers/:testerId/profile"),testerDownloadApp:a("/tester/download-app"),testerDownloadExtension:a("/tester/download-extension"),testerDownloadMobileApp:a("/tester/download-mobile-app"),testerAvailableJobs:a("/tester/jobs/available"),testerAvatarUploadUrl:a("/tester/profile/avatar-upload-url"),testerAppVersion:a("/tester/app-version"),testerPushToken:a("/tester/push-token"),testerDemoJob:a("/tester/demo-job"),extensionTokens:a("/tester/extension-tokens"),extensionToken:a("/tester/extension-tokens/:tokenId"),extensionStream:a("/extension/stream"),testerJobCorrelate:a("/tester/jobs/:jobId/correlate"),testerJobDataStatus:a("/tester/jobs/:jobId/data-status"),testerJobExtensionToken:a("/tester/jobs/:jobId/extension-token"),repoTemplates:a("/repos/:owner/:repo/templates"),repoTemplate:a("/repos/:owner/:repo/templates/:templateName"),organizations:a("/organizations"),organization:a("/organizations/:organizationId"),organizationMembers:a("/organizations/:organizationId/members"),organizationInvite:a("/organizations/:organizationId/invite"),organizationMember:a("/organizations/:organizationId/members/:userId"),organizationProjects:a("/organizations/:organizationId/projects"),organizationJobs:a("/organizations/:organizationId/jobs"),organizationTransferOwnership:a("/organizations/:organizationId/transfer-ownership"),organizationApiKeys:a("/organizations/:organizationId/api-keys"),organizationApiKey:a("/organizations/:organizationId/api-keys/:keyId"),organizationGitHubInstallations:a("/organizations/:organizationId/github/installations"),organizationGitHubInstallation:a("/organizations/:organizationId/github/installations/:installationId"),organizationGitHubRepos:a("/organizations/:organizationId/github/repos"),organizationGitHubInstallationRefresh:a("/organizations/:organizationId/github/installations/:installationId/refresh"),organizationGitHubRepoCheckAccess:a("/organizations/:organizationId/github/repos/check-access"),organizationGitHubRepoFindUrl:a("/organizations/:organizationId/github/repos/find-url"),organizationSchedules:a("/organizations/:organizationId/schedules"),organizationBilling:a("/organizations/:organizationId/billing"),transfersPending:a("/transfers/pending"),transfersOutgoing:a("/transfers/outgoing"),transferAccept:a("/transfers/:transferId/accept"),transferReject:a("/transfers/:transferId/reject"),transferCancel:a("/transfers/:transferId/cancel"),invite:a("/invites/:token"),inviteRedeem:a("/invites/:token/redeem"),agreements:a("/agreements"),agreementCheck:a("/agreements/check"),organizationSsoConnections:a("/organizations/:organizationId/sso/connections"),organizationSsoConnection:a("/organizations/:organizationId/sso/connections/:connectionId"),enterpriseInquiry:a("/enterprise/inquiry"),changelog:a("/changelog"),changelogUnreadCount:a("/changelog/unread-count"),changelogMarkRead:a("/changelog/mark-read"),telemetrySessions:a("/telemetry/sessions"),telemetrySessionEnd:a("/telemetry/sessions/:sessionId/end"),telemetryBatch:a("/telemetry/sessions/:sessionId/events"),telemetrySessionStatus:a("/telemetry/jobs/:jobId/status"),telemetryShortCodeResolve:a("/telemetry/short-codes/:code")}});var ge=L(()=>{"use strict";se();Pe();Ue()});import Et from"axios";var b,P=L(()=>{"use strict";I();ge();b=class{client;config;constructor(t){if(this.config=t,!t.apiUrl)throw new Error("apiUrl is required in CLI config");this.client=Et.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.method==="delete"&&!e.data&&delete e.headers["Content-Type"],e)),this.client.interceptors.response.use(e=>e,e=>{throw this.handleError(e)})}handleError(t){if(t.response){let e=t.response.status,r=t.response.data,o=r?.error||r?.message||t.message;switch(e){case 401:case 403:return new te(o,r);case 402:return new re(o,r);case 404:return new oe(o,r);case 400:case 422:return new M(o,r);default:return new k(o,1,r)}}return t.code==="ECONNABORTED"?new k("Request timeout",5):t.code==="ENOTFOUND"||t.code==="ECONNREFUSED"?new k("Cannot connect to Runhuman API",1):new k(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.job.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.jobCancel.build({jobId:t}))}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.patch(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.organizationApiKeys.build({organizationId:t}))).data}async createApiKey(t,e){return(await this.client.post(w.organizationApiKeys.build({organizationId:t}),{name:e})).data}async getApiKey(t){return(await this.client.get(w.key.build({keyId:t}))).data}async deleteApiKey(t){await this.client.delete(w.key.build({keyId:t}))}async listOrganizations(t){return(await this.client.get(w.organizations.build(),{params:t})).data}async getOrganization(t){return(await this.client.get(w.organization.build({organizationId:t}))).data}async getOrganizationBilling(t){return(await this.client.get(w.organizationBilling.build({organizationId:t}))).data}async listOrganizationProjects(t,e){return(await this.client.get(w.organizationProjects.build({organizationId:t}),{params:e})).data}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,r){return(await this.client.patch(w.projectTemplate.build({projectId:t,templateId:e}),r)).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 listGithubRepos(t,e){return(await this.client.get(w.organizationGitHubRepos.build({organizationId:t}),{params:e})).data}async listGithubIssues(t,e,r){return(await this.client.get(w.githubIssuesByRepo.build({owner:t,repo:e}),{params:r})).data}async getGithubIssue(t,e,r){return(await this.client.get(w.githubIssueByRepo.build({owner:t,repo:e,issueNumber:String(r)}))).data}async initiateTransfer(t,e){let r=await this.client.post(w.projectTransfer.build({projectId:t}),{toOrganizationId:e});return r.status===200?{type:"immediate",project:r.data}:{type:"pending",transfer:r.data}}async listPendingTransfers(){return(await this.client.get(w.transfersPending.build())).data}async listOutgoingTransfers(){return(await this.client.get(w.transfersOutgoing.build())).data}async acceptTransfer(t){return(await this.client.post(w.transferAccept.build({transferId:t}),{})).data}async rejectTransfer(t){return(await this.client.post(w.transferReject.build({transferId:t}),{})).data}async cancelTransfer(t){return(await this.client.post(w.transferCancel.build({transferId:t}),{})).data}}});import{cosmiconfig as vt}from"cosmiconfig";import{homedir as Rt}from"os";import{join as J}from"path";import{readFileSync as Y,writeFileSync as F,existsSync as D,mkdirSync as fe,chmodSync as kt}from"fs";function xt(s){return Ut.includes(s)}var Pt,z,H,Z,Ut,h,R=L(()=>{"use strict";Pt="runhuman",z=J(Rt(),".config","runhuman"),H=J(z,"config.json"),Z=J(z,"credentials.json"),Ut=["pretty","json","compact"];h=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(),r=e?.accessToken?{apiKey:e.accessToken}:{},o=Object.fromEntries(Object.entries(t).filter(([,c])=>c!==void 0));return{...this.getDefaults(),...this.globalConfig,...this.projectConfig,...r,...this.envConfig,...o}}getDefaults(){return{apiUrl:"https://runhuman.com",outputFormat:"pretty",color:!0,autoOpenBrowser:!0,defaultDuration:5,defaultDeviceClass:"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_ORGANIZATION&&(t.organization=process.env.RUNHUMAN_ORGANIZATION),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_DEVICE_CLASS&&(t.defaultDeviceClass=process.env.RUNHUMAN_DEFAULT_DEVICE_CLASS),process.env.RUNHUMAN_OUTPUT_FORMAT&&xt(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 vt(Pt).search(this.cwd))?.config||null}catch{return null}}loadGlobalConfig(){try{if(!D(H))return null;let t=Y(H,"utf-8");return JSON.parse(t)}catch{return null}}async get(t){return(await this.loadConfig())[t]}async set(t,e,r=!1){r?await this.setGlobalConfig(t,e):await this.setProjectConfig(t,e)}async setGlobalConfig(t,e){D(z)||fe(z,{recursive:!0});let r={};if(D(H)){let o=Y(H,"utf-8");r=JSON.parse(o)}r[t]=e,F(H,JSON.stringify(r,null,2))}async setProjectConfig(t,e){let r=J(this.cwd,".runhumanrc"),o={};if(D(r)){let n=Y(r,"utf-8");o=JSON.parse(n)}o[t]=e,F(r,JSON.stringify(o,null,2))}async saveProjectConfig(t){let e=J(this.cwd,".runhumanrc"),r={};if(D(e)){let n=Y(e,"utf-8");r=JSON.parse(n)}let o={...r,...t};F(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")&&D(H)&&F(H,"{}"),t==="project"||t==="all"){let e=J(this.cwd,".runhumanrc");D(e)&&F(e,"{}")}}saveCredentials(t){D(z)||fe(z,{recursive:!0}),F(Z,JSON.stringify(t,null,2));try{process.platform!=="win32"&&kt(Z,384)}catch{}}loadCredentials(){try{if(!D(Z))return null;let t=Y(Z,"utf-8");return JSON.parse(t)}catch{return null}}clearCredentials(){D(Z)&&F(Z,"{}")}saveUserInfo(t){let e=J(z,"user.json");D(z)||fe(z,{recursive:!0}),F(e,JSON.stringify(t,null,2))}loadUserInfo(){try{let t=J(z,"user.json");if(!D(t))return null;let e=Y(t,"utf-8");return JSON.parse(e)}catch{return null}}clearUserInfo(){let t=J(z,"user.json");D(t)&&F(t,"{}")}}});import V from"chalk";import Ot from"cli-table3";var Dt,he,p,v=L(()=>{"use strict";Dt=24,he=60,p=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 r={success:!1,error:{message:t,details:e},timestamp:new Date().toISOString()};console.error(JSON.stringify(r,null,2))}else console.error(this.color("red",`
2
+ var Dt=Object.defineProperty;var M=(i,t)=>()=>(i&&(t=i(i=0)),t);var Nt=(i,t)=>{for(var e in t)Dt(i,e,{get:t[e],enumerable:!0})};function zt(i){return i instanceof P}function f(i){return zt(i)?{message:i.message,exitCode:i.exitCode,details:i.details}:i instanceof Error?{message:i.message,exitCode:1}:{message:String(i),exitCode:1}}var P,ae,ce,$,le,ue,C=M(()=>{"use strict";P=class extends Error{constructor(e,r=1,o){super(e);this.exitCode=r;this.details=o;this.name="CliError"}},ae=class extends P{constructor(t="Authentication failed",e){super(t,2,e),this.name="AuthenticationError"}},ce=class extends P{constructor(t="Resource not found",e){super(t,3,e),this.name="NotFoundError"}},$=class extends P{constructor(t="Validation failed",e){super(t,4,e),this.name="ValidationError"}},le=class extends P{constructor(t="Operation timed out",e){super(t,5,e),this.name="TimeoutError"}},ue=class extends P{constructor(t="Insufficient balance",e){super(t,6,e),this.name="InsufficientBalanceError"}}});function c(i){return{pattern:i,build:e=>{if(!e)return i;let r=i;for(let[o,n]of Object.entries(e))n!==void 0&&(r=r.replace(`:${o}?`,n),r=r.replace(`:${o}`,n));return r=r.replace(/\/:[^/]+\?/g,""),r=r.replace(/\/+/g,"/"),r.length>1&&r.endsWith("/")&&(r=r.slice(0,-1)),r}}}var pe=M(()=>{"use strict"});var F,ze=M(()=>{"use strict";pe();F={dashboard:c("/dashboard"),managePlan:c("/dashboard/manage-plan"),plans:c("/dashboard/plans"),jobSimple:c("/dashboard/jobs/:jobId"),admin:c("/dashboard/admin"),organizations:c("/dashboard/organizations"),organization:c("/dashboard/organizations/:organizationId"),organizationMembers:c("/dashboard/organizations/:organizationId/members"),organizationSettings:c("/dashboard/organizations/:organizationId/settings"),organizationUsage:c("/dashboard/organizations/:organizationId/usage"),organizationProjects:c("/dashboard/organizations/:organizationId/projects"),organizationJobs:c("/dashboard/organizations/:organizationId/jobs"),organizationApiKeys:c("/dashboard/organizations/:organizationId/api-keys"),organizationIntegrations:c("/dashboard/organizations/:organizationId/integrations"),organizationGitHub:c("/dashboard/organizations/:organizationId/github"),organizationSso:c("/dashboard/organizations/:organizationId/sso"),organizationSchedules:c("/dashboard/organizations/:organizationId/schedules"),organizationImportGitHub:c("/dashboard/organizations/:organizationId/import"),settings:c("/dashboard/settings"),settingsAccount:c("/dashboard/settings/account"),tester:c("/tester"),testerJobs:c("/tester/jobs"),testerSettings:c("/tester/settings"),testerTest:c("/tester/test/:jobId"),project:c("/dashboard/:projectId"),playground:c("/dashboard/:projectId/playground"),jobs:c("/dashboard/:projectId/jobs"),job:c("/dashboard/:projectId/jobs/:jobId"),templates:c("/dashboard/:projectId/templates"),template:c("/dashboard/:projectId/templates/:templateId"),issues:c("/dashboard/:projectId/issues"),issue:c("/dashboard/:projectId/issues/:issueNumber"),issueSessions:c("/dashboard/:projectId/issue-sessions"),issueSession:c("/dashboard/:projectId/issue-sessions/:sessionId"),projectSettings:c("/dashboard/:projectId/settings"),schedules:c("/dashboard/:projectId/schedules"),flowCharts:c("/dashboard/:projectId/flowcharts"),flowChart:c("/dashboard/:projectId/flowcharts/:flowchartId"),flowChartView:c("/view/flowchart/:projectId/:flowchartId"),invite:c("/invite/:token"),quickStart:c("/start"),publicJob:c("/j/:jobId/:token"),statuspage:c("/statuspage"),docs:c("/docs/setup"),pricing:c("/pricing"),home:c("/"),learn:c("/learn"),learnLesson:c("/learn/:lessonSlug")}});var w,Me=M(()=>{"use strict";pe();w={jobs:c("/jobs"),job:c("/jobs/:jobId"),jobCancel:c("/jobs/:jobId/cancel"),jobCreateIssue:c("/jobs/:jobId/create-issue"),jobStatus:c("/jobs/:jobId/status"),jobArtifact:c("/jobs/:jobId/artifacts/:artifactType"),jobFeedback:c("/jobs/:jobId/feedback"),jobShare:c("/jobs/:jobId/share"),jobClaim:c("/jobs/:jobId/claim"),run:c("/run"),publicJob:c("/public/jobs/:jobId/:token"),testerJob:c("/tester/jobs/:jobId"),testerJobUploadUrls:c("/tester/jobs/:jobId/upload-urls"),testerJobProcessResults:c("/tester/jobs/:jobId/process-results"),testerJobProcessingStatus:c("/tester/jobs/:jobId/processing/:processingJobId"),testerJobExtractFrames:c("/tester/jobs/:jobId/extract-frames"),testerJobAbort:c("/tester/jobs/:jobId/abort"),testerJobInvalid:c("/tester/jobs/:jobId/invalid"),testerJobPhase:c("/tester/jobs/:jobId/phase"),livekitTesterToken:c("/tester/jobs/:jobId/livekit-token"),livekitEndSession:c("/tester/jobs/:jobId/livekit-end"),livekitTranscript:c("/tester/jobs/:jobId/livekit-transcript"),livekitViewerToken:c("/jobs/:jobId/livekit-viewer-token"),livekitStatus:c("/jobs/:jobId/livekit-status"),livekitRecording:c("/jobs/:jobId/livekit-recording"),keys:c("/keys"),key:c("/keys/:keyId"),keyRevoke:c("/keys/:keyId/revoke"),keyInfo:c("/key-info"),pats:c("/pats"),pat:c("/pats/:patId"),patRevoke:c("/pats/:patId/revoke"),patInfo:c("/pat-info"),projects:c("/projects"),project:c("/projects/:projectId"),projectJobs:c("/projects/:projectId/jobs"),projectApiKeys:c("/projects/:projectId/api-keys"),projectTransfer:c("/projects/:projectId/transfer"),projectTemplates:c("/projects/:projectId/templates"),projectTemplate:c("/projects/:projectId/templates/:templateId"),projectFlowCharts:c("/projects/:projectId/flowcharts"),projectFlowChartsUpload:c("/projects/:projectId/flowcharts/upload"),projectFlowChart:c("/projects/:projectId/flowcharts/:flowchartId"),projectFlowChartData:c("/projects/:projectId/flowcharts/:flowchartId/data"),projectFlowChartChat:c("/projects/:projectId/flowcharts/:flowchartId/chat"),projectFlowChartShare:c("/projects/:projectId/flowcharts/:flowchartId/share"),projectSchedules:c("/projects/:projectId/schedules"),projectSchedule:c("/projects/:projectId/schedules/:scheduleId"),projectScheduleExecutions:c("/projects/:projectId/schedules/:scheduleId/executions"),bulkCreateProjects:c("/projects/bulk"),githubOAuthAuthorize:c("/github/oauth/authorize"),githubCallback:c("/github/oauth/callback"),githubLink:c("/github/link"),githubIssuesByRepo:c("/github/issues/:owner/:repo"),githubIssueByRepo:c("/github/issues/:owner/:repo/:issueNumber"),githubIssues:c("/github/issues"),githubIssue:c("/github/issues/:issueNumber"),githubIssueComments:c("/github/issues/:issueNumber/comments"),githubIssueLabels:c("/github/issues/labels"),githubIssueAssignees:c("/github/issues/assignees"),githubIssueTest:c("/github/issues/test"),githubIssuesBulkTest:c("/github/issues/bulk-test"),githubTestSessions:c("/github/issues/test-sessions"),githubTestSession:c("/github/issues/test-sessions/:sessionId"),githubTestSessionSeen:c("/github/issues/test-sessions/:sessionId/seen"),githubTestSessionsCounts:c("/github/issues/test-sessions/counts"),githubBulkTest:c("/github/bulk-test"),githubWebhooks:c("/github/webhooks"),authSync:c("/auth/sync"),authMe:c("/auth/me"),authStartup:c("/auth/startup"),authDeleteAccount:c("/auth/account"),authDeletionPreview:c("/auth/account/deletion-preview"),billingBalance:c("/billing/balance"),billingHasCredits:c("/billing/has-credits"),billingPortal:c("/billing/portal"),billingCheckout:c("/billing/checkout"),billingSubscription:c("/billing/subscription"),billingChangePlan:c("/billing/change-plan"),health:c("/health"),status:c("/status"),templates:c("/templates"),issueAnalyzer:c("/issue-analyzer"),prAnalyzer:c("/pr-analyzer"),logs:c("/logs"),relevantIssuesDiscover:c("/relevant-issues/discover"),relevantIssuesLatest:c("/relevant-issues/latest"),onboarding:c("/onboarding"),onboardingComplete:c("/onboarding/complete"),onboardingCheck:c("/onboarding/check"),search:c("/search"),testerApply:c("/tester/apply"),testerProfile:c("/tester/profile"),testerPublicProfile:c("/testers/:testerId/profile"),testerDownloadApp:c("/tester/download-app"),testerDownloadExtension:c("/tester/download-extension"),testerDownloadMobileApp:c("/tester/download-mobile-app"),testerAvailableJobs:c("/tester/jobs/available"),testerAvatarUploadUrl:c("/tester/profile/avatar-upload-url"),testerAppVersion:c("/tester/app-version"),testerPushToken:c("/tester/push-token"),testerDemoJob:c("/tester/demo-job"),extensionTokens:c("/tester/extension-tokens"),extensionToken:c("/tester/extension-tokens/:tokenId"),extensionStream:c("/extension/stream"),testerJobCorrelate:c("/tester/jobs/:jobId/correlate"),testerJobDataStatus:c("/tester/jobs/:jobId/data-status"),testerJobExtensionToken:c("/tester/jobs/:jobId/extension-token"),repoTemplates:c("/repos/:owner/:repo/templates"),repoTemplate:c("/repos/:owner/:repo/templates/:templateName"),organizations:c("/organizations"),organization:c("/organizations/:organizationId"),organizationMembers:c("/organizations/:organizationId/members"),organizationInvite:c("/organizations/:organizationId/invite"),organizationMember:c("/organizations/:organizationId/members/:userId"),organizationProjects:c("/organizations/:organizationId/projects"),organizationJobs:c("/organizations/:organizationId/jobs"),organizationTransferOwnership:c("/organizations/:organizationId/transfer-ownership"),organizationApiKeys:c("/organizations/:organizationId/api-keys"),organizationApiKey:c("/organizations/:organizationId/api-keys/:keyId"),organizationGitHubInstallations:c("/organizations/:organizationId/github/installations"),organizationGitHubInstallation:c("/organizations/:organizationId/github/installations/:installationId"),organizationGitHubRepos:c("/organizations/:organizationId/github/repos"),organizationGitHubInstallationRefresh:c("/organizations/:organizationId/github/installations/:installationId/refresh"),organizationGitHubRepoCheckAccess:c("/organizations/:organizationId/github/repos/check-access"),organizationGitHubRepoFindUrl:c("/organizations/:organizationId/github/repos/find-url"),organizationSchedules:c("/organizations/:organizationId/schedules"),organizationBilling:c("/organizations/:organizationId/billing"),transfersPending:c("/transfers/pending"),transfersOutgoing:c("/transfers/outgoing"),transferAccept:c("/transfers/:transferId/accept"),transferReject:c("/transfers/:transferId/reject"),transferCancel:c("/transfers/:transferId/cancel"),invite:c("/invites/:token"),inviteRedeem:c("/invites/:token/redeem"),agreements:c("/agreements"),agreementCheck:c("/agreements/check"),organizationSsoConnections:c("/organizations/:organizationId/sso/connections"),organizationSsoConnection:c("/organizations/:organizationId/sso/connections/:connectionId"),enterpriseInquiry:c("/enterprise/inquiry"),changelog:c("/changelog"),changelogUnreadCount:c("/changelog/unread-count"),changelogMarkRead:c("/changelog/mark-read"),telemetrySessions:c("/telemetry/sessions"),telemetrySessionEnd:c("/telemetry/sessions/:sessionId/end"),telemetryBatch:c("/telemetry/sessions/:sessionId/events"),telemetrySessionStatus:c("/telemetry/jobs/:jobId/status"),telemetryShortCodeResolve:c("/telemetry/short-codes/:code")}});var je=M(()=>{"use strict";pe();ze();Me()});import Lt from"axios";var b,x=M(()=>{"use strict";C();je();b=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.method==="delete"&&!e.data&&delete e.headers["Content-Type"],e)),this.client.interceptors.response.use(e=>e,e=>{throw this.handleError(e)})}handleError(t){if(t.response){let e=t.response.status,r=t.response.data,o=r?.error||r?.message||t.message;switch(e){case 401:case 403:return new ae(o,r);case 402:return new ue(o,r);case 404:return new ce(o,r);case 400:case 422:return new $(o,r);default:return new P(o,1,r)}}return t.code==="ECONNABORTED"?new P("Request timeout",5):t.code==="ENOTFOUND"||t.code==="ECONNREFUSED"?new P("Cannot connect to Runhuman API",1):new P(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.job.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.jobCancel.build({jobId:t}))}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.patch(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.organizationApiKeys.build({organizationId:t}))).data}async createApiKey(t,e){return(await this.client.post(w.organizationApiKeys.build({organizationId:t}),{name:e})).data}async getApiKey(t){return(await this.client.get(w.key.build({keyId:t}))).data}async deleteApiKey(t){await this.client.delete(w.key.build({keyId:t}))}async listOrganizations(t){return(await this.client.get(w.organizations.build(),{params:t})).data}async getOrganization(t){return(await this.client.get(w.organization.build({organizationId:t}))).data}async getOrganizationBilling(t){return(await this.client.get(w.organizationBilling.build({organizationId:t}))).data}async listOrganizationProjects(t,e){return(await this.client.get(w.organizationProjects.build({organizationId:t}),{params:e})).data}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,r){return(await this.client.patch(w.projectTemplate.build({projectId:t,templateId:e}),r)).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 listGithubRepos(t,e){return(await this.client.get(w.organizationGitHubRepos.build({organizationId:t}),{params:e})).data}async listGithubIssues(t,e,r){return(await this.client.get(w.githubIssuesByRepo.build({owner:t,repo:e}),{params:r})).data}async getGithubIssue(t,e,r){return(await this.client.get(w.githubIssueByRepo.build({owner:t,repo:e,issueNumber:String(r)}))).data}async initiateTransfer(t,e){let r=await this.client.post(w.projectTransfer.build({projectId:t}),{toOrganizationId:e});return r.status===200?{type:"immediate",project:r.data}:{type:"pending",transfer:r.data}}async listPendingTransfers(){return(await this.client.get(w.transfersPending.build())).data}async listOutgoingTransfers(){return(await this.client.get(w.transfersOutgoing.build())).data}async acceptTransfer(t){return(await this.client.post(w.transferAccept.build({transferId:t}),{})).data}async rejectTransfer(t){return(await this.client.post(w.transferReject.build({transferId:t}),{})).data}async cancelTransfer(t){return(await this.client.post(w.transferCancel.build({transferId:t}),{})).data}}});import{cosmiconfig as _t}from"cosmiconfig";import{homedir as Ft}from"os";import{join as K}from"path";import{readFileSync as Q,writeFileSync as J,existsSync as D,mkdirSync as we,chmodSync as Jt}from"fs";function Ht(i){return qt.includes(i)}var Kt,L,V,X,qt,h,R=M(()=>{"use strict";Kt="runhuman",L=K(Ft(),".config","runhuman"),V=K(L,"config.json"),X=K(L,"credentials.json"),qt=["pretty","json","compact"];h=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(),r=e?.accessToken?{apiKey:e.accessToken}:{},o=Object.fromEntries(Object.entries(t).filter(([,a])=>a!==void 0));return{...this.getDefaults(),...this.globalConfig,...this.projectConfig,...r,...this.envConfig,...o}}getDefaults(){return{apiUrl:"https://runhuman.com",outputFormat:"pretty",color:!0,autoOpenBrowser:!0,defaultDuration:5,defaultDeviceClass:"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_ORGANIZATION&&(t.organization=process.env.RUNHUMAN_ORGANIZATION),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_DEVICE_CLASS&&(t.defaultDeviceClass=process.env.RUNHUMAN_DEFAULT_DEVICE_CLASS),process.env.RUNHUMAN_OUTPUT_FORMAT&&Ht(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 _t(Kt).search(this.cwd))?.config||null}catch{return null}}loadGlobalConfig(){try{if(!D(V))return null;let t=Q(V,"utf-8");return JSON.parse(t)}catch{return null}}async get(t){return(await this.loadConfig())[t]}async set(t,e,r=!1){r?await this.setGlobalConfig(t,e):await this.setProjectConfig(t,e)}async setGlobalConfig(t,e){D(L)||we(L,{recursive:!0});let r={};if(D(V)){let o=Q(V,"utf-8");r=JSON.parse(o)}r[t]=e,J(V,JSON.stringify(r,null,2))}async setProjectConfig(t,e){let r=K(this.cwd,".runhumanrc"),o={};if(D(r)){let n=Q(r,"utf-8");o=JSON.parse(n)}o[t]=e,J(r,JSON.stringify(o,null,2))}async saveProjectConfig(t){let e=K(this.cwd,".runhumanrc"),r={};if(D(e)){let n=Q(e,"utf-8");r=JSON.parse(n)}let o={...r,...t};J(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")&&D(V)&&J(V,"{}"),t==="project"||t==="all"){let e=K(this.cwd,".runhumanrc");D(e)&&J(e,"{}")}}saveCredentials(t){D(L)||we(L,{recursive:!0}),J(X,JSON.stringify(t,null,2));try{process.platform!=="win32"&&Jt(X,384)}catch{}}loadCredentials(){try{if(!D(X))return null;let t=Q(X,"utf-8");return JSON.parse(t)}catch{return null}}clearCredentials(){D(X)&&J(X,"{}")}saveUserInfo(t){let e=K(L,"user.json");D(L)||we(L,{recursive:!0}),J(e,JSON.stringify(t,null,2))}loadUserInfo(){try{let t=K(L,"user.json");if(!D(t))return null;let e=Q(t,"utf-8");return JSON.parse(e)}catch{return null}}clearUserInfo(){let t=K(L,"user.json");D(t)&&J(t,"{}")}}});import G from"chalk";import Vt from"cli-table3";var Gt,Ie,p,E=M(()=>{"use strict";Gt=24,Ie=60,p=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 r={success:!1,error:{message:t,details:e},timestamp:new Date().toISOString()};console.error(JSON.stringify(r,null,2))}else console.error(this.color("red",`
3
3
  Error: ${t}
4
4
  `)),e?.code==="INSUFFICIENT_TOKENS"&&e?.link?(console.error(this.color("yellow","Add funds to continue:")),console.error(this.color("cyan",e.link)),console.error("")):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
- `))}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}`))}separator(){this.options.json||this.options.quiet||console.log("=".repeat(he))}sectionHeader(t){this.options.json||this.options.quiet||(console.log(`
7
- `+"=".repeat(he)),console.log(t),console.log("=".repeat(he)+`
8
- `))}detail(t,e){this.options.json||this.options.quiet||console.log(` ${t}:`.padEnd(Dt)+String(e))}heading(t){this.options.json||this.options.quiet||console.log(`
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}`))}separator(){this.options.json||this.options.quiet||console.log("=".repeat(Ie))}sectionHeader(t){this.options.json||this.options.quiet||(console.log(`
7
+ `+"=".repeat(Ie)),console.log(t),console.log("=".repeat(Ie)+`
8
+ `))}detail(t,e){this.options.json||this.options.quiet||console.log(` ${t}:`.padEnd(Gt)+String(e))}heading(t){this.options.json||this.options.quiet||console.log(`
9
9
  ${t}
10
10
  `)}hints(t){if(this.options.json||this.options.quiet)return;let r=Math.max(...t.map(([o])=>o.length))+4;for(let[o,n]of t)console.log(` ${o}:`.padEnd(r)+` ${n}`);console.log()}tableHead(t){return t.map(e=>this.color("cyan",e))}formatTimestamp(t){return new Date(t).toLocaleString()}formatShortDate(t){return new Date(t).toLocaleDateString()}formatJobList(t){if(this.options.format==="compact")return t.map(r=>`${r.id} ${r.status} ${r.url}`).join(`
11
- `);let e=new Ot({head:this.tableHead(["Job ID","Status","URL","Created","Duration","Cost"]),colWidths:[15,12,30,14,10,10]});return t.forEach(r=>{e.push([r.id,this.formatStatus(r.status),r.url?this.truncate(r.url,28):"-",r.createdAt?this.formatDate(r.createdAt):"-",r.testDurationSeconds?this.formatDuration(r.testDurationSeconds):"-",r.costUsd?`$${r.costUsd.toFixed(3)}`:"-"])}),e.toString()}formatStatus(t){let e={pending:"yellow",waiting:"blue",working:"blue",completed:"green",incomplete:"yellow",abandoned:"red",rejected:"gray",error:"red",failed:"red"},r=t.replaceAll("_"," ");return this.color(e[t]||"white",r)}formatDuration(t){if(t<60)return`${t}s`;let e=Math.floor(t/60),r=t%60;if(e<60)return`${e}m ${r}s`;let o=Math.floor(e/60),n=e%60;return`${o}h ${n}m ${r}s`}formatDate(t){let e=new Date(t),o=new Date().getTime()-e.getTime(),n=Math.floor(o/1e3),c=Math.floor(n/60),l=Math.floor(c/60),i=Math.floor(l/24);return n<60?`${n}s ago`:c<60?`${c}m ago`:l<24?`${l}h ago`:i<30?`${i}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:V.red,green:V.green,blue:V.blue,yellow:V.yellow,cyan:V.cyan,gray:V.gray,white:V.white}[t];return o?o(e):e}static result(t){return{success:!0,data:t,timestamp:new Date().toISOString()}}static error(t,e,r){return{success:!1,error:{message:t,code:e,details:r},timestamp:new Date().toISOString()}}}});function ie(s){return s.replace(/-/g,"").toLowerCase()}function Lt(s){let t=s.replace(/-/g,"");return zt.test(t)}async function y(s,t,e,r){if(r?.requireFullId&&!Lt(e)){let i=xe[t],u=await i(s),d=ie(e),m=u.filter(g=>ie(g.id).startsWith(d));throw new je(e,t,r.requireFullId.reason,m)}if(Mt.test(e))return e;let o=xe[t],n=await o(s),c=ie(e),l=n.filter(i=>ie(i.id).startsWith(c));if(l.length===0)throw new ye(e,t);if(l.length===1)return l[0].id;throw new be(e,t,l)}var Mt,zt,be,ye,je,xe,x=L(()=>{"use strict";I();Mt=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,zt=/^[0-9a-f]{32}$/i,be=class extends k{constructor(e,r,o){let n=o.map(c=>` ${c.id} ${c.label}`).join(`
11
+ `);let e=new Vt({head:this.tableHead(["Job ID","Status","URL","Created","Duration","Cost"]),colWidths:[15,12,30,14,10,10]});return t.forEach(r=>{e.push([r.id,this.formatStatus(r.status),r.url?this.truncate(r.url,28):"-",r.createdAt?this.formatDate(r.createdAt):"-",r.testDurationSeconds?this.formatDuration(r.testDurationSeconds):"-",r.costUsd?`$${r.costUsd.toFixed(3)}`:"-"])}),e.toString()}formatStatus(t){let e={pending:"yellow",waiting:"blue",working:"blue",completed:"green",incomplete:"yellow",abandoned:"red",rejected:"gray",error:"red",failed:"red"},r=t.replaceAll("_"," ");return this.color(e[t]||"white",r)}formatDuration(t){if(t<60)return`${t}s`;let e=Math.floor(t/60),r=t%60;if(e<60)return`${e}m ${r}s`;let o=Math.floor(e/60),n=e%60;return`${o}h ${n}m ${r}s`}formatDate(t){let e=new Date(t),o=new Date().getTime()-e.getTime(),n=Math.floor(o/1e3),a=Math.floor(n/60),u=Math.floor(a/60),l=Math.floor(u/24);return n<60?`${n}s ago`:a<60?`${a}m ago`:u<24?`${u}h ago`:l<30?`${l}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:G.red,green:G.green,blue:G.blue,yellow:G.yellow,cyan:G.cyan,gray:G.gray,white:G.white}[t];return o?o(e):e}static result(t){return{success:!0,data:t,timestamp:new Date().toISOString()}}static error(t,e,r){return{success:!1,error:{message:t,code:e,details:r},timestamp:new Date().toISOString()}}}});function W(i){return i.replace(/-/g,"").toLowerCase()}function Se(i){let t=i.replace(/-/g,"");return Yt.test(t)}async function y(i,t,e,r){if(r?.requireFullId&&!Se(e)){let l=$e[t],s=await l(i),d=W(e),m=s.filter(g=>W(g.id).startsWith(d));throw new Ce(e,t,r.requireFullId.reason,m)}if(Ae.test(e))return e;let o=$e[t],n=await o(i),a=W(e),u=n.filter(l=>W(l.id).startsWith(a));if(u.length===0)throw new ne(e,t);if(u.length===1)return u[0].id;throw new oe(e,t,u)}var Ae,Yt,oe,ne,Ce,$e,U=M(()=>{"use strict";C();Ae=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,Yt=/^[0-9a-f]{32}$/i,oe=class extends P{constructor(e,r,o){let n=o.map(a=>` ${a.id} ${a.label}`).join(`
12
12
  `);super(`"${e}" matches multiple ${r}s:
13
13
  ${n}
14
- Please provide more characters.`,1);this.prefix=e;this.resourceType=r;this.matches=o;this.name="AmbiguousIdError"}},ye=class extends k{constructor(e,r){super(`No ${r} found matching "${e}".`,3);this.prefix=e;this.resourceType=r;this.name="NoMatchError"}},je=class extends k{constructor(e,r,o,n){let c=`Full ${r} ID required for ${o} \u2014 short prefixes are not allowed for this operation.`;if(c+=`
14
+ Please provide more characters.`,1);this.prefix=e;this.resourceType=r;this.matches=o;this.name="AmbiguousIdError"}},ne=class extends P{constructor(e,r){super(`No ${r} found matching "${e}".`,3);this.prefix=e;this.resourceType=r;this.name="NoMatchError"}},Ce=class extends P{constructor(e,r,o,n){let a=`Full ${r} ID required for ${o} \u2014 short prefixes are not allowed for this operation.`;if(a+=`
15
15
 
16
- Provided: "${e}"`,n.length===1)c+=`
16
+ Provided: "${e}"`,n.length===1)a+=`
17
17
 
18
- This prefix matches: ${n[0].id} ${n[0].label}`,c+=`
19
- If this is correct, re-run the command with the full ID.`;else if(n.length>1){let l=n.map(i=>` ${i.id} ${i.label}`).join(`
20
- `);c+=`
18
+ This prefix matches: ${n[0].id} ${n[0].label}`,a+=`
19
+ If this is correct, re-run the command with the full ID.`;else if(n.length>1){let u=n.map(l=>` ${l.id} ${l.label}`).join(`
20
+ `);a+=`
21
21
 
22
22
  This prefix matches multiple ${r}s:
23
- ${l}`,c+=`
24
- Use the full ID of the intended ${r}.`}else c+=`
23
+ ${u}`,a+=`
24
+ Use the full ID of the intended ${r}.`}else a+=`
25
25
 
26
- No ${r} found matching this prefix.`;super(c,1);this.prefix=e;this.resourceType=r;this.reason=o;this.matches=n;this.name="FullIdRequiredError"}};xe={async job(s){let{items:t}=await s.listJobs({limit:200});return t.map(e=>({id:e.id,label:`${e.status.padEnd(10)} ${e.description??""}`.trim()}))},async project(s){let{items:t}=await s.listProjects({limit:200});return t.map(e=>({id:e.id,label:e.name}))},async organization(s){let{items:t}=await s.listOrganizations({limit:200});return t.map(e=>({id:e.id,label:e.name}))},async key(s){let{items:t}=await s.listOrganizations({limit:200}),e=[];for(let r of t){let{items:o}=await s.listApiKeys(r.id);for(let n of o)e.push({id:n.id,label:n.name})}return e},async template(s){let{items:t}=await s.listProjects({limit:200}),e=[];for(let r of t){let{items:o}=await s.listTemplates(r.id);for(let n of o)e.push({id:n.id,label:n.name})}return e},async transfer(s){let[t,e]=await Promise.all([s.listPendingTransfers(),s.listOutgoingTransfers()]),r=[...t.items,...e.items],o=new Set,n=[];for(let c of r)o.has(c.id)||(o.add(c.id),n.push({id:c.id,label:c.projectName}));return n}}});var we={};It(we,{waitCommand:()=>le,waitForJob:()=>Me});import{Command as Fo}from"commander";import Jo from"ora";async function Me(s,t,e,r=600){let o=Date.now(),n=r*1e3,c=1e4,l=null;for(e.options.json||(l=Jo("Waiting for job completion...").start());;){let i=Date.now()-o;if(i>=n)throw l&&l.fail("Timeout waiting for job completion"),new ne(`Job did not complete within ${r} seconds`);let u=await t.getJob(s);if(l){let d=e.formatDuration(Math.floor(i/1e3));l.text=`Waiting for job completion... (${d} elapsed, status: ${u.status})`}if(u.status==="completed"){if(l&&l.succeed("Test Completed!"),!e.options.json){if(console.log(),e.detail("Duration",u.testDurationSeconds?e.formatDuration(u.testDurationSeconds):"N/A"),e.detail("Cost","$"+(u.costUsd||0).toFixed(3)),u.testerAlias&&e.detail("Tester",u.testerAlias),u.result){console.log(`
27
- Results Summary:`);for(let[d,m]of Object.entries(u.result))console.log(` ${d}: ${m}`)}console.log(),e.hints([["Full results",`runhuman results ${s}`]])}return}if(u.status==="error"||u.status==="incomplete"||u.status==="abandoned")throw l&&l.fail(`Test ${u.status}`),new Error(`Job ${u.status}: ${u.testerResponse||"No details available"}`);if(u.status==="rejected")throw l&&l.fail("Test was rejected"),new Error("Job was rejected");await new Promise(d=>setTimeout(d,c))}}function le(){let s=new Fo("wait");return s.description("Wait for a job to complete and display results").argument("<jobId>","Job ID to wait for").option("-T, --timeout <seconds>","Max wait time (default: 600)",parseInt).option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"job",t);if(await Me(l,c,n,e.timeout||600),e.json){let i=await c.getJob(l),u=p.result(i);n.output(u)}}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}var ue=L(()=>{"use strict";P();R();v();I();x()});import{Command as ft}from"commander";P();R();v();I();import{Command as Ko}from"commander";import qo from"ora";I();var Nt={project:{name:"Project ID",flag:"--project",configKey:"project",envVar:"RUNHUMAN_PROJECT",switchCommand:"runhuman projects switch <project-id>"},organization:{name:"Organization ID",flag:"--organization",configKey:"organization",envVar:"RUNHUMAN_ORGANIZATION",switchCommand:"runhuman orgs switch <organization-id>"}};function $t(s,t,e){return s?"flag":process.env[e.envVar]?"env":"global config"}function A(s,t,e,r){let o=Nt[s],n=t||e[o.configKey];if(!n)throw new M(`${o.name} is required. You can:
26
+ No ${r} found matching this prefix.`;super(a,1);this.prefix=e;this.resourceType=r;this.reason=o;this.matches=n;this.name="FullIdRequiredError"}};$e={async project(i){let{items:t}=await i.listProjects({limit:200});return t.map(e=>({id:e.id,label:e.name}))},async organization(i){let{items:t}=await i.listOrganizations({limit:200});return t.map(e=>({id:e.id,label:e.name}))},async key(i){let{items:t}=await i.listOrganizations({limit:200}),e=[];for(let r of t){let{items:o}=await i.listApiKeys(r.id);for(let n of o)e.push({id:n.id,label:n.name})}return e},async template(i){let{items:t}=await i.listProjects({limit:200}),e=[];for(let r of t){let{items:o}=await i.listTemplates(r.id);for(let n of o)e.push({id:n.id,label:n.name})}return e},async transfer(i){let[t,e]=await Promise.all([i.listPendingTransfers(),i.listOutgoingTransfers()]),r=[...t.items,...e.items],o=new Set,n=[];for(let a of r)o.has(a.id)||(o.add(a.id),n.push({id:a.id,label:a.projectName}));return n}}});function Te(i){return{id:i.id,label:`${i.status.padEnd(10)} ${i.description??""}`.trim()}}function Ve(i){return{id:i.id,label:i.name}}async function Ge(i,t,e,r){let o=W(i);for(let n of e){let u=(await n.fetch()).filter(l=>W(l.id).startsWith(o));if(u.length===1)return r.info(`Resolved ${t} in ${n.label}`),u[0].id;if(u.length>1)throw new oe(i,t,u)}throw new ne(i,t)}function We(i){return Ae.test(i)||Se(i)}async function q(i,t,e,r,o){if(We(t))return t;let n=Qo(i,e,r);return Ge(t,"job",n,o)}function Qo(i,t,e){if(t.projectId)return[{label:`project ${t.projectId}`,fetch:()=>Ke(i,t.projectId)}];if(t.organizationId)return[{label:`organization ${t.organizationId}`,fetch:()=>qe(i,t.organizationId)}];let r=[];return e.project&&r.push({label:"default project",fetch:()=>Ke(i,e.project)}),e.organization&&r.push({label:"default organization",fetch:()=>qe(i,e.organization)}),r.push({label:"all organizations",fetch:()=>Xo(i)}),r}async function Ke(i,t){let{items:e}=await i.listJobs({projectId:t,limit:200});return e.map(Te)}async function qe(i,t){let{items:e}=await i.listJobs({organizationId:t,limit:200});return e.map(Te)}async function Xo(i){let{items:t}=await i.listOrganizations({limit:200}),e=[];for(let r of t){let{items:o}=await i.listJobs({organizationId:r.id,limit:200});e.push(...o.map(Te))}return e}async function re(i,t,e,r,o){if(We(t))return t;let n=en(i,e,r);return Ge(t,"project",n,o)}function en(i,t,e){if(t.organizationId)return[{label:`organization ${t.organizationId}`,fetch:()=>He(i,t.organizationId)}];let r=[];return e.organization&&r.push({label:"default organization",fetch:()=>He(i,e.organization)}),r.push({label:"all organizations",fetch:()=>tn(i)}),r}async function He(i,t){let{items:e}=await i.listOrganizationProjects(t,{limit:200});return e.map(Ve)}async function tn(i){let{items:t}=await i.listProjects({limit:200});return t.map(Ve)}var ee=M(()=>{"use strict";U()});var ve={};Nt(ve,{waitCommand:()=>ge,waitForJob:()=>Be});import{Command as on}from"commander";import nn from"ora";async function Be(i,t,e,r=600){let o=Date.now(),n=r*1e3,a=1e4,u=null;for(e.options.json||(u=nn("Waiting for job completion...").start());;){let l=Date.now()-o;if(l>=n)throw u&&u.fail("Timeout waiting for job completion"),new le(`Job did not complete within ${r} seconds`);let s=await t.getJob(i);if(u){let d=e.formatDuration(Math.floor(l/1e3));u.text=`Waiting for job completion... (${d} elapsed, status: ${s.status})`}if(s.status==="completed"){if(u&&u.succeed("Test Completed!"),!e.options.json){if(console.log(),e.detail("Duration",s.testDurationSeconds?e.formatDuration(s.testDurationSeconds):"N/A"),e.detail("Cost","$"+(s.costUsd||0).toFixed(3)),s.testerAlias&&e.detail("Tester",s.testerAlias),s.result){console.log(`
27
+ Results Summary:`);for(let[d,m]of Object.entries(s.result))console.log(` ${d}: ${m}`)}console.log(),e.hints([["Full results",`runhuman results ${i}`]])}return}if(s.status==="error"||s.status==="incomplete"||s.status==="abandoned")throw u&&u.fail(`Test ${s.status}`),new Error(`Job ${s.status}: ${s.testerResponse||"No details available"}`);if(s.status==="rejected")throw u&&u.fail("Test was rejected"),new Error("Job was rejected");await new Promise(d=>setTimeout(d,a))}}function ge(){let i=new on("wait");return i.description("Wait for a job to complete and display results").argument("<jobId>","Job ID to wait for").option("-p, --project <id>","Scope search to a specific project").option("-o, --organization <id>","Scope search to a specific organization").option("-T, --timeout <seconds>","Max wait time (default: 600)",parseInt).option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u={projectId:e.project?await y(a,"project",e.project):void 0,organizationId:e.organization?await y(a,"organization",e.organization):void 0},l=await q(a,t,u,o,n);if(await Be(l,a,n,e.timeout||600),e.json){let s=await a.getJob(l),d=p.result(s);n.output(d)}}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}var fe=M(()=>{"use strict";x();R();E();C();ee();U()});import{Command as Et}from"commander";x();R();E();C();import{Command as rn}from"commander";import sn from"ora";C();var Wt={project:{name:"Project ID",flag:"--project",configKey:"project",envVar:"RUNHUMAN_PROJECT",switchCommand:"runhuman projects switch <project-id>"},organization:{name:"Organization ID",flag:"--organization",configKey:"organization",envVar:"RUNHUMAN_ORGANIZATION",switchCommand:"runhuman orgs switch <organization-id>"}};function Bt(i,t,e){return i?"flag":process.env[e.envVar]?"env":"global config"}function A(i,t,e,r){let o=Wt[i],n=t||e[o.configKey];if(!n)throw new $(`${o.name} is required. You can:
28
28
  \u2022 Pass ${o.flag} <id> for this command
29
29
  \u2022 Set a default: ${o.switchCommand}
30
- \u2022 Set via env: export ${o.envVar}=<id>`);let c=$t(t,e,o);return c!=="flag"&&r.info(`Using default ${s}: ${n} [from ${c}]`),n}x();ge();var ae=["ios","android","pc","mac"],ce=["english","spanish"];function Ce(){let s=new Ko("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("--device-class <class>","Device class: desktop|mobile").option("-S, --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("--required-devices <devices>","Required tester devices (comma-separated: ios,android,pc,mac)").option("--required-languages <languages>","Required tester languages (comma-separated: english,spanish)").option("--require-social-videos","Require tester to create social videos").option("-s, --sync","Wait for result before exiting (synchronous mode)").option("--wait <seconds>","Max wait time in sync mode (default: 300)",parseInt).option("-p, --project <id>","Project ID (or use default from config)").option("--api-key <key>","API key (or use from config/env)").option("-j, --json","Output as JSON (for scripting)").option("-q, --quiet","Minimal output (only job ID)").action(async(t,e)=>{try{let o=await new h().loadConfig({apiKey:e.apiKey}),n=new p({json:e.json,quiet:e.quiet,color:o.color}),c=new b(o);if(!t&&!e.template&&!o.defaultUrl)throw new M("URL is required (provide as argument, via --template, or set defaultUrl in config)");if(!e.description&&!e.template)throw new M("Description is required (use -d flag or --template)");let l=A("project",e.project,o,n),u={projectId:await y(c,"project",l),url:t||o.defaultUrl||"",description:e.description||"",duration:e.duration||o.defaultDuration,deviceClass:e.deviceClass||o.defaultDeviceClass};if(e.schema){let j=await(await import("fs/promises")).readFile(e.schema,"utf-8");u.schema=JSON.parse(j)}else e.schemaInline&&(u.schema=JSON.parse(e.schemaInline));if(e.metadata&&(u.metadata=JSON.parse(e.metadata)),e.githubRepo&&(u.githubRepo=e.githubRepo),e.template&&(u.templateId=await y(c,"template",e.template)),e.requiredDevices){let g=e.requiredDevices.split(",");for(let j of g)if(!ae.includes(j))throw new M(`Invalid device "${j}". Valid values: ${ae.join(", ")}`);u.requiredDevices=g}if(e.requiredLanguages){let g=e.requiredLanguages.split(",");for(let j of g)if(!ce.includes(j))throw new M(`Invalid language "${j}". Valid values: ${ce.join(", ")}`);u.requiredLanguages=g}e.requireSocialVideos&&(u.requireSocialVideos=!0);let d=e.json?null:qo("Creating QA test job...").start(),m=await c.createJob(u);if(d&&d.succeed("Job created successfully!"),e.json){let g=p.result({jobId:m.jobId,status:m.status,message:m.message,dashboardUrl:`${o.apiUrl}${_.jobSimple.build({jobId:m.jobId})}`,estimatedCompletionTime:m.estimatedCompletionTime});n.output(g)}else e.quiet?console.log(m.jobId):(n.detail("Job ID",m.jobId),n.detail("Status",m.status),n.detail("URL",u.url),n.detail("Dashboard",`${o.apiUrl}${_.jobSimple.build({jobId:m.jobId})}`),console.log(),n.hints([["Track progress",`runhuman status ${m.jobId}`],["Wait for result",`runhuman wait ${m.jobId}`]]));if(e.sync){let{waitForJob:g}=await Promise.resolve().then(()=>(ue(),we));await g(m.jobId,c,n,e.wait||300)}}catch(r){let o=f(r);new p({json:e.json,quiet:!1}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}P();R();v();import{Command as Ho}from"commander";I();x();function Ie(){let s=new Ho("status");return s.description("Check the current status of a job").argument("<jobId>","Job ID to check").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"job",t),i=await c.getJob(l);if(e.json){let u=p.result(i);n.output(u)}else n.heading(`Job Status: ${l}`),n.detail("Status",n.formatStatus(i.status)),i.testerAlias&&n.detail("Tester",i.testerAlias),i.url&&n.detail("URL",i.url),i.description&&n.detail("Description",i.description),i.createdAt&&n.detail("Created",n.formatTimestamp(i.createdAt)),i.completedAt&&n.detail("Completed",n.formatTimestamp(i.completedAt)),i.testDurationSeconds&&n.detail("Duration",n.formatDuration(i.testDurationSeconds)),i.costUsd&&n.detail("Cost",`$${i.costUsd.toFixed(3)}`),i.deviceClass&&n.detail("Device Class",i.deviceClass),i.requiredDevices?.length&&n.detail("Required Devices",i.requiredDevices.join(", ")),i.requiredLanguages?.length&&n.detail("Required Langs",i.requiredLanguages.join(", ")),i.requireSocialVideos&&n.detail("Social Videos","required"),n.detail("Dashboard",`${o.apiUrl}${_.jobSimple.build({jobId:l})}`),console.log(),i.status==="pending"||i.status==="waiting"||i.status==="working"?n.hints([["Wait for completion",`runhuman wait ${l}`]]):i.status==="completed"&&n.hints([["View results",`runhuman results ${l}`]])}catch(r){let o=new p({json:e.json}),n=f(r);o.outputError(n.message,n.details),process.exit(n.exitCode)}}),s}ue();P();R();v();I();x();import{Command as Go}from"commander";var ze="=".repeat(60);function $(s){return`
31
- ${ze}
32
- ${s}
33
- ${ze}
34
- `}function X(s){return new Date(s).toLocaleTimeString()}function Vo(s){let t=Math.floor(s/60),e=Math.floor(s%60);return`${String(t).padStart(2,"0")}:${String(e).padStart(2,"0")}`}function Le(s){if(!s.transcription?.segments?.length)return $("Transcript")+` No transcript available
35
- `;let t=s.transcription.segments.map(e=>{let r=e.words[0];return` [${r?Vo(r.start):"00:00"}] ${e.text}`});return $("Transcript")+t.join(`
30
+ \u2022 Set via env: export ${o.envVar}=<id>`);let a=Bt(t,e,o);return a!=="flag"&&r.info(`Using default ${i}: ${n} [from ${a}]`),n}U();je();var de=["ios","android","pc","mac"],me=["english","spanish"];function Ee(){let i=new rn("create");return i.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("--device-class <class>","Device class: desktop|mobile").option("-S, --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("--required-devices <devices>","Required tester devices (comma-separated: ios,android,pc,mac)").option("--required-languages <languages>","Required tester languages (comma-separated: english,spanish)").option("--require-social-videos","Require tester to create social videos").option("-s, --sync","Wait for result before exiting (synchronous mode)").option("--wait <seconds>","Max wait time in sync mode (default: 300)",parseInt).option("-p, --project <id>","Project ID (or use default from config)").option("--api-key <key>","API key (or use from config/env)").option("-j, --json","Output as JSON (for scripting)").option("-q, --quiet","Minimal output (only job ID)").action(async(t,e)=>{try{let o=await new h().loadConfig({apiKey:e.apiKey}),n=new p({json:e.json,quiet:e.quiet,color:o.color}),a=new b(o);if(!t&&!e.template&&!o.defaultUrl)throw new $("URL is required (provide as argument, via --template, or set defaultUrl in config)");if(!e.description&&!e.template)throw new $("Description is required (use -d flag or --template)");let u=A("project",e.project,o,n),s={projectId:await y(a,"project",u),url:t||o.defaultUrl||"",description:e.description||"",duration:e.duration||o.defaultDuration,deviceClass:e.deviceClass||o.defaultDeviceClass};if(e.schema){let j=await(await import("fs/promises")).readFile(e.schema,"utf-8");s.schema=JSON.parse(j)}else e.schemaInline&&(s.schema=JSON.parse(e.schemaInline));if(e.metadata&&(s.metadata=JSON.parse(e.metadata)),e.githubRepo&&(s.githubRepo=e.githubRepo),e.template&&(s.templateId=await y(a,"template",e.template)),e.requiredDevices){let g=e.requiredDevices.split(",");for(let j of g)if(!de.includes(j))throw new $(`Invalid device "${j}". Valid values: ${de.join(", ")}`);s.requiredDevices=g}if(e.requiredLanguages){let g=e.requiredLanguages.split(",");for(let j of g)if(!me.includes(j))throw new $(`Invalid language "${j}". Valid values: ${me.join(", ")}`);s.requiredLanguages=g}e.requireSocialVideos&&(s.requireSocialVideos=!0);let d=e.json?null:sn("Creating QA test job...").start(),m=await a.createJob(s);if(d&&d.succeed("Job created successfully!"),e.json){let g=p.result({jobId:m.jobId,status:m.status,message:m.message,dashboardUrl:`${o.apiUrl}${F.jobSimple.build({jobId:m.jobId})}`,estimatedCompletionTime:m.estimatedCompletionTime});n.output(g)}else e.quiet?console.log(m.jobId):(n.detail("Job ID",m.jobId),n.detail("Status",m.status),n.detail("URL",s.url),n.detail("Dashboard",`${o.apiUrl}${F.jobSimple.build({jobId:m.jobId})}`),console.log(),n.hints([["Track progress",`runhuman status ${m.jobId}`],["Wait for result",`runhuman wait ${m.jobId}`]]));if(e.sync){let{waitForJob:g}=await Promise.resolve().then(()=>(fe(),ve));await g(m.jobId,a,n,e.wait||300)}}catch(r){let o=f(r);new p({json:e.json,quiet:!1}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}x();R();E();import{Command as an}from"commander";C();ee();U();function Re(){let i=new an("status");return i.description("Check the current status of a job").argument("<jobId>","Job ID to check").option("-p, --project <id>","Scope search to a specific project").option("-o, --organization <id>","Scope search to a specific organization").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u={projectId:e.project?await y(a,"project",e.project):void 0,organizationId:e.organization?await y(a,"organization",e.organization):void 0},l=await q(a,t,u,o,n),s=await a.getJob(l);if(e.json){let d=p.result(s);n.output(d)}else n.heading(`Job Status: ${l}`),n.detail("Status",n.formatStatus(s.status)),s.testerAlias&&n.detail("Tester",s.testerAlias),s.url&&n.detail("URL",s.url),s.description&&n.detail("Description",s.description),s.createdAt&&n.detail("Created",n.formatTimestamp(s.createdAt)),s.completedAt&&n.detail("Completed",n.formatTimestamp(s.completedAt)),s.testDurationSeconds&&n.detail("Duration",n.formatDuration(s.testDurationSeconds)),s.costUsd&&n.detail("Cost",`$${s.costUsd.toFixed(3)}`),s.deviceClass&&n.detail("Device Class",s.deviceClass),s.requiredDevices?.length&&n.detail("Required Devices",s.requiredDevices.join(", ")),s.requiredLanguages?.length&&n.detail("Required Langs",s.requiredLanguages.join(", ")),s.requireSocialVideos&&n.detail("Social Videos","required"),n.detail("Dashboard",`${o.apiUrl}${F.jobSimple.build({jobId:l})}`),console.log(),s.status==="pending"||s.status==="waiting"||s.status==="working"?n.hints([["Wait for completion",`runhuman wait ${l}`]]):s.status==="completed"&&n.hints([["View results",`runhuman results ${l}`]])}catch(r){let o=new p({json:e.json}),n=f(r);o.outputError(n.message,n.details),process.exit(n.exitCode)}}),i}fe();x();R();E();C();ee();U();import{Command as ln}from"commander";var Ye="=".repeat(60);function z(i){return`
31
+ ${Ye}
32
+ ${i}
33
+ ${Ye}
34
+ `}function ie(i){return new Date(i).toLocaleTimeString()}function cn(i){let t=Math.floor(i/60),e=Math.floor(i%60);return`${String(t).padStart(2,"0")}:${String(e).padStart(2,"0")}`}function Ze(i){if(!i.transcription?.segments?.length)return z("Transcript")+` No transcript available
35
+ `;let t=i.transcription.segments.map(e=>{let r=e.words[0];return` [${r?cn(r.start):"00:00"}] ${e.text}`});return z("Transcript")+t.join(`
36
36
  `)+`
37
- `}function _e(s){let t=s.testerData?.consoleMessages;if(!t?.length)return $("Console Logs")+` No console logs recorded
38
- `;let e=t.map(o=>{let n=X(o.timestamp);return` [${o.type}] ${n} ${o.message}`}),r=$("Console Logs")+e.join(`
37
+ `}function Qe(i){let t=i.testerData?.consoleMessages;if(!t?.length)return z("Console Logs")+` No console logs recorded
38
+ `;let e=t.map(o=>{let n=ie(o.timestamp);return` [${o.type}] ${n} ${o.message}`}),r=z("Console Logs")+e.join(`
39
39
  `)+`
40
- `;if(s.testerData?.truncationInfo){let o=s.testerData.truncationInfo;r+=`
40
+ `;if(i.testerData?.truncationInfo){let o=i.testerData.truncationInfo;r+=`
41
41
  (Truncated: ${JSON.stringify(o)})
42
- `}return r}function Fe(s){let t=s.testerData?.networkRequests;if(!t?.length)return $("Network Requests")+` No network requests recorded
43
- `;let e=t.map(r=>{let o=X(r.timestamp),n=r.status??"---";return` ${r.method} ${n} ${r.url} (${o})`});return $("Network Requests")+e.join(`
42
+ `}return r}function Xe(i){let t=i.testerData?.networkRequests;if(!t?.length)return z("Network Requests")+` No network requests recorded
43
+ `;let e=t.map(r=>{let o=ie(r.timestamp),n=r.status??"---";return` ${r.method} ${n} ${r.url} (${o})`});return z("Network Requests")+e.join(`
44
44
  `)+`
45
- `}function Je(s){let t=s.testerData?.events;if(!t?.length)return $("User Events")+` No events recorded
46
- `;let e=t.map(r=>{let o=X(r.timestamp),n;switch(r.type){case"click":n=`Click at (${r.x}, ${r.y})${r.element?` on ${r.element}`:""}`;break;case"page_load":n=`${r.event}: ${r.url}`;break;case"form_submit":n=`Form submit (${r.formInfo.fieldCount} fields)`;break;case"spa_route_change":n=`Route: ${r.route}`;break;case"keyboard_input":n=`Typed in ${r.elementInfo.tagName}`;break}return` [${r.type}] ${o} ${n}`});return $("User Events")+e.join(`
45
+ `}function et(i){let t=i.testerData?.events;if(!t?.length)return z("User Events")+` No events recorded
46
+ `;let e=t.map(r=>{let o=ie(r.timestamp),n;switch(r.type){case"click":n=`Click at (${r.x}, ${r.y})${r.element?` on ${r.element}`:""}`;break;case"page_load":n=`${r.event}: ${r.url}`;break;case"form_submit":n=`Form submit (${r.formInfo.fieldCount} fields)`;break;case"spa_route_change":n=`Route: ${r.route}`;break;case"keyboard_input":n=`Typed in ${r.elementInfo.tagName}`;break}return` [${r.type}] ${o} ${n}`});return z("User Events")+e.join(`
47
47
  `)+`
48
- `}function Ke(s){if(!s.keyMoments?.length)return $("Key Moments")+` No key moments available
49
- `;let t=s.keyMoments.map(e=>{let r=X(e.timestamp);return` [${e.type}] ${r} ${e.description} (significance: ${e.significance})`});return $("Key Moments")+t.join(`
48
+ `}function tt(i){if(!i.keyMoments?.length)return z("Key Moments")+` No key moments available
49
+ `;let t=i.keyMoments.map(e=>{let r=ie(e.timestamp);return` [${e.type}] ${r} ${e.description} (significance: ${e.significance})`});return z("Key Moments")+t.join(`
50
50
  `)+`
51
- `}function qe(s){if(!s.conversationHistory?.length)return $("Conversation History")+` No conversation messages
52
- `;let t=s.conversationHistory.map(e=>{let r=X(e.timestamp),o=e.role.charAt(0).toUpperCase()+e.role.slice(1);return` [${r}] ${o}: ${e.content}`});return $("Conversation History")+t.join(`
51
+ `}function ot(i){if(!i.conversationHistory?.length)return z("Conversation History")+` No conversation messages
52
+ `;let t=i.conversationHistory.map(e=>{let r=ie(e.timestamp),o=e.role.charAt(0).toUpperCase()+e.role.slice(1);return` [${r}] ${o}: ${e.content}`});return z("Conversation History")+t.join(`
53
53
  `)+`
54
- `}function Ae(){let s=new Go("results");return s.description("Display detailed results for a completed job").argument("<jobId>","Job ID to show results for").option("-j, --json","Output as JSON").option("--schema-only","Show only extracted schema data").option("--raw","Show raw tester response (no processing)").option("--transcript","Show voice transcript").option("--console-logs","Show console log output").option("--network","Show network requests").option("--events","Show user interaction events").option("--key-moments","Show key moments").option("--conversation","Show conversation history").option("-a, --all","Show all rich test data").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"job",t),i=await c.getJob(l);if(e.json){let d=p.result(i);n.output(d);return}if(n.heading(`Test Results: ${l}`),n.sectionHeader("Job Information"),n.detail("Job ID",i.id),n.detail("Status",i.status),i.testerAlias&&n.detail("Tester",i.testerAlias),i.url&&n.detail("URL",i.url),i.description&&n.detail("Description",i.description),i.createdAt&&n.detail("Created",n.formatTimestamp(i.createdAt)),i.completedAt&&n.detail("Completed",n.formatTimestamp(i.completedAt)),i.testDurationSeconds&&n.detail("Duration",n.formatDuration(i.testDurationSeconds)),i.costUsd&&n.detail("Cost",`$${i.costUsd.toFixed(3)}`),i.deviceClass&&n.detail("Device Class",i.deviceClass),i.requiredDevices?.length&&n.detail("Required Devices",i.requiredDevices.join(", ")),i.requiredLanguages?.length&&n.detail("Required Langs",i.requiredLanguages.join(", ")),i.requireSocialVideos&&n.detail("Social Videos","required"),i.result&&Object.keys(i.result).length>0&&!e.raw){n.sectionHeader("Structured Results (Extracted)");for(let[d,m]of Object.entries(i.result)){let g=typeof m=="object"?JSON.stringify(m,null,2):String(m);console.log(` ${d}:`.padEnd(30)+g)}}i.testerResponse&&!e.schemaOnly&&(n.sectionHeader("Tester Feedback"),console.log(" "+i.testerResponse.split(`
54
+ `}function Pe(){let i=new ln("results");return i.description("Display detailed results for a completed job").argument("<jobId>","Job ID to show results for").option("-p, --project <id>","Scope search to a specific project").option("-o, --organization <id>","Scope search to a specific organization").option("-j, --json","Output as JSON").option("--schema-only","Show only extracted schema data").option("--raw","Show raw tester response (no processing)").option("--transcript","Show voice transcript").option("--console-logs","Show console log output").option("--network","Show network requests").option("--events","Show user interaction events").option("--key-moments","Show key moments").option("--conversation","Show conversation history").option("-a, --all","Show all rich test data").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u={projectId:e.project?await y(a,"project",e.project):void 0,organizationId:e.organization?await y(a,"organization",e.organization):void 0},l=await q(a,t,u,o,n),s=await a.getJob(l);if(e.json){let m=p.result(s);n.output(m);return}if(n.heading(`Test Results: ${l}`),n.sectionHeader("Job Information"),n.detail("Job ID",s.id),n.detail("Status",s.status),s.testerAlias&&n.detail("Tester",s.testerAlias),s.url&&n.detail("URL",s.url),s.description&&n.detail("Description",s.description),s.createdAt&&n.detail("Created",n.formatTimestamp(s.createdAt)),s.completedAt&&n.detail("Completed",n.formatTimestamp(s.completedAt)),s.testDurationSeconds&&n.detail("Duration",n.formatDuration(s.testDurationSeconds)),s.costUsd&&n.detail("Cost",`$${s.costUsd.toFixed(3)}`),s.deviceClass&&n.detail("Device Class",s.deviceClass),s.requiredDevices?.length&&n.detail("Required Devices",s.requiredDevices.join(", ")),s.requiredLanguages?.length&&n.detail("Required Langs",s.requiredLanguages.join(", ")),s.requireSocialVideos&&n.detail("Social Videos","required"),s.result&&Object.keys(s.result).length>0&&!e.raw){n.sectionHeader("Structured Results (Extracted)");for(let[m,g]of Object.entries(s.result)){let j=typeof g=="object"?JSON.stringify(g,null,2):String(g);console.log(` ${m}:`.padEnd(30)+j)}}s.testerResponse&&!e.schemaOnly&&(n.sectionHeader("Tester Feedback"),console.log(" "+s.testerResponse.split(`
55
55
  `).join(`
56
- `)));let u=e.all;(u||e.transcript)&&console.log(Le(i)),(u||e.consoleLogs)&&console.log(_e(i)),(u||e.network)&&console.log(Fe(i)),(u||e.events)&&console.log(Je(i)),(u||e.keyMoments)&&console.log(Ke(i)),(u||e.conversation)&&console.log(qe(i)),n.separator(),console.log(),i.richResultsGated&&(console.log(" Rich test data (transcript, logs, network) requires a paid plan."),console.log(` Upgrade at https://runhuman.com/pricing
57
- `))}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}P();R();v();I();import{Command as Wo}from"commander";x();function Se(){let s=new Wo("list");return s.description("List all jobs with optional filtering").argument("[filter]","Status filter: all|pending|claimed|in_progress|completed|failed|timeout").option("-p, --project <id>","Filter by project").option("-n, --limit <number>","Number of results (default: 20)",parseInt).option("--offset <number>","Pagination offset (default: 0)",parseInt).option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,format:e.format,color:o.color}),c=new b(o),l=A("project",e.project,o,n),i=await y(c,"project",l),u={limit:e.limit||20,offset:e.offset||0,projectId:i};t&&t!=="all"&&(u.status=t);let{items:d,pagination:m}=await c.listJobs(u);if(e.json){let g=p.result({jobs:d,pagination:m});n.output(g)}else{if(m.total===0){n.heading("Recent Jobs (0)"),console.log(`No jobs found.
58
- `),n.hints([["Create a job",'runhuman create <url> -d "Test description"']]);return}n.heading(`Recent Jobs (${d.length} of ${m.total})`),console.log(n.formatJobList(d)),console.log(),n.hints([["View details","runhuman status <jobId>"],["View results","runhuman results <jobId>"]])}}catch(r){let o=new p({json:e.json}),n=f(r);o.outputError(n.message,n.details),process.exit(n.exitCode)}}),s}P();R();v();I();x();import{Command as Bo}from"commander";import Yo from"inquirer";function Te(){let s=new Bo("delete");return s.description("Delete a job permanently").argument("<jobId>","Job ID to delete").option("-f, --force","Skip confirmation prompt").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"job",t);if(!e.json&&!e.force){let{confirmed:i}=await Yo.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete job ${l}? This cannot be undone.`,default:!1}]);if(!i)return}if(await c.deleteJob(l),e.json){let i=p.result({success:!0,message:"Job deleted successfully",jobId:l});n.output(i)}else n.success("Job deleted successfully"),n.detail("Job ID",l),console.log()}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}P();R();v();import{Command as Zo}from"commander";import Qo from"chokidar";I();x();import{existsSync as pe,mkdirSync as Xo,readFileSync as Ee,writeFileSync as en,unlinkSync as tn}from"fs";import{join as He}from"path";import{homedir as Ve}from"os";var q=He(Ve(),".config","runhuman","watch.pid");function ve(){let s=new Zo("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("-u, --url <url>","URL to test (required)").option("-t, --template <name>","Template to use for tests").option("-d, --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 nn();return}if(e.status){rn();return}Ge()&&(console.log(`
56
+ `)));let d=e.all;(d||e.transcript)&&console.log(Ze(s)),(d||e.consoleLogs)&&console.log(Qe(s)),(d||e.network)&&console.log(Xe(s)),(d||e.events)&&console.log(et(s)),(d||e.keyMoments)&&console.log(tt(s)),(d||e.conversation)&&console.log(ot(s)),n.separator(),console.log(),s.richResultsGated&&(console.log(" Rich test data (transcript, logs, network) requires a paid plan."),console.log(` Upgrade at https://runhuman.com/pricing
57
+ `))}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}x();R();E();C();import{Command as un}from"commander";U();function xe(){let i=new un("list");return i.description("List all jobs with optional filtering").argument("[filter]","Status filter: all|pending|claimed|in_progress|completed|failed|timeout").option("-p, --project <id>","Filter by project").option("-n, --limit <number>","Number of results (default: 20)",parseInt).option("--offset <number>","Pagination offset (default: 0)",parseInt).option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,format:e.format,color:o.color}),a=new b(o),u=A("project",e.project,o,n),l=await y(a,"project",u),s={limit:e.limit||20,offset:e.offset||0,projectId:l};t&&t!=="all"&&(s.status=t);let{items:d,pagination:m}=await a.listJobs(s);if(e.json){let g=p.result({jobs:d,pagination:m});n.output(g)}else{if(m.total===0){n.heading("Recent Jobs (0)"),console.log(`No jobs found.
58
+ `),n.hints([["Create a job",'runhuman create <url> -d "Test description"']]);return}n.heading(`Recent Jobs (${d.length} of ${m.total})`),console.log(n.formatJobList(d)),console.log(),n.hints([["View details","runhuman status <jobId>"],["View results","runhuman results <jobId>"]])}}catch(r){let o=new p({json:e.json}),n=f(r);o.outputError(n.message,n.details),process.exit(n.exitCode)}}),i}x();R();E();C();ee();U();import{Command as pn}from"commander";import dn from"inquirer";function ke(){let i=new pn("delete");return i.description("Delete a job permanently").argument("<jobId>","Job ID to delete").option("-p, --project <id>","Scope search to a specific project").option("-o, --organization <id>","Scope search to a specific organization").option("-f, --force","Skip confirmation prompt").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u={projectId:e.project?await y(a,"project",e.project):void 0,organizationId:e.organization?await y(a,"organization",e.organization):void 0},l=await q(a,t,u,o,n);if(!e.json&&!e.force){let{confirmed:s}=await dn.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete job ${l}? This cannot be undone.`,default:!1}]);if(!s)return}if(await a.deleteJob(l),e.json){let s=p.result({success:!0,message:"Job deleted successfully",jobId:l});n.output(s)}else n.success("Job deleted successfully"),n.detail("Job ID",l),console.log()}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}x();R();E();import{Command as mn}from"commander";import gn from"chokidar";C();U();import{existsSync as he,mkdirSync as fn,readFileSync as Ue,writeFileSync as hn,unlinkSync as bn}from"fs";import{join as nt}from"path";import{homedir as rt}from"os";var H=nt(rt(),".config","runhuman","watch.pid");function Oe(){let i=new mn("watch");return i.description("Watch files and auto-create QA test jobs on changes").argument("[patterns...]",'File patterns to watch (e.g., "src/**/*.tsx")').option("-u, --url <url>","URL to test (required)").option("-t, --template <name>","Template to use for tests").option("-d, --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 jn();return}if(e.status){wn();return}it()&&(console.log(`
59
59
  Watch mode is already running`),console.log(` Use --stop to stop it first
60
- `),process.exit(1));let o=await new h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=o.watch||{},c=t.length>0?t:n.patterns||["src/**/*"],l=e.ignore||n.ignore||["**/node_modules/**","**/dist/**","**/build/**","**/.git/**"],i=e.debounce||n.debounce||2e3,u=e.url||n.url||o.defaultUrl,d=e.description||n.description||"Auto-test from watch mode",m=e.template||n.template;u||(console.log(`
60
+ `),process.exit(1));let o=await new h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=o.watch||{},a=t.length>0?t:n.patterns||["src/**/*"],u=e.ignore||n.ignore||["**/node_modules/**","**/dist/**","**/build/**","**/.git/**"],l=e.debounce||n.debounce||2e3,s=e.url||n.url||o.defaultUrl,d=e.description||n.description||"Auto-test from watch mode",m=e.template||n.template;s||(console.log(`
61
61
  \u274C Error: URL required
62
62
  `),console.log(`Provide via --url flag or set in .runhumanrc:
63
63
  `),console.log(' runhuman watch --url "https://myapp.com"'),console.log(` OR add to .runhumanrc: { "watch": { "url": "..." } }
64
- `),process.exit(1));let g=new p({color:o.color}),j=new b(o),T=A("project",void 0,o,g),E=await y(j,"project",T),C=m?await y(j,"template",m):void 0;g.heading("Starting watch mode"),g.detail("Watching",c.join(", ")),g.detail("Ignoring",l.join(", ")),g.detail("Debounce",`${i}ms`),g.detail("URL",u),m&&g.detail("Template",m),console.log(`
64
+ `),process.exit(1));let g=new p({color:o.color}),j=new b(o),T=A("project",void 0,o,g),v=await y(j,"project",T),I=m?await y(j,"template",m):void 0;g.heading("Starting watch mode"),g.detail("Watching",a.join(", ")),g.detail("Ignoring",u.join(", ")),g.detail("Debounce",`${l}ms`),g.detail("URL",s),m&&g.detail("Template",m),console.log(`
65
65
  Press Ctrl+C to stop
66
- `),on(process.pid);let O=null,N=new Set,W=Qo.watch(c,{ignored:l,persistent:!0,ignoreInitial:!0});W.on("change",B=>{N.add(B),console.log(` Changed: ${B}`),O&&clearTimeout(O),O=setTimeout(async()=>{let ke=Array.from(N);N.clear(),console.log(`
67
- Creating test job for ${ke.length} file(s)...
68
- `);try{let me={projectId:E,url:u,description:`${d}
66
+ `),yn(process.pid);let O=null,N=new Set,Y=gn.watch(a,{ignored:u,persistent:!0,ignoreInitial:!0});Y.on("change",Z=>{N.add(Z),console.log(` Changed: ${Z}`),O&&clearTimeout(O),O=setTimeout(async()=>{let Ne=Array.from(N);N.clear(),console.log(`
67
+ Creating test job for ${Ne.length} file(s)...
68
+ `);try{let ye={projectId:v,url:s,description:`${d}
69
69
 
70
70
  Changed files:
71
- ${ke.map(wt=>`- ${wt}`).join(`
72
- `)}`,templateId:C},Q=await j.createJob(me);console.log(` Job created: ${Q.jobId}`),console.log(` Dashboard: ${o.apiUrl}${_.jobSimple.build({jobId:Q.jobId})}
71
+ ${Ne.map(Ot=>`- ${Ot}`).join(`
72
+ `)}`,templateId:I},te=await j.createJob(ye);console.log(` Job created: ${te.jobId}`),console.log(` Dashboard: ${o.apiUrl}${F.jobSimple.build({jobId:te.jobId})}
73
73
  `),console.log(` Watching for more changes...
74
- `)}catch(me){let Q=f(me);g.outputError(Q.message,Q.details),console.log(`
74
+ `)}catch(ye){let te=f(ye);g.outputError(te.message,te.details),console.log(`
75
75
  Watching for more changes...
76
- `)}},i)}),W.on("error",B=>{console.error(`
77
- \u274C Watch error: ${B instanceof Error?B.message:String(B)}
78
- `)});let Re=()=>{console.log(`
76
+ `)}},l)}),Y.on("error",Z=>{console.error(`
77
+ \u274C Watch error: ${Z instanceof Error?Z.message:String(Z)}
78
+ `)});let De=()=>{console.log(`
79
79
 
80
80
  Stopping watch mode...
81
- `),W.close(),ee(),process.exit(0)};process.on("SIGINT",Re),process.on("SIGTERM",Re)}catch(r){let o=f(r);new p({}).outputError(o.message,o.details),ee(),process.exit(o.exitCode)}}),s}function on(s){let t=He(Ve(),".config","runhuman");pe(t)||Xo(t,{recursive:!0}),en(q,s.toString())}function ee(){pe(q)&&tn(q)}function Ge(){if(!pe(q))return!1;try{let s=parseInt(Ee(q,"utf-8"));return process.kill(s,0),!0}catch{return ee(),!1}}async function nn(){if(!pe(q)){console.log(`
81
+ `),Y.close(),se(),process.exit(0)};process.on("SIGINT",De),process.on("SIGTERM",De)}catch(r){let o=f(r);new p({}).outputError(o.message,o.details),se(),process.exit(o.exitCode)}}),i}function yn(i){let t=nt(rt(),".config","runhuman");he(t)||fn(t,{recursive:!0}),hn(H,i.toString())}function se(){he(H)&&bn(H)}function it(){if(!he(H))return!1;try{let i=parseInt(Ue(H,"utf-8"));return process.kill(i,0),!0}catch{return se(),!1}}async function jn(){if(!he(H)){console.log(`
82
82
  Watch mode is not running
83
- `);return}try{let s=parseInt(Ee(q,"utf-8"));process.kill(s,"SIGTERM"),ee(),console.log(`
83
+ `);return}try{let i=parseInt(Ue(H,"utf-8"));process.kill(i,"SIGTERM"),se(),console.log(`
84
84
  Watch mode stopped
85
- `)}catch{ee(),console.log(`
85
+ `)}catch{se(),console.log(`
86
86
  Watch process not found (already stopped)
87
- `)}}function rn(){if(Ge()){let s=parseInt(Ee(q,"utf-8"));console.log(`
88
- Watch mode is running`),console.log(` PID: ${s}`),console.log(`
87
+ `)}}function wn(){if(it()){let i=parseInt(Ue(H,"utf-8"));console.log(`
88
+ Watch mode is running`),console.log(` PID: ${i}`),console.log(`
89
89
  Stop with: runhuman watch --stop
90
90
  `)}else console.log(`
91
91
  Watch mode is not running
92
- `)}P();R();I();v();import{Command as un}from"commander";import sn from"http";import{URL as an}from"url";import cn from"open";async function de(s){let{apiUrl:t,autoOpenBrowser:e=!0}=s;return new Promise((r,o)=>{let n=sn.createServer((l,i)=>{if(!l.url){i.writeHead(400),i.end("Bad request");return}let u=new an(l.url,"http://localhost");if(u.pathname==="/callback"){let d=u.searchParams.get("token"),m=u.searchParams.get("email"),g=u.searchParams.get("projectId")||"",j=u.searchParams.get("error");if(j){i.writeHead(200,{"Content-Type":"text/html"}),i.end(We(j)),n.close(),o(new Error(j));return}if(d&&m){i.writeHead(200,{"Content-Type":"text/html"}),i.end(ln(m)),n.close(),r({token:d,email:m,projectId:g});return}i.writeHead(400,{"Content-Type":"text/html"}),i.end(We("Missing token or email in callback")),n.close(),o(new Error("Missing token or email in callback"));return}i.writeHead(404),i.end("Not found")});n.listen(0,"127.0.0.1",async()=>{let l=n.address();if(!l||typeof l=="string"){o(new Error("Failed to start local server"));return}let u=`http://127.0.0.1:${l.port}/callback`,d=`${t}/cli/auth?callback=${encodeURIComponent(u)}`;if(console.log(""),e){console.log("Opening browser for authentication...");try{await cn(d)}catch{console.log("Could not open browser automatically."),console.log(`
93
- Please visit: ${d}`)}}else console.log(`Please visit: ${d}`);console.log(""),console.log("Waiting for authentication...")});let c=setTimeout(()=>{n.close(),o(new Error("Authentication timed out. Please try again."))},60*1e3);n.on("close",()=>clearTimeout(c)),n.on("error",l=>{clearTimeout(c),o(new Error(`Failed to start local server: ${l.message}`))})})}function ln(s){return`<!DOCTYPE html>
92
+ `)}x();R();C();E();import{Command as Tn}from"commander";import In from"http";import{URL as Cn}from"url";import An from"open";async function be(i){let{apiUrl:t,autoOpenBrowser:e=!0}=i;return new Promise((r,o)=>{let n=In.createServer((u,l)=>{if(!u.url){l.writeHead(400),l.end("Bad request");return}let s=new Cn(u.url,"http://localhost");if(s.pathname==="/callback"){let d=s.searchParams.get("token"),m=s.searchParams.get("email"),g=s.searchParams.get("projectId")||"",j=s.searchParams.get("error");if(j){l.writeHead(200,{"Content-Type":"text/html"}),l.end(st(j)),n.close(),o(new Error(j));return}if(d&&m){l.writeHead(200,{"Content-Type":"text/html"}),l.end(Sn(m)),n.close(),r({token:d,email:m,projectId:g});return}l.writeHead(400,{"Content-Type":"text/html"}),l.end(st("Missing token or email in callback")),n.close(),o(new Error("Missing token or email in callback"));return}l.writeHead(404),l.end("Not found")});n.listen(0,"127.0.0.1",async()=>{let u=n.address();if(!u||typeof u=="string"){o(new Error("Failed to start local server"));return}let s=`http://127.0.0.1:${u.port}/callback`,d=`${t}/cli/auth?callback=${encodeURIComponent(s)}`;if(console.log(""),e){console.log("Opening browser for authentication...");try{await An(d)}catch{console.log("Could not open browser automatically."),console.log(`
93
+ Please visit: ${d}`)}}else console.log(`Please visit: ${d}`);console.log(""),console.log("Waiting for authentication...")});let a=setTimeout(()=>{n.close(),o(new Error("Authentication timed out. Please try again."))},60*1e3);n.on("close",()=>clearTimeout(a)),n.on("error",u=>{clearTimeout(a),o(new Error(`Failed to start local server: ${u.message}`))})})}function Sn(i){return`<!DOCTYPE html>
94
94
  <html>
95
95
  <head>
96
96
  <title>Runhuman CLI - Authenticated</title>
@@ -128,11 +128,11 @@ Please visit: ${d}`)}}else console.log(`Please visit: ${d}`);console.log(""),con
128
128
  <body>
129
129
  <div class="container">
130
130
  <h1>Successfully authenticated!</h1>
131
- <p>Logged in as <span class="email">${s}</span></p>
131
+ <p>Logged in as <span class="email">${i}</span></p>
132
132
  <p class="close-note">You can close this tab and return to the terminal.</p>
133
133
  </div>
134
134
  </body>
135
- </html>`}function We(s){return`<!DOCTYPE html>
135
+ </html>`}function st(i){return`<!DOCTYPE html>
136
136
  <html>
137
137
  <head>
138
138
  <title>Runhuman CLI - Authentication Failed</title>
@@ -172,45 +172,45 @@ Please visit: ${d}`)}}else console.log(`Please visit: ${d}`);console.log(""),con
172
172
  <div class="container">
173
173
  <h1>Authentication failed</h1>
174
174
  <p>Please try again from the terminal.</p>
175
- <div class="error">${s}</div>
175
+ <div class="error">${i}</div>
176
176
  </div>
177
177
  </body>
178
- </html>`}function Be(){let s=new un("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("-j, --json","Output as JSON").action(async t=>{try{let e=new h,r=await e.loadConfig(),o=new p({json:t.json,color:r.color});if(t.token)await pn(e,o,t.token,t.json);else{let n=t.browser!==!1&&r.autoOpenBrowser!==!1;await dn(e,o,r.apiUrl,n,t.json)}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),s}async function pn(s,t,e,r){s.saveCredentials({accessToken:e});let o=await s.loadConfig({apiKey:e}),c=await new b(o).getCurrentUser();s.saveUserInfo(c),r?t.output(p.result({success:!0,user:c})):(t.success("Successfully logged in!"),t.detail("Email",c.email),t.detail("Account ID",c.accountId),console.log())}async function dn(s,t,e,r,o){o||console.log("Logging in to Runhuman...");let n=await de({apiUrl:e||"https://runhuman.com",autoOpenBrowser:r});s.saveCredentials({accessToken:n.token}),await s.set("project",n.projectId,!0);let c=await s.loadConfig({apiKey:n.token}),i=await new b(c).getCurrentUser();s.saveUserInfo(i),o?t.output(p.result({success:!0,user:i,projectId:n.projectId})):(console.log(""),t.success("Successfully logged in!"),t.detail("Email",i.email),t.detail("Account ID",i.accountId),t.detail("Default Project",n.projectId),console.log()),process.exit(0)}R();I();v();import{Command as mn}from"commander";function Ye(){let s=new mn("logout");return s.description("Log out and clear stored credentials").option("-f, --force","Skip confirmation prompt").option("-j, --json","Output as JSON").action(async t=>{try{let e=new h,r=new p({json:t.json});if(e.clearCredentials(),e.clearUserInfo(),t.json){let o=p.result({success:!0,message:"Logged out successfully"});r.output(o)}else r.success("Logged out successfully!"),console.log(` Credentials have been cleared.
179
- `)}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),s}P();R();I();v();import{Command as gn}from"commander";function Ze(){let s=new gn("whoami");return s.description("Display current user information").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new p({json:t.json}),c=await new b(r).getCurrentUser();if(t.json){let l=p.result({user:c});o.output(l)}else o.heading("Current User"),o.detail("Email",c.email),o.detail("Account ID",c.accountId),r.project&&(console.log(),o.detail("Default Project",r.project)),console.log()}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),s}R();I();v();import{Command as fn}from"commander";function Qe(){let s=new fn("config");return s.description("Manage CLI configuration"),s.command("get").description("Get a configuration value").argument("<key>","Configuration key").option("-j, --json","Output as JSON").action(async(t,e)=>{try{let o=await new h().get(t),n=new p({json:e.json});if(e.json){let c=p.result({[t]:o});n.output(c)}else console.log(o!==void 0?o:"(not set)")}catch(r){let o=f(r);new p({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("-g, --global","Set globally (not project-specific)").option("-j, --json","Output as JSON").action(async(t,e,r)=>{try{await new h().set(t,e,r.global);let n=new p({json:r.json});if(r.json){let c=p.result({success:!0,key:t,value:e,scope:r.global?"global":"project"});n.output(c)}else n.success(`Set ${t} = ${e}`+(r.global?" (global)":" (project)"))}catch(o){let n=f(o);new p({json:r.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),s.command("list").description("List all configuration values").option("--show-secrets","Show API keys (default: masked)").option("-j, --json","Output as JSON").action(async t=>{try{let r=await new h().list(),o=new p({json:t.json});if(t.json){let n=p.result(r);o.output(n)}else{o.heading("Configuration");let n=["organization","project","apiKey","apiUrl"],c={organization:"RUNHUMAN_ORGANIZATION",project:"RUNHUMAN_PROJECT",apiKey:"RUNHUMAN_API_KEY",apiUrl:"RUNHUMAN_API_URL"};console.log(`Resolution chain (highest priority \u2192 lowest):
180
- `);for(let i of n){let u=r.effective[i],d=r.env?.[i],m=r.project?.[i],g=r.global?.[i],j=i==="apiKey",T=O=>j&&!t.showSecrets?hn(String(O)):String(O),E=d?"env":m?"project":g?"global":u?"default":null;console.log(` ${i}:`),console.log(u?` \u2713 ${T(u)} [from ${E}]`:" (not set)");let C=c[i];d&&console.log(` \u251C\u2500 env (${C}): ${T(d)}`),m&&console.log(` \u251C\u2500 project (.runhumanrc): ${T(m)}`),g&&console.log(` \u2514\u2500 global (~/.config/runhuman): ${T(g)}`),console.log()}let l=Object.keys(r.effective).filter(i=>!n.includes(i));if(l.length>0){console.log(`Other settings:
181
- `);for(let i of l){let u=r.effective[i];u!==void 0&&console.log(` ${i}:`.padEnd(24)+String(u))}console.log()}console.log("Environment Variables:"),console.log(" RUNHUMAN_ORGANIZATION Default organization ID"),console.log(" RUNHUMAN_PROJECT Default project ID"),console.log(" RUNHUMAN_API_KEY API key"),console.log(" RUNHUMAN_API_URL API URL"),console.log(" RUNHUMAN_NO_COLOR=1 Disable colors"),console.log(),o.hints([["Set value","runhuman config set <key> <value>"],["Get value","runhuman config get <key>"]])}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),s.command("reset").description("Reset configuration to defaults").option("-g, --global","Reset global config").option("--project","Reset project config").option("--all","Reset all config").option("-f, --force","Skip confirmation prompt").option("-j, --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.
182
- `),process.exit(0)),await new h().reset(e);let o=new p({json:t.json});if(t.json){let n=p.result({success:!0,scope:e});o.output(n)}else o.success(`Reset ${e} configuration`)}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),s}function hn(s){return s.length<=8?"****":s.substring(0,4)+"*".repeat(s.length-8)+s.substring(s.length-4)}I();v();import{Command as bn}from"commander";import yn from"inquirer";import{writeFileSync as jn}from"fs";import{join as wn}from"path";function Xe(){let s=new bn("init");return s.description("Initialize a new Runhuman project with configuration").option("-n, --name <text>","Project name").option("-u, --url <url>","Default URL").option("--github-repo <owner/repo>","GitHub repository").option("-y, --yes","Skip all prompts (use defaults)").option("-j, --json","Output as JSON").action(async t=>{try{let e=new p({json:t.json});!t.json&&!t.yes&&(console.log(`Welcome to Runhuman!
178
+ </html>`}function at(){let i=new Tn("login");return i.description("Authenticate with Runhuman").option("--token <token>","Login with API key (skip browser)").option("--no-browser","Print auth URL instead of opening browser").option("-j, --json","Output as JSON").action(async t=>{try{let e=new h,r=await e.loadConfig(),o=new p({json:t.json,color:r.color});if(t.token)await vn(e,o,t.token,t.json);else{let n=t.browser!==!1&&r.autoOpenBrowser!==!1;await En(e,o,r.apiUrl,n,t.json)}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i}async function vn(i,t,e,r){i.saveCredentials({accessToken:e});let o=await i.loadConfig({apiKey:e}),a=await new b(o).getCurrentUser();i.saveUserInfo(a),r?t.output(p.result({success:!0,user:a})):(t.success("Successfully logged in!"),t.detail("Email",a.email),t.detail("Account ID",a.accountId),console.log())}async function En(i,t,e,r,o){o||console.log("Logging in to Runhuman...");let n=await be({apiUrl:e||"https://runhuman.com",autoOpenBrowser:r});i.saveCredentials({accessToken:n.token}),await i.set("project",n.projectId,!0);let a=await i.loadConfig({apiKey:n.token}),l=await new b(a).getCurrentUser();i.saveUserInfo(l),o?t.output(p.result({success:!0,user:l,projectId:n.projectId})):(console.log(""),t.success("Successfully logged in!"),t.detail("Email",l.email),t.detail("Account ID",l.accountId),t.detail("Default Project",n.projectId),console.log()),process.exit(0)}R();C();E();import{Command as Rn}from"commander";function ct(){let i=new Rn("logout");return i.description("Log out and clear stored credentials").option("-f, --force","Skip confirmation prompt").option("-j, --json","Output as JSON").action(async t=>{try{let e=new h,r=new p({json:t.json});if(e.clearCredentials(),e.clearUserInfo(),t.json){let o=p.result({success:!0,message:"Logged out successfully"});r.output(o)}else r.success("Logged out successfully!"),console.log(` Credentials have been cleared.
179
+ `)}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i}x();R();C();E();import{Command as Pn}from"commander";function lt(){let i=new Pn("whoami");return i.description("Display current user information").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new p({json:t.json}),a=await new b(r).getCurrentUser();if(t.json){let u=p.result({user:a});o.output(u)}else o.heading("Current User"),o.detail("Email",a.email),o.detail("Account ID",a.accountId),r.project&&(console.log(),o.detail("Default Project",r.project)),console.log()}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i}R();C();E();import{Command as xn}from"commander";function ut(){let i=new xn("config");return i.description("Manage CLI configuration"),i.command("get").description("Get a configuration value").argument("<key>","Configuration key").option("-j, --json","Output as JSON").action(async(t,e)=>{try{let o=await new h().get(t),n=new p({json:e.json});if(e.json){let a=p.result({[t]:o});n.output(a)}else console.log(o!==void 0?o:"(not set)")}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("set").description("Set a configuration value").argument("<key>","Configuration key").argument("<value>","Configuration value").option("-g, --global","Set globally (not project-specific)").option("-j, --json","Output as JSON").action(async(t,e,r)=>{try{await new h().set(t,e,r.global);let n=new p({json:r.json});if(r.json){let a=p.result({success:!0,key:t,value:e,scope:r.global?"global":"project"});n.output(a)}else n.success(`Set ${t} = ${e}`+(r.global?" (global)":" (project)"))}catch(o){let n=f(o);new p({json:r.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i.command("list").description("List all configuration values").option("--show-secrets","Show API keys (default: masked)").option("-j, --json","Output as JSON").action(async t=>{try{let r=await new h().list(),o=new p({json:t.json});if(t.json){let n=p.result(r);o.output(n)}else{o.heading("Configuration");let n=["organization","project","apiKey","apiUrl"],a={organization:"RUNHUMAN_ORGANIZATION",project:"RUNHUMAN_PROJECT",apiKey:"RUNHUMAN_API_KEY",apiUrl:"RUNHUMAN_API_URL"};console.log(`Resolution chain (highest priority \u2192 lowest):
180
+ `);for(let l of n){let s=r.effective[l],d=r.env?.[l],m=r.project?.[l],g=r.global?.[l],j=l==="apiKey",T=O=>j&&!t.showSecrets?kn(String(O)):String(O),v=d?"env":m?"project":g?"global":s?"default":null;console.log(` ${l}:`),console.log(s?` \u2713 ${T(s)} [from ${v}]`:" (not set)");let I=a[l];d&&console.log(` \u251C\u2500 env (${I}): ${T(d)}`),m&&console.log(` \u251C\u2500 project (.runhumanrc): ${T(m)}`),g&&console.log(` \u2514\u2500 global (~/.config/runhuman): ${T(g)}`),console.log()}let u=Object.keys(r.effective).filter(l=>!n.includes(l));if(u.length>0){console.log(`Other settings:
181
+ `);for(let l of u){let s=r.effective[l];s!==void 0&&console.log(` ${l}:`.padEnd(24)+String(s))}console.log()}console.log("Environment Variables:"),console.log(" RUNHUMAN_ORGANIZATION Default organization ID"),console.log(" RUNHUMAN_PROJECT Default project ID"),console.log(" RUNHUMAN_API_KEY API key"),console.log(" RUNHUMAN_API_URL API URL"),console.log(" RUNHUMAN_NO_COLOR=1 Disable colors"),console.log(),o.hints([["Set value","runhuman config set <key> <value>"],["Get value","runhuman config get <key>"]])}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i.command("reset").description("Reset configuration to defaults").option("-g, --global","Reset global config").option("--project","Reset project config").option("--all","Reset all config").option("-f, --force","Skip confirmation prompt").option("-j, --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.
182
+ `),process.exit(0)),await new h().reset(e);let o=new p({json:t.json});if(t.json){let n=p.result({success:!0,scope:e});o.output(n)}else o.success(`Reset ${e} configuration`)}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i}function kn(i){return i.length<=8?"****":i.substring(0,4)+"*".repeat(i.length-8)+i.substring(i.length-4)}C();E();import{Command as Un}from"commander";import On from"inquirer";import{writeFileSync as Dn}from"fs";import{join as Nn}from"path";function pt(){let i=new Un("init");return i.description("Initialize a new Runhuman project with configuration").option("-n, --name <text>","Project name").option("-u, --url <url>","Default URL").option("--github-repo <owner/repo>","GitHub repository").option("-y, --yes","Skip all prompts (use defaults)").option("-j, --json","Output as JSON").action(async t=>{try{let e=new p({json:t.json});!t.json&&!t.yes&&(console.log(`Welcome to Runhuman!
183
183
  `),console.log("Let's set up your project. This will create:"),console.log(" - A configuration file (.runhumanrc)"),console.log(` - Setup your project defaults
184
- `));let r=t.name,o=t.url,n=t.githubRepo;if(!t.yes&&!t.json){let i=await yn.prompt([{type:"input",name:"projectName",message:"Project name:",default:"My Project",when:!r},{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:!n}]);r=r||i.projectName,o=o||i.defaultUrl,n=n||i.githubRepo}let c={defaultUrl:o||void 0,githubRepo:n||void 0,defaultDuration:5,defaultDeviceClass:"desktop"},l=wn(process.cwd(),".runhumanrc");if(jn(l,JSON.stringify(c,null,2)),t.json){let i=p.result({success:!0,configPath:l,config:c});e.output(i)}else e.success("Project initialized successfully!"),console.log(` Configuration saved to: .runhumanrc
184
+ `));let r=t.name,o=t.url,n=t.githubRepo;if(!t.yes&&!t.json){let l=await On.prompt([{type:"input",name:"projectName",message:"Project name:",default:"My Project",when:!r},{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:!n}]);r=r||l.projectName,o=o||l.defaultUrl,n=n||l.githubRepo}let a={defaultUrl:o||void 0,githubRepo:n||void 0,defaultDuration:5,defaultDeviceClass:"desktop"},u=Nn(process.cwd(),".runhumanrc");if(Dn(u,JSON.stringify(a,null,2)),t.json){let l=p.result({success:!0,configPath:u,config:a});e.output(l)}else e.success("Project initialized successfully!"),console.log(` Configuration saved to: .runhumanrc
185
185
  `),console.log(` All set! Try creating your first test:
186
186
  `),console.log(` runhuman create https://example.com -d "Test homepage"
187
187
  `),console.log(` Learn more: https://runhuman.com/docs/cli
188
- `)}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),s}P();R();I();v();import{Command as Cn}from"commander";import et from"inquirer";x();import In from"cli-table3";function tt(){let s=new Cn("projects");return s.description("Manage projects"),s.command("list").alias("ls").description("List all projects").option("-n, --limit <number>","Number of results (default: 20)",parseInt).option("--offset <number>","Pagination offset (default: 0)",parseInt).option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new p({json:t.json,color:r.color}),n=new b(r),c=t.limit||20,l=t.offset||0,[{items:i,pagination:u},{items:d}]=await Promise.all([n.listProjects({limit:c,offset:l}),n.listOrganizations({})]),m=new Map(d.map(g=>[g.id,g.name]));if(t.json){let g=p.result({projects:i,pagination:u});o.output(g)}else{if(u.total===0){o.heading("Your Projects (0)"),console.log(`No projects found.
189
- `),o.hints([["Create a project","runhuman projects create <name> -o <org-id>"]]);return}o.heading(`Your Projects (${i.length} of ${u.total})`);let g=new In({head:o.tableHead(["Name","Organization","Project ID","Created"]),colWidths:[25,25,25,15]});i.forEach(C=>{let O=m.get(C.organizationId)||"(unknown)";g.push([C.name,O,C.id,o.formatShortDate(C.createdAt)])}),console.log(g.toString());let j=l+1,T=l+i.length;console.log(`
190
- Showing ${j}-${T} of ${u.total} projects`);let E=[["View project","runhuman projects show <id>"],["Filter by org","runhuman orgs projects <org-id>"]];u.hasMore&&E.push(["Next page",`runhuman projects list --offset ${l+c}`]),console.log(),o.hints(E)}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),s.command("create").alias("new").description("Create a new project in an organization").argument("<name>","Project name").option("-o, --organization <id>","Organization ID (required)").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("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let r=new h,o=await r.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=A("organization",e.organization,o,n),i=await y(c,"organization",l),u=await c.createProject({name:t,organizationId:i,defaultUrl:e.defaultUrl,githubRepo:e.githubRepo});if(e.setDefault&&await r.set("project",u.id,!1),e.json){let d=p.result(u);n.output(d)}else n.success("Project created successfully!"),console.log(" Project ID: "+u.id),console.log(" Name: "+u.name),e.setDefault&&console.log(" Set as default project"),console.log()}catch(r){let o=f(r);new p({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("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"project",t),i=await c.getProject(l),[u,d]=await Promise.all([c.getOrganization(i.organizationId),c.getOrganizationBilling(i.organizationId)]);if(e.json){let m=p.result({project:i,organization:u,balance:{balance:d.balance,balanceUsd:d.balanceUsd}});n.output(m)}else n.heading("Project Details"),n.detail("Project ID",i.id),n.detail("Name",i.name),n.detail("Organization",u.name+" ("+u.id+")"),n.detail("Org Balance","$"+d.balanceUsd.toFixed(2)),i.defaultUrl&&n.detail("Default URL",i.defaultUrl),i.githubRepo&&n.detail("GitHub Repo",i.githubRepo),n.detail("Created",n.formatTimestamp(i.createdAt)),n.detail("Last Updated",n.formatTimestamp(i.updatedAt)),console.log(),n.hints([["View organization","runhuman orgs show "+u.id],["List org projects","runhuman orgs projects "+u.id],["Check org balance","runhuman orgs balance "+u.id]])}catch(r){let o=f(r);new p({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("-g, --global","Set as global default instead of local").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let r=new h,o=await r.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"project",t),i=await c.getProject(l);if(await r.set("project",l,e.global!==!1),e.json){let u=p.result({success:!0,projectId:l,projectName:i.name,scope:e.global!==!1?"global":"local"});n.output(u)}else n.success(`Default project set to: ${i.name} (${l})`),e.global!==!1?console.log(" Scope: global (all directories)"):console.log(" Scope: local (current directory only)"),console.log()}catch(r){let o=f(r);new p({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("-n, --name <text>","Update name").option("-u, --default-url <url>","Update default URL").option("-g, --github-repo <owner/repo>","Update GitHub repo").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c={name:e.name,defaultUrl:e.defaultUrl,githubRepo:e.githubRepo},l=new b(o),i=await y(l,"project",t),u=await l.updateProject(i,c);if(e.json){let d=p.result(u);n.output(d)}else n.success("Project updated successfully!")}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("transfer").description("Transfer a project to another organization").argument("<projectId>","Project ID to transfer").requiredOption("--to-org <organizationId>","Target organization ID").option("-f, --force","Skip confirmation prompt").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"project",t),i=await y(c,"organization",e.toOrg,{requireFullId:{reason:"transfer target"}}),[u,d]=await Promise.all([c.getProject(l),c.getOrganization(i)]);if(!e.json&&!e.force){let{confirmed:g}=await et.prompt([{type:"confirm",name:"confirmed",message:`Transfer "${u.name}" to organization "${d.name}"? The current organization will lose access to this project.`,default:!1}]);if(!g)return}let m=await c.initiateTransfer(l,i);e.json?n.output(p.result(m)):m.type==="immediate"?(n.success(`Project transferred to ${d.name}!`),n.detail("Project",m.project.name),n.detail("New Organization",d.name),console.log(),n.hints([["View project","runhuman projects show "+l]])):(n.success("Transfer request sent (pending approval)."),n.detail("Transfer ID",m.transfer.id),n.detail("Project",u.name),n.detail("To Organization",d.name),console.log(),n.hints([["Check status","runhuman transfers list"],["Cancel transfer","runhuman transfers cancel "+m.transfer.id]]))}catch(r){let o=f(r);new p({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("-f, --force","Skip confirmation prompt").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"project",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.force){let{confirmed:i}=await et.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete project ${l} and all its data? This cannot be undone.`,default:!1}]);if(!i)return}if(await c.deleteProject(l),e.json){let i=p.result({success:!0});n.output(i)}else n.success("Project deleted successfully!")}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}P();R();I();v();x();import{Command as An}from"commander";import ot from"cli-table3";function nt(){let s=new An("orgs");return s.description("Manage organizations"),s.command("list").alias("ls").description("List all organizations you belong to").option("-n, --limit <number>","Number of results (default: 20)",parseInt).option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new p({json:t.json,color:r.color}),n=new b(r),{items:c,total:l}=await n.listOrganizations({limit:t.limit||20});if(t.json){let i=p.result({organizations:c,pagination:{total:l,limit:t.limit||20,offset:0,hasMore:c.length<l}});o.output(i)}else{o.heading(`Your Organizations (${c.length} of ${l})`);let i=new ot({head:o.tableHead(["Organization ID","Name","Projects","Created"]),colWidths:[25,30,12,15]});c.forEach(u=>{i.push([u.id,u.name,u.projectCount?.toString()||"0",o.formatShortDate(u.createdAt)])}),console.log(i.toString()),console.log(),o.hints([["View org details","runhuman orgs show <id>"],["Check org balance","runhuman orgs balance <id>"]])}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),s.command("show").alias("info").alias("get").description("Show detailed organization information").argument("<organizationId>","Organization ID to show").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"organization",t),i=await c.getOrganization(l);if(e.json){let u=p.result(i);n.output(u)}else n.heading("Organization Details"),n.detail("Organization ID",i.id),n.detail("Name",i.name),n.detail("Created",n.formatTimestamp(i.createdAt)),console.log(),n.hints([["List projects","runhuman orgs projects "+i.id],["Check balance","runhuman orgs balance "+i.id]])}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("balance").description("Show organization billing balance").argument("<organizationId>","Organization ID").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"organization",t),[i,u]=await Promise.all([c.getOrganization(l),c.getOrganizationBilling(l)]);if(e.json){let d=p.result({balance:u});n.output(d)}else n.heading(`Organization Balance: ${i.name}`),n.detail("Current Balance","$"+u.balanceUsd.toFixed(2)),n.detail("Billing Active",u.hasBilling?"Yes":"No"),n.detail("Estimated Tests","~"+Math.floor(u.balance)+" tests at $1.00 per test"),console.log(),u.hasBilling?n.hints([["Manage billing","https://runhuman.com/dashboard/organizations/"+l+"/billing"]]):n.hints([["Set up billing","https://runhuman.com/dashboard/organizations/"+l+"/billing"]])}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("switch").alias("use").description("Set default organization for CLI commands").argument("<organizationId>","Organization ID to use as default").option("-g, --global","Set as global default instead of local").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let r=new h,o=await r.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"organization",t),i=await c.getOrganization(l);if(await r.set("organization",l,e.global!==!1),e.json){let u=p.result({success:!0,organizationId:l,organizationName:i.name,scope:e.global!==!1?"global":"local"});n.output(u)}else n.success(`Default organization set to: ${i.name} (${l})`),e.global!==!1?console.log(" Scope: global (all directories)"):console.log(" Scope: local (current directory only)"),console.log()}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("projects").description("List all projects in an organization").argument("<organizationId>","Organization ID").option("-n, --limit <number>","Number of results (default: 50)",parseInt).option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"organization",t),[i,{items:u,total:d}]=await Promise.all([c.getOrganization(l),c.listOrganizationProjects(l,{limit:e.limit||50})]);if(e.json){let m=p.result({organizationId:l,projects:u,total:d});n.output(m)}else{if(d===0){n.heading(`Projects in ${i.name} (0 projects)`),console.log(`No projects found in this organization.
191
- `),n.hints([["Create a project","runhuman projects create <name> --organization "+l],["View organization","runhuman orgs show "+l]]);return}n.heading(`Projects in ${i.name} (${u.length} of ${d})`);let m=new ot({head:n.tableHead(["Name","Project ID","Default URL","Created"]),colWidths:[25,25,30,15]});u.forEach(g=>{let j=g.defaultUrl?g.defaultUrl.length>27?g.defaultUrl.substring(0,24)+"...":g.defaultUrl:"-";m.push([g.name,g.id,j,n.formatShortDate(g.createdAt)])}),console.log(m.toString()),console.log(),n.hints([["View project details","runhuman projects show <project-id>"],["Create new project","runhuman projects create <name> --organization "+l],["View organization","runhuman orgs show "+l]])}}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}P();R();I();v();import{Command as Sn}from"commander";import Tn from"inquirer";x();import En from"cli-table3";function st(){let s=new Sn("keys");return s.description("Manage API keys"),s.command("list").alias("ls").description("List all API keys for an organization").option("-o, --organization <id>","Organization ID (required)").option("--show-keys","Show full API keys (default: masked)").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new p({json:t.json,color:r.color}),n=new b(r),c=A("organization",t.organization,r,o),l=await y(n,"organization",c),{items:i,total:u}=await n.listApiKeys(l);if(t.json){let d=p.result({keys:i,total:u});o.output(d)}else{if(u===0){o.heading("API Keys (0)"),console.log(`No API keys found for this organization.
192
- `),o.hints([["Create a key",'runhuman keys create "Key Name" --organization '+l]]);return}o.heading(`API Keys (${i.length} of ${u})`);let d=new En({head:o.tableHead(["Key ID","Name","Key","Last Used","Created"]),colWidths:[20,25,20,20,20]});i.forEach(m=>{let g=t.showKeys?m.key:rt(m.key),j=m.lastUsedAt?o.formatShortDate(m.lastUsedAt):"Never";d.push([m.id,m.name,g,j,o.formatShortDate(m.createdAt)])}),console.log(d.toString()),console.log(),o.hints([["Create new key",'runhuman keys create "Key Name" --organization '+l],["Show full key","runhuman keys show <keyId>"]])}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),s.command("create").alias("new").description("Create a new API key for an organization").argument("<name>","API key name").option("-o, --organization <id>","Organization ID (required)").option("--copy","Copy key to clipboard").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=A("organization",e.organization,o,n),i=await y(c,"organization",l),u=await c.createApiKey(i,t);if(e.json){let d=p.result(u);n.output(d)}else n.success("API Key created successfully!"),n.detail("Key ID",u.id),n.detail("Name",u.name),console.log(),n.detail("API Key",u.key),console.log(" "+"^".repeat(u.key.length)),console.log(` \u26A0\uFE0F Save this key! It won't be shown again.
193
- `),console.log(" Use this key:"),console.log(" export RUNHUMAN_API_KEY="+u.key),console.log(` runhuman create https://myapp.com -d "Test"
188
+ `)}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i}x();R();C();E();import{Command as zn}from"commander";import dt from"inquirer";U();ee();import Mn from"cli-table3";function mt(){let i=new zn("projects");return i.description("Manage projects"),i.command("list").alias("ls").description("List all projects").option("-n, --limit <number>","Number of results (default: 20)",parseInt).option("--offset <number>","Pagination offset (default: 0)",parseInt).option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new p({json:t.json,color:r.color}),n=new b(r),a=t.limit||20,u=t.offset||0,[{items:l,pagination:s},{items:d}]=await Promise.all([n.listProjects({limit:a,offset:u}),n.listOrganizations({})]),m=new Map(d.map(g=>[g.id,g.name]));if(t.json){let g=p.result({projects:l,pagination:s});o.output(g)}else{if(s.total===0){o.heading("Your Projects (0)"),console.log(`No projects found.
189
+ `),o.hints([["Create a project","runhuman projects create <name> -o <org-id>"]]);return}o.heading(`Your Projects (${l.length} of ${s.total})`);let g=new Mn({head:o.tableHead(["Name","Organization","Project ID","Created"]),colWidths:[25,25,25,15]});l.forEach(I=>{let O=m.get(I.organizationId)||"(unknown)";g.push([I.name,O,I.id,o.formatShortDate(I.createdAt)])}),console.log(g.toString());let j=u+1,T=u+l.length;console.log(`
190
+ Showing ${j}-${T} of ${s.total} projects`);let v=[["View project","runhuman projects show <id>"],["Filter by org","runhuman orgs projects <org-id>"]];s.hasMore&&v.push(["Next page",`runhuman projects list --offset ${u+a}`]),console.log(),o.hints(v)}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i.command("create").alias("new").description("Create a new project in an organization").argument("<name>","Project name").option("-o, --organization <id>","Organization ID (required)").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("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let r=new h,o=await r.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=A("organization",e.organization,o,n),l=await y(a,"organization",u),s=await a.createProject({name:t,organizationId:l,defaultUrl:e.defaultUrl,githubRepo:e.githubRepo});if(e.setDefault&&await r.set("project",s.id,!1),e.json){let d=p.result(s);n.output(d)}else n.success("Project created successfully!"),console.log(" Project ID: "+s.id),console.log(" Name: "+s.name),e.setDefault&&console.log(" Set as default project"),console.log()}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("show").alias("info").alias("get").description("Show detailed project information").argument("<projectId>","Project ID to show").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=await re(a,t,{},o,n),l=await a.getProject(u),[s,d]=await Promise.all([a.getOrganization(l.organizationId),a.getOrganizationBilling(l.organizationId)]);if(e.json){let m=p.result({project:l,organization:s,balance:{balance:d.balance,balanceUsd:d.balanceUsd}});n.output(m)}else n.heading("Project Details"),n.detail("Project ID",l.id),n.detail("Name",l.name),n.detail("Organization",s.name+" ("+s.id+")"),n.detail("Org Balance","$"+d.balanceUsd.toFixed(2)),l.defaultUrl&&n.detail("Default URL",l.defaultUrl),l.githubRepo&&n.detail("GitHub Repo",l.githubRepo),n.detail("Created",n.formatTimestamp(l.createdAt)),n.detail("Last Updated",n.formatTimestamp(l.updatedAt)),console.log(),n.hints([["View organization","runhuman orgs show "+s.id],["List org projects","runhuman orgs projects "+s.id],["Check org balance","runhuman orgs balance "+s.id]])}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("switch").alias("use").description("Set default project for CLI commands").argument("<projectId>","Project ID to use as default").option("-g, --global","Set as global default instead of local").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let r=new h,o=await r.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=await re(a,t,{},o,n),l=await a.getProject(u);if(await r.set("project",u,e.global!==!1),e.json){let s=p.result({success:!0,projectId:u,projectName:l.name,scope:e.global!==!1?"global":"local"});n.output(s)}else n.success(`Default project set to: ${l.name} (${u})`),e.global!==!1?console.log(" Scope: global (all directories)"):console.log(" Scope: local (current directory only)"),console.log()}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("update").description("Update project settings").argument("<projectId>","Project ID to update").option("-n, --name <text>","Update name").option("-u, --default-url <url>","Update default URL").option("-g, --github-repo <owner/repo>","Update GitHub repo").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a={name:e.name,defaultUrl:e.defaultUrl,githubRepo:e.githubRepo},u=new b(o),l=await re(u,t,{},o,n),s=await u.updateProject(l,a);if(e.json){let d=p.result(s);n.output(d)}else n.success("Project updated successfully!")}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("transfer").description("Transfer a project to another organization").argument("<projectId>","Project ID to transfer").requiredOption("--to-org <organizationId>","Target organization ID").option("-f, --force","Skip confirmation prompt").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=await re(a,t,{},o,n),l=await y(a,"organization",e.toOrg,{requireFullId:{reason:"transfer target"}}),[s,d]=await Promise.all([a.getProject(u),a.getOrganization(l)]);if(!e.json&&!e.force){let{confirmed:g}=await dt.prompt([{type:"confirm",name:"confirmed",message:`Transfer "${s.name}" to organization "${d.name}"? The current organization will lose access to this project.`,default:!1}]);if(!g)return}let m=await a.initiateTransfer(u,l);e.json?n.output(p.result(m)):m.type==="immediate"?(n.success(`Project transferred to ${d.name}!`),n.detail("Project",m.project.name),n.detail("New Organization",d.name),console.log(),n.hints([["View project","runhuman projects show "+u]])):(n.success("Transfer request sent (pending approval)."),n.detail("Transfer ID",m.transfer.id),n.detail("Project",s.name),n.detail("To Organization",d.name),console.log(),n.hints([["Check status","runhuman transfers list"],["Cancel transfer","runhuman transfers cancel "+m.transfer.id]]))}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("delete").alias("rm").description("Delete a project permanently").argument("<projectId>","Project ID to delete").option("-f, --force","Skip confirmation prompt").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=await y(a,"project",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.force){let{confirmed:l}=await dt.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete project ${u} and all its data? This cannot be undone.`,default:!1}]);if(!l)return}if(await a.deleteProject(u),e.json){let l=p.result({success:!0});n.output(l)}else n.success("Project deleted successfully!")}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}x();R();C();E();U();import{Command as $n}from"commander";import gt from"cli-table3";function ft(){let i=new $n("orgs");return i.description("Manage organizations"),i.command("list").alias("ls").description("List all organizations you belong to").option("-n, --limit <number>","Number of results (default: 20)",parseInt).option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new p({json:t.json,color:r.color}),n=new b(r),{items:a,total:u}=await n.listOrganizations({limit:t.limit||20});if(t.json){let l=p.result({organizations:a,pagination:{total:u,limit:t.limit||20,offset:0,hasMore:a.length<u}});o.output(l)}else{o.heading(`Your Organizations (${a.length} of ${u})`);let l=new gt({head:o.tableHead(["Organization ID","Name","Projects","Created"]),colWidths:[25,30,12,15]});a.forEach(s=>{l.push([s.id,s.name,s.projectCount?.toString()||"0",o.formatShortDate(s.createdAt)])}),console.log(l.toString()),console.log(),o.hints([["View org details","runhuman orgs show <id>"],["Check org balance","runhuman orgs balance <id>"]])}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i.command("show").alias("info").alias("get").description("Show detailed organization information").argument("<organizationId>","Organization ID to show").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=await y(a,"organization",t),l=await a.getOrganization(u);if(e.json){let s=p.result(l);n.output(s)}else n.heading("Organization Details"),n.detail("Organization ID",l.id),n.detail("Name",l.name),n.detail("Created",n.formatTimestamp(l.createdAt)),console.log(),n.hints([["List projects","runhuman orgs projects "+l.id],["Check balance","runhuman orgs balance "+l.id]])}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("balance").description("Show organization billing balance").argument("<organizationId>","Organization ID").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=await y(a,"organization",t),[l,s]=await Promise.all([a.getOrganization(u),a.getOrganizationBilling(u)]);if(e.json){let d=p.result({balance:s});n.output(d)}else n.heading(`Organization Balance: ${l.name}`),n.detail("Current Balance","$"+s.balanceUsd.toFixed(2)),n.detail("Billing Active",s.hasBilling?"Yes":"No"),n.detail("Estimated Tests","~"+Math.floor(s.balance)+" tests at $1.00 per test"),console.log(),s.hasBilling?n.hints([["Manage billing","https://runhuman.com/dashboard/organizations/"+u+"/billing"]]):n.hints([["Set up billing","https://runhuman.com/dashboard/organizations/"+u+"/billing"]])}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("switch").alias("use").description("Set default organization for CLI commands").argument("<organizationId>","Organization ID to use as default").option("-g, --global","Set as global default instead of local").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let r=new h,o=await r.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=await y(a,"organization",t),l=await a.getOrganization(u);if(await r.set("organization",u,e.global!==!1),e.json){let s=p.result({success:!0,organizationId:u,organizationName:l.name,scope:e.global!==!1?"global":"local"});n.output(s)}else n.success(`Default organization set to: ${l.name} (${u})`),e.global!==!1?console.log(" Scope: global (all directories)"):console.log(" Scope: local (current directory only)"),console.log()}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("projects").description("List all projects in an organization").argument("<organizationId>","Organization ID").option("-n, --limit <number>","Number of results (default: 50)",parseInt).option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=await y(a,"organization",t),[l,{items:s,total:d}]=await Promise.all([a.getOrganization(u),a.listOrganizationProjects(u,{limit:e.limit||50})]);if(e.json){let m=p.result({organizationId:u,projects:s,total:d});n.output(m)}else{if(d===0){n.heading(`Projects in ${l.name} (0 projects)`),console.log(`No projects found in this organization.
191
+ `),n.hints([["Create a project","runhuman projects create <name> --organization "+u],["View organization","runhuman orgs show "+u]]);return}n.heading(`Projects in ${l.name} (${s.length} of ${d})`);let m=new gt({head:n.tableHead(["Name","Project ID","Default URL","Created"]),colWidths:[25,25,30,15]});s.forEach(g=>{let j=g.defaultUrl?g.defaultUrl.length>27?g.defaultUrl.substring(0,24)+"...":g.defaultUrl:"-";m.push([g.name,g.id,j,n.formatShortDate(g.createdAt)])}),console.log(m.toString()),console.log(),n.hints([["View project details","runhuman projects show <project-id>"],["Create new project","runhuman projects create <name> --organization "+u],["View organization","runhuman orgs show "+u]])}}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}x();R();C();E();import{Command as Ln}from"commander";import _n from"inquirer";U();import Fn from"cli-table3";function bt(){let i=new Ln("keys");return i.description("Manage API keys"),i.command("list").alias("ls").description("List all API keys for an organization").option("-o, --organization <id>","Organization ID (required)").option("--show-keys","Show full API keys (default: masked)").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new p({json:t.json,color:r.color}),n=new b(r),a=A("organization",t.organization,r,o),u=await y(n,"organization",a),{items:l,total:s}=await n.listApiKeys(u);if(t.json){let d=p.result({keys:l,total:s});o.output(d)}else{if(s===0){o.heading("API Keys (0)"),console.log(`No API keys found for this organization.
192
+ `),o.hints([["Create a key",'runhuman keys create "Key Name" --organization '+u]]);return}o.heading(`API Keys (${l.length} of ${s})`);let d=new Fn({head:o.tableHead(["Key ID","Name","Key","Last Used","Created"]),colWidths:[20,25,20,20,20]});l.forEach(m=>{let g=t.showKeys?m.key:ht(m.key),j=m.lastUsedAt?o.formatShortDate(m.lastUsedAt):"Never";d.push([m.id,m.name,g,j,o.formatShortDate(m.createdAt)])}),console.log(d.toString()),console.log(),o.hints([["Create new key",'runhuman keys create "Key Name" --organization '+u],["Show full key","runhuman keys show <keyId>"]])}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i.command("create").alias("new").description("Create a new API key for an organization").argument("<name>","API key name").option("-o, --organization <id>","Organization ID (required)").option("--copy","Copy key to clipboard").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=A("organization",e.organization,o,n),l=await y(a,"organization",u),s=await a.createApiKey(l,t);if(e.json){let d=p.result(s);n.output(d)}else n.success("API Key created successfully!"),n.detail("Key ID",s.id),n.detail("Name",s.name),console.log(),n.detail("API Key",s.key),console.log(" "+"^".repeat(s.key.length)),console.log(` \u26A0\uFE0F Save this key! It won't be shown again.
193
+ `),console.log(" Use this key:"),console.log(" export RUNHUMAN_API_KEY="+s.key),console.log(` runhuman create https://myapp.com -d "Test"
194
194
  `),console.log(" Store securely:"),console.log(" - Use environment variables (recommended)"),console.log(" - Use secret management tools"),console.log(` - Never commit to git!
195
- `)}catch(r){let o=f(r);new p({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("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"key",t),i=await c.getApiKey(l);if(e.json){let u=p.result(i);n.output(u)}else n.heading("API Key Details"),n.detail("Key ID",i.id),n.detail("Name",i.name),n.detail("API Key",e.showKey?i.key:rt(i.key)),n.detail("Created",n.formatTimestamp(i.createdAt)),i.lastUsedAt&&n.detail("Last Used",n.formatTimestamp(i.lastUsedAt)),console.log()}catch(r){let o=f(r);new p({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("-f, --force","Skip confirmation prompt").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"key",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.force){let{confirmed:i}=await Tn.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete API key ${l}? This cannot be undone.`,default:!1}]);if(!i)return}if(await c.deleteApiKey(l),e.json){let i=p.result({success:!0});n.output(i)}else n.success("API key deleted successfully!")}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}function rt(s){return s.length<=12?"****":s.substring(0,8)+"*".repeat(s.length-12)+s.substring(s.length-4)}P();R();I();v();import{Command as vn}from"commander";import Rn from"inquirer";x();import kn from"cli-table3";function it(){let s=new vn("templates");return s.description("Manage test templates"),s.command("list").alias("ls").description("List all test templates").option("-p, --project <id>","Filter by project (required)").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new p({json:t.json,color:r.color}),n=new b(r),c=A("project",t.project,r,o),l=await y(n,"project",c),{items:i,pagination:u}=await n.listTemplates(l);if(t.json){let d=p.result({templates:i,pagination:u});o.output(d)}else{if(u.total===0){o.heading("Test Templates (0)"),console.log(`No templates found for this project.
196
- `),o.hints([["Create a template",'runhuman templates create "Template Name" --project '+l]]);return}o.heading(`Test Templates (${i.length} of ${u.total})`);let d=new kn({head:o.tableHead(["ID","Name","Description","Created"]),colWidths:[30,25,35,20]});i.forEach(m=>{let g=m.testDescription&&m.testDescription.length>30?m.testDescription.substring(0,27)+"...":m.testDescription||"-";d.push([m.id,m.name,g,o.formatShortDate(m.createdAt)])}),console.log(d.toString()),console.log(),o.hints([["Create new template",'runhuman templates create "Template Name" --project '+l],["View template","runhuman templates show <templateId>"]])}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),s.command("create").alias("new").description("Create a new test template").argument("<name>","Template name").option("-p, --project <id>","Project ID (required)").option("-d, --description <text>","Template description").option("--duration <seconds>","Target test duration in seconds").option("--device-class <class>","Device class (desktop/mobile)").option("-S, --schema <path>","Path to JSON schema file").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=A("project",e.project,o,n),i=await y(c,"project",l),u;if(e.schema){let{readFileSync:g}=await import("fs"),j=g(e.schema,"utf-8");u=JSON.parse(j)}let d={name:t,testDescription:e.description,targetDurationMinutes:e.duration?Math.floor(parseInt(e.duration)/60):void 0,deviceClass:e.deviceClass,outputSchema:u},m=await c.createTemplate(i,d);if(e.json){let g=p.result(m);n.output(g)}else n.success("Template created successfully!"),n.detail("Template ID",m.id),n.detail("Name",m.name),m.testDescription&&n.detail("Description",m.testDescription),console.log(),n.hints([["Use this template","runhuman create https://myapp.com --template "+m.id]])}catch(r){let o=f(r);new p({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("-p, --project <id>","Project ID (required)").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=A("project",e.project,o,n),i=await y(c,"project",l),u=await y(c,"template",t),d=await c.getTemplate(i,u);if(e.json){let m=p.result(d);n.output(m)}else n.heading("Template Details"),n.detail("Template ID",d.id),n.detail("Name",d.name),n.detail("Description",d.testDescription||"-"),n.detail("Project",d.projectId),d.targetDurationMinutes&&n.detail("Duration",d.targetDurationMinutes+" minutes"),d.deviceClass&&n.detail("Device Class",d.deviceClass),n.detail("Created",n.formatTimestamp(d.createdAt)),d.outputSchema&&(console.log(`
197
- Output Schema:`),console.log(JSON.stringify(d.outputSchema,null,2))),console.log(),n.hints([["Use this template","runhuman create https://myapp.com --template "+d.id]])}catch(r){let o=f(r);new p({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("-p, --project <id>","Project ID (required)").option("-n, --name <name>","New template name").option("-d, --description <text>","New description").option("--duration <seconds>","New target duration in seconds").option("--device-class <class>","New device class (desktop/mobile)").option("-S, --schema <path>","Path to new JSON schema file").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=A("project",e.project,o,n),i=await y(c,"project",l),u=await y(c,"template",t),d;if(e.schema){let{readFileSync:T}=await import("fs"),E=T(e.schema,"utf-8");d=JSON.parse(E)}let m={name:e.name,testDescription:e.description,targetDurationMinutes:e.duration?Math.floor(parseInt(e.duration)/60):void 0,deviceClass:e.deviceClass,outputSchema:d},g=Object.fromEntries(Object.entries(m).filter(([,T])=>T!==void 0));if(Object.keys(g).length===0)throw new Error("No updates provided. Use --name, --description, --duration, --device-class, or --schema");let j=await c.updateTemplate(i,u,m);if(e.json){let T=p.result(j);n.output(T)}else n.success("Template updated successfully!"),n.detail("Template ID",j.id),n.detail("Name",j.name),console.log()}catch(r){let o=f(r);new p({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("-p, --project <id>","Project ID (required)").option("-f, --force","Skip confirmation prompt").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=A("project",e.project,o,n),i=await y(c,"project",l),u=await y(c,"template",t);if(!e.json&&!e.force){let{confirmed:d}=await Rn.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete template ${u}? This cannot be undone.`,default:!1}]);if(!d)return}if(await c.deleteTemplate(i,u),e.json){let d=p.result({success:!0});n.output(d)}else n.success("Template deleted successfully!")}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}P();R();I();v();import{Command as Pn}from"commander";x();import at from"cli-table3";function ct(){let s=new Pn("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("-p, --project <id>","Project ID (required)").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let r=new h,o=await r.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=A("project",e.project,o,n),i=await y(c,"project",l);if(!t.match(/^([^/]+)\/([^/]+)$/))throw new Error("Invalid repository format. Use: owner/repo");if(await c.updateProject(i,{githubRepo:t}),e.json){let d=p.result({success:!0,repository:t});n.output(d)}else n.success("GitHub repository linked successfully!"),n.detail("Repository",t),n.detail("Project",i),console.log(),await r.saveProjectConfig({githubRepo:t}),console.log(` Repository saved to project config (.runhumanrc)
198
- `),n.hints([["List issues","runhuman github issues "+t],["Test an issue","runhuman github test <issueNumber> --repo "+t],["Bulk test","runhuman github bulk-test --repo "+t]])}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("repos").alias("repositories").description("List GitHub repositories accessible to an organization").option("-o, --organization <id>","Organization ID (required)").option("-s, --search <query>","Filter by repository name").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new p({json:t.json,color:r.color}),n=new b(r),c=A("organization",t.organization,r,o),l=await y(n,"organization",c),i=await n.listGithubRepos(l,{search:t.search});if(t.json)o.output(p.result(i));else{o.heading(`GitHub Repositories (${i.items.length})`);let u=new at({head:o.tableHead(["Repository","Added"]),colWidths:[45,20]});i.items.forEach(d=>{u.push([d.fullName,o.formatShortDate(d.createdAt)])}),console.log(u.toString()),console.log(),o.hints([["Test an issue","runhuman github test <issueNumber> --repo owner/repo"]])}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),s.command("issues").description("List GitHub issues for a repository").argument("<repo>","Repository in format owner/repo").option("-s, --state <state>","Filter by state (open/closed/all)","open").option("-l, --labels <labels>","Filter by comma-separated labels").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=t.match(/^([^/]+)\/([^/]+)$/);if(!c)throw new Error("Invalid repository format. Use: owner/repo");let[,l,i]=c,d=await new b(o).listGithubIssues(l,i,{state:e.state,labels:e.labels?.split(",")});if(e.json){let m=p.result({issues:d});n.output(m)}else{n.heading(`GitHub Issues for ${t} (${d.length})`);let m=new at({head:n.tableHead(["#","Title","State","Labels","Created"]),colWidths:[8,40,10,20,15]});d.forEach(g=>{let j=g.labels?.join(", ")||"-",T=j.length>18?j.substring(0,15)+"...":j;m.push(["#"+g.number,g.title.length>38?g.title.substring(0,35)+"...":g.title,g.state,T,n.formatShortDate(g.createdAt)])}),console.log(m.toString()),console.log(),n.hints([["Test an issue","runhuman github test <issueNumber> --repo "+t]])}}catch(r){let o=f(r);new p({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("-r, --repo <owner/repo>","Repository (or use default from config)").option("-u, --url <url>","URL to test (required)").option("-t, --template <id>","Template ID to use").option("-s, --sync","Wait for result before exiting").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=e.repo||o.githubRepo;if(!n)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 c=new p({json:e.json,color:o.color}),l=n.match(/^([^/]+)\/([^/]+)$/);if(!l)throw new Error("Invalid repository format. Use: owner/repo");let[,i,u]=l,d=new b(o),m=await d.getGithubIssue(i,u,parseInt(t)),g=A("project",void 0,o,c),T={projectId:await y(d,"project",g),url:e.url,description:`Test GitHub issue #${t}: ${m.title}
195
+ `)}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.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("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=await y(a,"key",t),l=await a.getApiKey(u);if(e.json){let s=p.result(l);n.output(s)}else n.heading("API Key Details"),n.detail("Key ID",l.id),n.detail("Name",l.name),n.detail("API Key",e.showKey?l.key:ht(l.key)),n.detail("Created",n.formatTimestamp(l.createdAt)),l.lastUsedAt&&n.detail("Last Used",n.formatTimestamp(l.lastUsedAt)),console.log()}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("delete").aliases(["rm","revoke"]).description("Delete an API key permanently").argument("<keyId>","Key ID to delete").option("-f, --force","Skip confirmation prompt").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=await y(a,"key",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.force){let{confirmed:l}=await _n.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete API key ${u}? This cannot be undone.`,default:!1}]);if(!l)return}if(await a.deleteApiKey(u),e.json){let l=p.result({success:!0});n.output(l)}else n.success("API key deleted successfully!")}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}function ht(i){return i.length<=12?"****":i.substring(0,8)+"*".repeat(i.length-12)+i.substring(i.length-4)}x();R();C();E();import{Command as Jn}from"commander";import Kn from"inquirer";U();import qn from"cli-table3";function yt(){let i=new Jn("templates");return i.description("Manage test templates"),i.command("list").alias("ls").description("List all test templates").option("-p, --project <id>","Filter by project (required)").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new p({json:t.json,color:r.color}),n=new b(r),a=A("project",t.project,r,o),u=await y(n,"project",a),{items:l,pagination:s}=await n.listTemplates(u);if(t.json){let d=p.result({templates:l,pagination:s});o.output(d)}else{if(s.total===0){o.heading("Test Templates (0)"),console.log(`No templates found for this project.
196
+ `),o.hints([["Create a template",'runhuman templates create "Template Name" --project '+u]]);return}o.heading(`Test Templates (${l.length} of ${s.total})`);let d=new qn({head:o.tableHead(["ID","Name","Description","Created"]),colWidths:[30,25,35,20]});l.forEach(m=>{let g=m.testDescription&&m.testDescription.length>30?m.testDescription.substring(0,27)+"...":m.testDescription||"-";d.push([m.id,m.name,g,o.formatShortDate(m.createdAt)])}),console.log(d.toString()),console.log(),o.hints([["Create new template",'runhuman templates create "Template Name" --project '+u],["View template","runhuman templates show <templateId>"]])}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i.command("create").alias("new").description("Create a new test template").argument("<name>","Template name").option("-p, --project <id>","Project ID (required)").option("-d, --description <text>","Template description").option("--duration <seconds>","Target test duration in seconds").option("--device-class <class>","Device class (desktop/mobile)").option("-S, --schema <path>","Path to JSON schema file").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=A("project",e.project,o,n),l=await y(a,"project",u),s;if(e.schema){let{readFileSync:g}=await import("fs"),j=g(e.schema,"utf-8");s=JSON.parse(j)}let d={name:t,testDescription:e.description,targetDurationMinutes:e.duration?Math.floor(parseInt(e.duration)/60):void 0,deviceClass:e.deviceClass,outputSchema:s},m=await a.createTemplate(l,d);if(e.json){let g=p.result(m);n.output(g)}else n.success("Template created successfully!"),n.detail("Template ID",m.id),n.detail("Name",m.name),m.testDescription&&n.detail("Description",m.testDescription),console.log(),n.hints([["Use this template","runhuman create https://myapp.com --template "+m.id]])}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("show").alias("info").alias("get").description("Show details of a specific template").argument("<templateId>","Template ID to show").option("-p, --project <id>","Project ID (required)").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=A("project",e.project,o,n),l=await y(a,"project",u),s=await y(a,"template",t),d=await a.getTemplate(l,s);if(e.json){let m=p.result(d);n.output(m)}else n.heading("Template Details"),n.detail("Template ID",d.id),n.detail("Name",d.name),n.detail("Description",d.testDescription||"-"),n.detail("Project",d.projectId),d.targetDurationMinutes&&n.detail("Duration",d.targetDurationMinutes+" minutes"),d.deviceClass&&n.detail("Device Class",d.deviceClass),n.detail("Created",n.formatTimestamp(d.createdAt)),d.outputSchema&&(console.log(`
197
+ Output Schema:`),console.log(JSON.stringify(d.outputSchema,null,2))),console.log(),n.hints([["Use this template","runhuman create https://myapp.com --template "+d.id]])}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("update").alias("edit").description("Update a template").argument("<templateId>","Template ID to update").option("-p, --project <id>","Project ID (required)").option("-n, --name <name>","New template name").option("-d, --description <text>","New description").option("--duration <seconds>","New target duration in seconds").option("--device-class <class>","New device class (desktop/mobile)").option("-S, --schema <path>","Path to new JSON schema file").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=A("project",e.project,o,n),l=await y(a,"project",u),s=await y(a,"template",t),d;if(e.schema){let{readFileSync:T}=await import("fs"),v=T(e.schema,"utf-8");d=JSON.parse(v)}let m={name:e.name,testDescription:e.description,targetDurationMinutes:e.duration?Math.floor(parseInt(e.duration)/60):void 0,deviceClass:e.deviceClass,outputSchema:d},g=Object.fromEntries(Object.entries(m).filter(([,T])=>T!==void 0));if(Object.keys(g).length===0)throw new Error("No updates provided. Use --name, --description, --duration, --device-class, or --schema");let j=await a.updateTemplate(l,s,m);if(e.json){let T=p.result(j);n.output(T)}else n.success("Template updated successfully!"),n.detail("Template ID",j.id),n.detail("Name",j.name),console.log()}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("delete").alias("rm").description("Delete a template permanently").argument("<templateId>","Template ID to delete").option("-p, --project <id>","Project ID (required)").option("-f, --force","Skip confirmation prompt").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=A("project",e.project,o,n),l=await y(a,"project",u),s=await y(a,"template",t);if(!e.json&&!e.force){let{confirmed:d}=await Kn.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete template ${s}? This cannot be undone.`,default:!1}]);if(!d)return}if(await a.deleteTemplate(l,s),e.json){let d=p.result({success:!0});n.output(d)}else n.success("Template deleted successfully!")}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}x();R();C();E();import{Command as Hn}from"commander";U();import jt from"cli-table3";function wt(){let i=new Hn("github");return i.alias("gh"),i.description("GitHub integration commands"),i.command("link").description("Link a GitHub repository to your project").argument("<repo>","Repository in format owner/repo").option("-p, --project <id>","Project ID (required)").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let r=new h,o=await r.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=A("project",e.project,o,n),l=await y(a,"project",u);if(!t.match(/^([^/]+)\/([^/]+)$/))throw new Error("Invalid repository format. Use: owner/repo");if(await a.updateProject(l,{githubRepo:t}),e.json){let d=p.result({success:!0,repository:t});n.output(d)}else n.success("GitHub repository linked successfully!"),n.detail("Repository",t),n.detail("Project",l),console.log(),await r.saveProjectConfig({githubRepo:t}),console.log(` Repository saved to project config (.runhumanrc)
198
+ `),n.hints([["List issues","runhuman github issues "+t],["Test an issue","runhuman github test <issueNumber> --repo "+t],["Bulk test","runhuman github bulk-test --repo "+t]])}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("repos").alias("repositories").description("List GitHub repositories accessible to an organization").option("-o, --organization <id>","Organization ID (required)").option("-s, --search <query>","Filter by repository name").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new p({json:t.json,color:r.color}),n=new b(r),a=A("organization",t.organization,r,o),u=await y(n,"organization",a),l=await n.listGithubRepos(u,{search:t.search});if(t.json)o.output(p.result(l));else{o.heading(`GitHub Repositories (${l.items.length})`);let s=new jt({head:o.tableHead(["Repository","Added"]),colWidths:[45,20]});l.items.forEach(d=>{s.push([d.fullName,o.formatShortDate(d.createdAt)])}),console.log(s.toString()),console.log(),o.hints([["Test an issue","runhuman github test <issueNumber> --repo owner/repo"]])}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i.command("issues").description("List GitHub issues for a repository").argument("<repo>","Repository in format owner/repo").option("-s, --state <state>","Filter by state (open/closed/all)","open").option("-l, --labels <labels>","Filter by comma-separated labels").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=t.match(/^([^/]+)\/([^/]+)$/);if(!a)throw new Error("Invalid repository format. Use: owner/repo");let[,u,l]=a,d=await new b(o).listGithubIssues(u,l,{state:e.state,labels:e.labels?.split(",")});if(e.json){let m=p.result({issues:d});n.output(m)}else{n.heading(`GitHub Issues for ${t} (${d.length})`);let m=new jt({head:n.tableHead(["#","Title","State","Labels","Created"]),colWidths:[8,40,10,20,15]});d.forEach(g=>{let j=g.labels?.join(", ")||"-",T=j.length>18?j.substring(0,15)+"...":j;m.push(["#"+g.number,g.title.length>38?g.title.substring(0,35)+"...":g.title,g.state,T,n.formatShortDate(g.createdAt)])}),console.log(m.toString()),console.log(),n.hints([["Test an issue","runhuman github test <issueNumber> --repo "+t]])}}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("test").description("Create a QA test job for a GitHub issue").argument("<issueNumber>","Issue number to test").option("-r, --repo <owner/repo>","Repository (or use default from config)").option("-u, --url <url>","URL to test (required)").option("-t, --template <id>","Template ID to use").option("-s, --sync","Wait for result before exiting").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=e.repo||o.githubRepo;if(!n)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 a=new p({json:e.json,color:o.color}),u=n.match(/^([^/]+)\/([^/]+)$/);if(!u)throw new Error("Invalid repository format. Use: owner/repo");let[,l,s]=u,d=new b(o),m=await d.getGithubIssue(l,s,parseInt(t)),g=A("project",void 0,o,a),T={projectId:await y(d,"project",g),url:e.url,description:`Test GitHub issue #${t}: ${m.title}
199
199
 
200
- ${m.body}`,metadata:{githubIssue:{owner:i,repo:u,number:parseInt(t),url:m.url}},templateId:e.template},E=await d.createJob(T);if(e.json){let C=p.result(E);c.output(C)}else if(c.success("QA test job created for issue #"+t),c.detail("Job ID",E.jobId),c.detail("Issue","#"+t+" - "+m.title),c.detail("Status",E.status),c.detail("URL",e.url),console.log(),c.hints([["Check status","runhuman status "+E.jobId]]),e.sync){let{waitForJob:C}=await Promise.resolve().then(()=>(ue(),we));await C(E.jobId,d,c,600)}}catch(r){let o=f(r);new p({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("-r, --repo <owner/repo>","Repository (or use default from config)").option("-u, --url <url>","URL to test (required)").option("-l, --labels <labels>","Filter issues by comma-separated labels").option("-s, --state <state>","Filter by state (open/closed/all)","open").option("-t, --template <id>","Template ID to use").option("-n, --limit <number>","Maximum number of jobs to create","10").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=t.repo||r.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 n=new p({json:t.json,color:r.color}),c=o.match(/^([^/]+)\/([^/]+)$/);if(!c)throw new Error("Invalid repository format. Use: owner/repo");let[,l,i]=c,u=new b(r),d=await u.listGithubIssues(l,i,{state:t.state,labels:t.labels?.split(",")}),m=parseInt(t.limit),g=d.slice(0,m);if(g.length===0){console.log(`No issues found matching the criteria.
201
- `);return}let j=A("project",void 0,r,n),T=await y(u,"project",j);console.log(`
200
+ ${m.body}`,metadata:{githubIssue:{owner:l,repo:s,number:parseInt(t),url:m.url}},templateId:e.template},v=await d.createJob(T);if(e.json){let I=p.result(v);a.output(I)}else if(a.success("QA test job created for issue #"+t),a.detail("Job ID",v.jobId),a.detail("Issue","#"+t+" - "+m.title),a.detail("Status",v.status),a.detail("URL",e.url),console.log(),a.hints([["Check status","runhuman status "+v.jobId]]),e.sync){let{waitForJob:I}=await Promise.resolve().then(()=>(fe(),ve));await I(v.jobId,d,a,600)}}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("bulk-test").description("Create QA test jobs for multiple GitHub issues").option("-r, --repo <owner/repo>","Repository (or use default from config)").option("-u, --url <url>","URL to test (required)").option("-l, --labels <labels>","Filter issues by comma-separated labels").option("-s, --state <state>","Filter by state (open/closed/all)","open").option("-t, --template <id>","Template ID to use").option("-n, --limit <number>","Maximum number of jobs to create","10").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=t.repo||r.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 n=new p({json:t.json,color:r.color}),a=o.match(/^([^/]+)\/([^/]+)$/);if(!a)throw new Error("Invalid repository format. Use: owner/repo");let[,u,l]=a,s=new b(r),d=await s.listGithubIssues(u,l,{state:t.state,labels:t.labels?.split(",")}),m=parseInt(t.limit),g=d.slice(0,m);if(g.length===0){console.log(`No issues found matching the criteria.
201
+ `);return}let j=A("project",void 0,r,n),T=await y(s,"project",j);console.log(`
202
202
  Creating ${g.length} test jobs...
203
- `);let E=[];for(let C of g){let O={projectId:T,url:t.url,description:`Test GitHub issue #${C.number}: ${C.title}
203
+ `);let v=[];for(let I of g){let O={projectId:T,url:t.url,description:`Test GitHub issue #${I.number}: ${I.title}
204
204
 
205
- ${C.body}`,metadata:{githubIssue:{owner:l,repo:i,number:C.number,url:C.url}},templateId:t.template};try{let N=await u.createJob(O);E.push({issue:C.number,jobId:N.jobId,status:"created"}),console.log(` ok Issue #${C.number} \u2192 Job ${N.jobId}`)}catch(N){let W=f(N);E.push({issue:C.number,error:W.message,status:"failed"}),console.log(` FAIL Issue #${C.number} \u2192 Failed: ${W.message}`)}}if(t.json){let C=p.result({jobs:E});n.output(C)}else{let C=E.filter(N=>N.status==="created").length,O=E.filter(N=>N.status==="failed").length;console.log(`
206
- Created ${C} jobs`),O>0&&console.log(`Failed ${O} jobs`),console.log(),n.hints([["Check all jobs","runhuman list"]])}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),s}P();R();I();v();x();import{Command as Un}from"commander";import lt from"cli-table3";function ut(){let s=new Un("transfers");return s.description("Manage project transfers between organizations"),s.command("list").alias("ls").description("List pending incoming and outgoing transfers").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new p({json:t.json,color:r.color}),n=new b(r),[c,l]=await Promise.all([n.listPendingTransfers(),n.listOutgoingTransfers()]);if(t.json){let i=p.result({incoming:c.items,outgoing:l.items});o.output(i);return}if(o.heading(`Incoming Transfers (${c.total})`),c.items.length===0)console.log(`No pending incoming transfers.
207
- `);else{let i=new lt({head:o.tableHead(["Transfer ID","Project","From","To Org","Created"]),colWidths:[20,25,25,25,15]});c.items.forEach(u=>{i.push([u.id.substring(0,18),u.projectName,u.initiatedByEmail??"Unknown",u.toOrganizationName??"-",o.formatShortDate(u.createdAt)])}),console.log(i.toString()),console.log()}if(o.heading(`Outgoing Transfers (${l.total})`),l.items.length===0)console.log(`No pending outgoing transfers.
208
- `);else{let i=new lt({head:o.tableHead(["Transfer ID","Project","To Org","Status","Created"]),colWidths:[20,25,25,15,15]});l.items.forEach(u=>{i.push([u.id.substring(0,18),u.projectName,u.toOrganizationName??"-",u.status,o.formatShortDate(u.createdAt)])}),console.log(i.toString()),console.log()}o.hints([["Accept transfer","runhuman transfers accept <transferId>"],["Reject transfer","runhuman transfers reject <transferId>"],["Cancel transfer","runhuman transfers cancel <transferId>"]])}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),s.command("accept").description("Accept a pending transfer").argument("<transferId>","Transfer ID to accept").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"transfer",t);await c.acceptTransfer(l),e.json?n.output(p.result({success:!0,transferId:l})):(n.success("Transfer accepted!"),n.detail("Transfer ID",l),console.log())}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("reject").description("Reject a pending transfer").argument("<transferId>","Transfer ID to reject").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"transfer",t);await c.rejectTransfer(l),e.json?n.output(p.result({success:!0,transferId:l})):(n.success("Transfer rejected."),n.detail("Transfer ID",l),console.log())}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s.command("cancel").description("Cancel a transfer you initiated").argument("<transferId>","Transfer ID to cancel").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),c=new b(o),l=await y(c,"transfer",t);await c.cancelTransfer(l),e.json?n.output(p.result({success:!0,transferId:l})):(n.success("Transfer cancelled."),n.detail("Transfer ID",l),console.log())}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),s}R();P();import{existsSync as xn}from"fs";import{join as On}from"path";import{execSync as Dn}from"child_process";import G from"inquirer";import U from"chalk";import dt from"ora";v();async function mt(){let s=new h,t=new p({color:!0});console.log(""),console.log(U.bold("Runhuman")+" - Human QA testing for your apps"),console.log("");let e=await pt(s);if(!e.isAuthenticated){let{shouldLogin:r}=await G.prompt([{type:"confirm",name:"shouldLogin",message:"You're not logged in. Would you like to sign in?",default:!0}]);if(!r){console.log(`
209
- Run `+U.cyan("runhuman login")+` when you're ready to sign in.
210
- `);return}await Nn(s,t),Object.assign(e,await pt(s))}console.log(U.green("Logged in as "+e.userEmail)+`
211
- `),e.isRunhumanProject?await $n(s,e):await Mn(s,e)}async function pt(s){let t={isAuthenticated:!1,isGitRepo:!1,isRunhumanProject:!1},e=s.loadCredentials();if(e?.accessToken)try{let o=await s.loadConfig({apiKey:e.accessToken}),c=await new b(o).getCurrentUser();t.isAuthenticated=!0,t.userEmail=c.email}catch{t.isAuthenticated=!1}try{Dn("git rev-parse --is-inside-work-tree",{stdio:"ignore"}),t.isGitRepo=!0}catch{t.isGitRepo=!1}let r=On(process.cwd(),".runhumanrc");if(xn(r)){t.isRunhumanProject=!0;try{let o=await s.loadConfig();t.projectConfig={defaultUrl:o.defaultUrl,githubRepo:o.githubRepo}}catch{}}return t}async function Nn(s,t){let e=await s.loadConfig(),r=e.apiUrl||"https://runhuman.com";console.log("");let o=dt("Opening browser for authentication...").start();try{let n=await de({apiUrl:r,autoOpenBrowser:e.autoOpenBrowser!==!1});o.stop(),s.saveCredentials({accessToken:n.token});let c=await s.loadConfig({apiKey:n.token}),i=await new b(c).getCurrentUser();s.saveUserInfo(i),t.success("Successfully logged in!"),console.log("")}catch(n){throw o.stop(),n}}async function $n(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 G.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 G.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await gt(s,t);break;case"run-template":console.log(`
212
- Run `+U.cyan("runhuman templates")+" to see available templates."),console.log("Then run "+U.cyan("runhuman create --template <name>")+` to use one.
205
+ ${I.body}`,metadata:{githubIssue:{owner:u,repo:l,number:I.number,url:I.url}},templateId:t.template};try{let N=await s.createJob(O);v.push({issue:I.number,jobId:N.jobId,status:"created"}),console.log(` ok Issue #${I.number} \u2192 Job ${N.jobId}`)}catch(N){let Y=f(N);v.push({issue:I.number,error:Y.message,status:"failed"}),console.log(` FAIL Issue #${I.number} \u2192 Failed: ${Y.message}`)}}if(t.json){let I=p.result({jobs:v});n.output(I)}else{let I=v.filter(N=>N.status==="created").length,O=v.filter(N=>N.status==="failed").length;console.log(`
206
+ Created ${I} jobs`),O>0&&console.log(`Failed ${O} jobs`),console.log(),n.hints([["Check all jobs","runhuman list"]])}}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i}x();R();C();E();U();import{Command as Vn}from"commander";import It from"cli-table3";function Ct(){let i=new Vn("transfers");return i.description("Manage project transfers between organizations"),i.command("list").alias("ls").description("List pending incoming and outgoing transfers").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new p({json:t.json,color:r.color}),n=new b(r),[a,u]=await Promise.all([n.listPendingTransfers(),n.listOutgoingTransfers()]);if(t.json){let l=p.result({incoming:a.items,outgoing:u.items});o.output(l);return}if(o.heading(`Incoming Transfers (${a.total})`),a.items.length===0)console.log(`No pending incoming transfers.
207
+ `);else{let l=new It({head:o.tableHead(["Transfer ID","Project","From","To Org","Created"]),colWidths:[20,25,25,25,15]});a.items.forEach(s=>{l.push([s.id.substring(0,18),s.projectName,s.initiatedByEmail??"Unknown",s.toOrganizationName??"-",o.formatShortDate(s.createdAt)])}),console.log(l.toString()),console.log()}if(o.heading(`Outgoing Transfers (${u.total})`),u.items.length===0)console.log(`No pending outgoing transfers.
208
+ `);else{let l=new It({head:o.tableHead(["Transfer ID","Project","To Org","Status","Created"]),colWidths:[20,25,25,15,15]});u.items.forEach(s=>{l.push([s.id.substring(0,18),s.projectName,s.toOrganizationName??"-",s.status,o.formatShortDate(s.createdAt)])}),console.log(l.toString()),console.log()}o.hints([["Accept transfer","runhuman transfers accept <transferId>"],["Reject transfer","runhuman transfers reject <transferId>"],["Cancel transfer","runhuman transfers cancel <transferId>"]])}catch(e){let r=f(e);new p({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i.command("accept").description("Accept a pending transfer").argument("<transferId>","Transfer ID to accept").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=await y(a,"transfer",t);await a.acceptTransfer(u),e.json?n.output(p.result({success:!0,transferId:u})):(n.success("Transfer accepted!"),n.detail("Transfer ID",u),console.log())}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("reject").description("Reject a pending transfer").argument("<transferId>","Transfer ID to reject").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=await y(a,"transfer",t);await a.rejectTransfer(u),e.json?n.output(p.result({success:!0,transferId:u})):(n.success("Transfer rejected."),n.detail("Transfer ID",u),console.log())}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("cancel").description("Cancel a transfer you initiated").argument("<transferId>","Transfer ID to cancel").option("-j, --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 h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),n=new p({json:e.json,color:o.color}),a=new b(o),u=await y(a,"transfer",t);await a.cancelTransfer(u),e.json?n.output(p.result({success:!0,transferId:u})):(n.success("Transfer cancelled."),n.detail("Transfer ID",u),console.log())}catch(r){let o=f(r);new p({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}R();x();import{existsSync as Gn}from"fs";import{join as Wn}from"path";import{execSync as Bn}from"child_process";import B from"inquirer";import k from"chalk";import St from"ora";E();async function Tt(){let i=new h,t=new p({color:!0});console.log(""),console.log(k.bold("Runhuman")+" - Human QA testing for your apps"),console.log("");let e=await At(i);if(!e.isAuthenticated){let{shouldLogin:r}=await B.prompt([{type:"confirm",name:"shouldLogin",message:"You're not logged in. Would you like to sign in?",default:!0}]);if(!r){console.log(`
209
+ Run `+k.cyan("runhuman login")+` when you're ready to sign in.
210
+ `);return}await Yn(i,t),Object.assign(e,await At(i))}console.log(k.green("Logged in as "+e.userEmail)+`
211
+ `),e.isRunhumanProject?await Zn(i,e):await Qn(i,e)}async function At(i){let t={isAuthenticated:!1,isGitRepo:!1,isRunhumanProject:!1},e=i.loadCredentials();if(e?.accessToken)try{let o=await i.loadConfig({apiKey:e.accessToken}),a=await new b(o).getCurrentUser();t.isAuthenticated=!0,t.userEmail=a.email}catch{t.isAuthenticated=!1}try{Bn("git rev-parse --is-inside-work-tree",{stdio:"ignore"}),t.isGitRepo=!0}catch{t.isGitRepo=!1}let r=Wn(process.cwd(),".runhumanrc");if(Gn(r)){t.isRunhumanProject=!0;try{let o=await i.loadConfig();t.projectConfig={defaultUrl:o.defaultUrl,githubRepo:o.githubRepo}}catch{}}return t}async function Yn(i,t){let e=await i.loadConfig(),r=e.apiUrl||"https://runhuman.com";console.log("");let o=St("Opening browser for authentication...").start();try{let n=await be({apiUrl:r,autoOpenBrowser:e.autoOpenBrowser!==!1});o.stop(),i.saveCredentials({accessToken:n.token});let a=await i.loadConfig({apiKey:n.token}),l=await new b(a).getCurrentUser();i.saveUserInfo(l),t.success("Successfully logged in!"),console.log("")}catch(n){throw o.stop(),n}}async function Zn(i,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 B.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 B.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await vt(i,t);break;case"run-template":console.log(`
212
+ Run `+k.cyan("runhuman templates")+" to see available templates."),console.log("Then run "+k.cyan("runhuman create --template <name>")+` to use one.
213
213
  `);break;case"list-jobs":console.log(`
214
- Run `+U.cyan("runhuman list")+` to see your recent jobs.
215
- `);break;case"exit":break}}async function Mn(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 G.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 G.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await gt(s,t);break;case"setup-repo":await zn(s);break;case"connect-github":await Ln(s);break;case"exit":break}}async function gt(s,t){let e=t.projectConfig?.defaultUrl||"",r=await G.prompt([{type:"input",name:"url",message:"URL to test:",default:e||void 0,validate:n=>{if(!n.trim())return"URL is required";try{return new URL(n),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"description",message:"What should we test? (describe in plain English):",validate:n=>n.trim()?!0:"Description is required"}]),o=dt("Creating test job...").start();try{let n=s.loadCredentials(),c=await s.loadConfig({apiKey:n?.accessToken});if(!c.project){o.fail("No project selected"),console.log(`
216
- Run: runhuman projects switch <project-id>`);return}let i=await new b(c).createJob({projectId:c.project,url:r.url,description:r.description});o.succeed("Test job created!"),console.log(""),console.log(" Job ID: "+U.cyan(i.jobId)),console.log(" Status: "+i.status),console.log(""),console.log("A human tester will test your app shortly."),console.log("Run "+U.cyan(`runhuman wait ${i.jobId}`)+" to wait for results."),console.log("")}catch(n){throw o.fail("Failed to create test job"),n}}async function zn(s){console.log(""),console.log("Setting up Runhuman in the current repository..."),console.log("");let t=await G.prompt([{type:"input",name:"defaultUrl",message:"Default URL to test (optional):"}]);await s.saveProjectConfig({defaultUrl:t.defaultUrl||void 0,defaultDuration:5,defaultDeviceClass:"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 Ln(s){let r=`${(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(r)),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 _n}from"fs";import{join as Fn,dirname as Jn}from"path";import{fileURLToPath as Kn}from"url";var qn=Kn(import.meta.url),Hn=Jn(qn),Vn=Fn(Hn,"../package.json"),Gn=JSON.parse(_n(Vn,"utf-8")),Wn=Gn.version,S=new ft;S.name("runhuman").description("CLI for Runhuman - AI-orchestrated human QA testing").version(Wn);S.addCommand(Ce());S.addCommand(Ie());S.addCommand(le());S.addCommand(Ae());S.addCommand(Se());S.addCommand(Te());S.addCommand(ve());var K=new ft("job");K.description("Manage QA test jobs");K.addCommand(Ce());K.addCommand(Ie());K.addCommand(le());K.addCommand(Ae());K.addCommand(Se());K.addCommand(Te());K.addCommand(ve());S.addCommand(K);S.addCommand(Be());S.addCommand(Ye());S.addCommand(Ze());S.addCommand(Qe());S.addCommand(Xe());var ht=tt();ht.alias("project");S.addCommand(ht);var bt=nt();bt.aliases(["org","organizations","organization"]);S.addCommand(bt);var yt=st();yt.alias("key");S.addCommand(yt);var jt=it();jt.alias("template");S.addCommand(jt);S.addCommand(ct());S.addCommand(ut());process.argv.slice(2).length?S.parse(process.argv):mt().catch(s=>{console.error(s.message),process.exit(1)});
214
+ Run `+k.cyan("runhuman list")+` to see your recent jobs.
215
+ `);break;case"exit":break}}async function Qn(i,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 B.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 B.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await vt(i,t);break;case"setup-repo":await Xn(i);break;case"connect-github":await er(i);break;case"exit":break}}async function vt(i,t){let e=t.projectConfig?.defaultUrl||"",r=await B.prompt([{type:"input",name:"url",message:"URL to test:",default:e||void 0,validate:n=>{if(!n.trim())return"URL is required";try{return new URL(n),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"description",message:"What should we test? (describe in plain English):",validate:n=>n.trim()?!0:"Description is required"}]),o=St("Creating test job...").start();try{let n=i.loadCredentials(),a=await i.loadConfig({apiKey:n?.accessToken});if(!a.project){o.fail("No project selected"),console.log(`
216
+ Run: runhuman projects switch <project-id>`);return}let l=await new b(a).createJob({projectId:a.project,url:r.url,description:r.description});o.succeed("Test job created!"),console.log(""),console.log(" Job ID: "+k.cyan(l.jobId)),console.log(" Status: "+l.status),console.log(""),console.log("A human tester will test your app shortly."),console.log("Run "+k.cyan(`runhuman wait ${l.jobId}`)+" to wait for results."),console.log("")}catch(n){throw o.fail("Failed to create test job"),n}}async function Xn(i){console.log(""),console.log("Setting up Runhuman in the current repository..."),console.log("");let t=await B.prompt([{type:"input",name:"defaultUrl",message:"Default URL to test (optional):"}]);await i.saveProjectConfig({defaultUrl:t.defaultUrl||void 0,defaultDuration:5,defaultDeviceClass:"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 er(i){let r=`${(await i.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(r)),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 tr}from"fs";import{join as or,dirname as nr}from"path";import{fileURLToPath as rr}from"url";var ir=rr(import.meta.url),sr=nr(ir),ar=or(sr,"../package.json"),cr=JSON.parse(tr(ar,"utf-8")),lr=cr.version,S=new Et;S.name("runhuman").description("CLI for Runhuman - AI-orchestrated human QA testing").version(lr);S.addCommand(Ee());S.addCommand(Re());S.addCommand(ge());S.addCommand(Pe());S.addCommand(xe());S.addCommand(ke());S.addCommand(Oe());var _=new Et("job");_.alias("jobs");_.description("Manage QA test jobs");_.addCommand(Ee());_.addCommand(Re());_.addCommand(ge());_.addCommand(Pe());_.addCommand(xe());_.addCommand(ke());_.addCommand(Oe());S.addCommand(_);S.addCommand(at());S.addCommand(ct());S.addCommand(lt());S.addCommand(ut());S.addCommand(pt());var Rt=mt();Rt.alias("project");S.addCommand(Rt);var Pt=ft();Pt.aliases(["org","organizations","organization"]);S.addCommand(Pt);var xt=bt();xt.alias("key");S.addCommand(xt);var kt=yt();kt.alias("template");S.addCommand(kt);S.addCommand(wt());var Ut=Ct();Ut.alias("transfer");S.addCommand(Ut);process.argv.slice(2).length?S.parse(process.argv):Tt().catch(i=>{console.error(i.message),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runhuman",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "CLI for Runhuman - AI-orchestrated human QA testing",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",