runhuman 1.9.1 → 1.10.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.
- package/dist/index.js +93 -89
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,104 +1,104 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var zt=Object.defineProperty;var z=(i,t)=>()=>(i&&(t=i(i=0)),t);var $t=(i,t)=>{for(var e in t)zt(i,e,{get:t[e],enumerable:!0})};function Lt(i){return i instanceof k}function f(i){return Lt(i)?{message:i.message,exitCode:i.exitCode,details:i.details}:i instanceof Error?{message:i.message,exitCode:1}:{message:String(i),exitCode:1}}var k,le,ue,_,pe,de,A=z(()=>{"use strict";k=class extends Error{constructor(e,r=1,o){super(e);this.exitCode=r;this.details=o;this.name="CliError"}},le=class extends k{constructor(t="Authentication failed",e){super(t,2,e),this.name="AuthenticationError"}},ue=class extends k{constructor(t="Resource not found",e){super(t,3,e),this.name="NotFoundError"}},_=class extends k{constructor(t="Validation failed",e){super(t,4,e),this.name="ValidationError"}},pe=class extends k{constructor(t="Operation timed out",e){super(t,5,e),this.name="TimeoutError"}},de=class extends k{constructor(t="Insufficient balance",e){super(t,6,e),this.name="InsufficientBalanceError"}}});function s(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 me=z(()=>{"use strict"});var F,ze=z(()=>{"use strict";me();F={dashboard:s("/dashboard"),managePlan:s("/dashboard/manage-plan"),plans:s("/dashboard/plans"),jobSimple:s("/dashboard/jobs/:jobId"),admin:s("/dashboard/admin"),organizations:s("/dashboard/organizations"),organization:s("/dashboard/organizations/:organizationId"),organizationMembers:s("/dashboard/organizations/:organizationId/members"),organizationSettings:s("/dashboard/organizations/:organizationId/settings"),organizationUsage:s("/dashboard/organizations/:organizationId/usage"),organizationProjects:s("/dashboard/organizations/:organizationId/projects"),organizationJobs:s("/dashboard/organizations/:organizationId/jobs"),organizationApiKeys:s("/dashboard/organizations/:organizationId/api-keys"),organizationIntegrations:s("/dashboard/organizations/:organizationId/integrations"),organizationGitHub:s("/dashboard/organizations/:organizationId/github"),organizationSso:s("/dashboard/organizations/:organizationId/sso"),organizationSlack:s("/dashboard/organizations/:organizationId/slack"),organizationSentry:s("/dashboard/organizations/:organizationId/sentry"),organizationSchedules:s("/dashboard/organizations/:organizationId/schedules"),organizationImportGitHub:s("/dashboard/organizations/:organizationId/import"),settings:s("/dashboard/settings"),settingsAccount:s("/dashboard/settings/account"),tester:s("/tester"),testerApply:s("/tester/apply"),testerJobs:s("/tester/jobs"),testerSettings:s("/tester/settings"),testerTest:s("/tester/test/:jobId"),project:s("/dashboard/:projectId"),playground:s("/dashboard/:projectId/playground"),jobs:s("/dashboard/:projectId/jobs"),job:s("/dashboard/:projectId/jobs/:jobId"),templates:s("/dashboard/:projectId/templates"),template:s("/dashboard/:projectId/templates/:templateId"),issues:s("/dashboard/:projectId/issues"),issue:s("/dashboard/:projectId/issues/:issueNumber"),issueSessions:s("/dashboard/:projectId/issue-sessions"),issueSession:s("/dashboard/:projectId/issue-sessions/:sessionId"),projectSettings:s("/dashboard/:projectId/settings"),schedules:s("/dashboard/:projectId/schedules"),flowCharts:s("/dashboard/:projectId/flowcharts"),flowChart:s("/dashboard/:projectId/flowcharts/:flowchartId"),flowChartView:s("/view/flowchart/:projectId/:flowchartId"),invite:s("/invite/:token"),quickStart:s("/start"),publicJob:s("/j/:jobId/:token"),statuspage:s("/statuspage"),docs:s("/docs/setup"),pricing:s("/pricing"),home:s("/"),learn:s("/learn"),learnLesson:s("/learn/:lessonSlug")}});var w,$e=z(()=>{"use strict";me();w={jobs:s("/jobs"),job:s("/jobs/:jobId"),jobCancel:s("/jobs/:jobId/cancel"),jobCreateIssue:s("/jobs/:jobId/create-issue"),jobStatus:s("/jobs/:jobId/status"),jobArtifact:s("/jobs/:jobId/artifacts/:artifactType"),jobFeedback:s("/jobs/:jobId/feedback"),jobShare:s("/jobs/:jobId/share"),jobClaim:s("/jobs/:jobId/claim"),run:s("/run"),publicJob:s("/public/jobs/:jobId/:token"),testerJob:s("/tester/jobs/:jobId"),testerJobUploadUrls:s("/tester/jobs/:jobId/upload-urls"),testerJobProcessResults:s("/tester/jobs/:jobId/process-results"),testerJobProcessingStatus:s("/tester/jobs/:jobId/processing/:processingJobId"),testerJobExtractFrames:s("/tester/jobs/:jobId/extract-frames"),testerJobAbort:s("/tester/jobs/:jobId/abort"),testerJobRequestExtension:s("/tester/jobs/:jobId/request-extension"),testerJobInvalid:s("/tester/jobs/:jobId/invalid"),testerJobPhase:s("/tester/jobs/:jobId/phase"),livekitTesterToken:s("/tester/jobs/:jobId/livekit-token"),livekitEndSession:s("/tester/jobs/:jobId/livekit-end"),livekitTranscript:s("/tester/jobs/:jobId/livekit-transcript"),livekitViewerToken:s("/jobs/:jobId/livekit-viewer-token"),livekitStatus:s("/jobs/:jobId/livekit-status"),livekitRecording:s("/jobs/:jobId/livekit-recording"),keys:s("/keys"),key:s("/keys/:keyId"),keyRevoke:s("/keys/:keyId/revoke"),keyInfo:s("/key-info"),pats:s("/pats"),pat:s("/pats/:patId"),patRevoke:s("/pats/:patId/revoke"),patInfo:s("/pat-info"),projects:s("/projects"),project:s("/projects/:projectId"),projectJobs:s("/projects/:projectId/jobs"),projectApiKeys:s("/projects/:projectId/api-keys"),projectTransfer:s("/projects/:projectId/transfer"),projectTemplates:s("/projects/:projectId/templates"),projectTemplate:s("/projects/:projectId/templates/:templateId"),projectFlowCharts:s("/projects/:projectId/flowcharts"),projectFlowChartsUpload:s("/projects/:projectId/flowcharts/upload"),projectFlowChart:s("/projects/:projectId/flowcharts/:flowchartId"),projectFlowChartData:s("/projects/:projectId/flowcharts/:flowchartId/data"),projectFlowChartChat:s("/projects/:projectId/flowcharts/:flowchartId/chat"),projectFlowChartShare:s("/projects/:projectId/flowcharts/:flowchartId/share"),projectSchedules:s("/projects/:projectId/schedules"),projectSchedule:s("/projects/:projectId/schedules/:scheduleId"),projectScheduleExecutions:s("/projects/:projectId/schedules/:scheduleId/executions"),bulkCreateProjects:s("/projects/bulk"),githubOAuthAuthorize:s("/github/oauth/authorize"),githubCallback:s("/github/oauth/callback"),githubLink:s("/github/link"),githubIssuesByRepo:s("/github/issues/:owner/:repo"),githubIssueByRepo:s("/github/issues/:owner/:repo/:issueNumber"),githubIssues:s("/github/issues"),githubIssue:s("/github/issues/:issueNumber"),githubIssueComments:s("/github/issues/:issueNumber/comments"),githubIssueLabels:s("/github/issues/labels"),githubIssueAssignees:s("/github/issues/assignees"),githubIssueTest:s("/github/issues/test"),githubIssuesBulkTest:s("/github/issues/bulk-test"),githubTestSessions:s("/github/issues/test-sessions"),githubTestSession:s("/github/issues/test-sessions/:sessionId"),githubTestSessionSeen:s("/github/issues/test-sessions/:sessionId/seen"),githubTestSessionsCounts:s("/github/issues/test-sessions/counts"),githubBulkTest:s("/github/bulk-test"),githubWebhooks:s("/github/webhooks"),authSync:s("/auth/sync"),authMe:s("/auth/me"),authStartup:s("/auth/startup"),authDeleteAccount:s("/auth/account"),authDeletionPreview:s("/auth/account/deletion-preview"),authAvatarUploadUrl:s("/auth/avatar-upload-url"),authAvatar:s("/auth/avatar"),billingBalance:s("/billing/balance"),billingHasCredits:s("/billing/has-credits"),billingPortal:s("/billing/portal"),billingCheckout:s("/billing/checkout"),billingSubscription:s("/billing/subscription"),billingChangePlan:s("/billing/change-plan"),health:s("/health"),status:s("/status"),templates:s("/templates"),issueAnalyzer:s("/issue-analyzer"),prAnalyzer:s("/pr-analyzer"),logs:s("/logs"),relevantIssuesDiscover:s("/relevant-issues/discover"),relevantIssuesLatest:s("/relevant-issues/latest"),onboarding:s("/onboarding"),onboardingComplete:s("/onboarding/complete"),onboardingCheck:s("/onboarding/check"),search:s("/search"),testerApply:s("/tester/apply"),testerProfile:s("/tester/profile"),testerPublicProfile:s("/testers/:testerId/profile"),testerDownloadApp:s("/tester/download-app"),testerDownloadExtension:s("/tester/download-extension"),testerDownloadMobileApp:s("/tester/download-mobile-app"),testerAvailableJobs:s("/tester/jobs/available"),testerActiveJob:s("/tester/active-job"),testerActiveJobRelease:s("/tester/active-job/release"),testerAvatarUploadUrl:s("/tester/profile/avatar-upload-url"),testerAppVersion:s("/tester/app-version"),testerPushToken:s("/tester/push-token"),testerDemoJob:s("/tester/demo-job"),extensionTokens:s("/tester/extension-tokens"),extensionToken:s("/tester/extension-tokens/:tokenId"),extensionStream:s("/extension/stream"),testerJobCorrelate:s("/tester/jobs/:jobId/correlate"),testerJobDataStatus:s("/tester/jobs/:jobId/data-status"),testerJobExtensionToken:s("/tester/jobs/:jobId/extension-token"),repoTemplates:s("/repos/:owner/:repo/templates"),repoTemplate:s("/repos/:owner/:repo/templates/:templateName"),organizations:s("/organizations"),organization:s("/organizations/:organizationId"),organizationMembers:s("/organizations/:organizationId/members"),organizationInvite:s("/organizations/:organizationId/invite"),organizationMember:s("/organizations/:organizationId/members/:userId"),organizationProjects:s("/organizations/:organizationId/projects"),organizationJobs:s("/organizations/:organizationId/jobs"),organizationTransferOwnership:s("/organizations/:organizationId/transfer-ownership"),organizationApiKeys:s("/organizations/:organizationId/api-keys"),organizationApiKey:s("/organizations/:organizationId/api-keys/:keyId"),organizationGitHubInstallations:s("/organizations/:organizationId/github/installations"),organizationGitHubInstallation:s("/organizations/:organizationId/github/installations/:installationId"),organizationGitHubRepos:s("/organizations/:organizationId/github/repos"),organizationGitHubInstallationRefresh:s("/organizations/:organizationId/github/installations/:installationId/refresh"),organizationGitHubRepoCheckAccess:s("/organizations/:organizationId/github/repos/check-access"),organizationGitHubRepoFindUrl:s("/organizations/:organizationId/github/repos/find-url"),organizationSchedules:s("/organizations/:organizationId/schedules"),organizationBilling:s("/organizations/:organizationId/billing"),transfersPending:s("/transfers/pending"),transfersOutgoing:s("/transfers/outgoing"),transferAccept:s("/transfers/:transferId/accept"),transferReject:s("/transfers/:transferId/reject"),transferCancel:s("/transfers/:transferId/cancel"),invite:s("/invites/:token"),inviteRedeem:s("/invites/:token/redeem"),agreements:s("/agreements"),agreementCheck:s("/agreements/check"),organizationSlackInstallations:s("/organizations/:organizationId/slack/installations"),organizationSlackInstallation:s("/organizations/:organizationId/slack/installations/:slackIntegrationId"),organizationSlackOAuthAuthorize:s("/organizations/:organizationId/slack/oauth/authorize"),slackOAuthCallback:s("/slack/oauth/callback"),organizationSlackChannels:s("/organizations/:organizationId/slack/channels"),organizationSlackInstallationChannels:s("/organizations/:organizationId/slack/installations/:slackIntegrationId/channels"),organizationSlackDefaultChannel:s("/organizations/:organizationId/slack/installations/:slackIntegrationId/default-channel"),organizationSsoConnections:s("/organizations/:organizationId/sso/connections"),organizationSsoConnection:s("/organizations/:organizationId/sso/connections/:connectionId"),enterpriseInquiry:s("/enterprise/inquiry"),changelog:s("/changelog"),changelogUnreadCount:s("/changelog/unread-count"),changelogMarkRead:s("/changelog/mark-read"),testSessions:s("/test-session/sessions"),testSessionEnd:s("/test-session/sessions/:sessionId/end"),testSessionEvents:s("/test-session/sessions/:sessionId/events"),testSessionJobStatus:s("/test-session/jobs/:jobId/status"),testSessionShortCode:s("/test-session/short-codes/:code"),organizationSentryConnections:s("/organizations/:organizationId/sentry/connections"),organizationSentryConnection:s("/organizations/:organizationId/sentry/connections/:sentryConnectionId"),organizationSentryTestConnection:s("/organizations/:organizationId/sentry/connections/:sentryConnectionId/test"),organizationSentryProjects:s("/organizations/:organizationId/sentry/connections/:sentryConnectionId/projects"),jobSentryEvents:s("/jobs/:jobId/sentry-events"),feedbackSubmissions:s("/feedback-submissions")}});var je=z(()=>{"use strict";me();ze();$e()});import Kt from"axios";var b,P=z(()=>{"use strict";A();je();b=class{client;config;constructor(t){if(this.config=t,!t.apiUrl)throw new Error("apiUrl is required in CLI config");this.client=Kt.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 le(o,r);case 402:return new de(o,r);case 404:return new ue(o,r);case 400:case 422:return new _(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 qt}from"cosmiconfig";import{homedir as Ht}from"os";import{join as K}from"path";import{readFileSync as Z,writeFileSync as J,existsSync as D,mkdirSync as we,chmodSync as Vt}from"fs";function Bt(i){return Wt.includes(i)}var Gt,$,V,Q,Wt,h,U=z(()=>{"use strict";Gt="runhuman",$=K(Ht(),".config","runhuman"),V=K($,"config.json"),Q=K($,"credentials.json"),Wt=["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&&Bt(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 qt(Gt).search(this.cwd))?.config||null}catch{return null}}loadGlobalConfig(){try{if(!D(V))return null;let t=Z(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($)||we($,{recursive:!0});let r={};if(D(V)){let o=Z(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=Z(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=Z(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($)||we($,{recursive:!0}),J(Q,JSON.stringify(t,null,2));try{process.platform!=="win32"&&Vt(Q,384)}catch{}}loadCredentials(){try{if(!D(Q))return null;let t=Z(Q,"utf-8");return JSON.parse(t)}catch{return null}}clearCredentials(){D(Q)&&J(Q,"{}")}saveUserInfo(t){let e=K($,"user.json");D($)||we($,{recursive:!0}),J(e,JSON.stringify(t,null,2))}loadUserInfo(){try{let t=K($,"user.json");if(!D(t))return null;let e=Z(t,"utf-8");return JSON.parse(e)}catch{return null}}clearUserInfo(){let t=K($,"user.json");D(t)&&J(t,"{}")}}});import G from"chalk";import Yt from"cli-table3";var Xt,Ie,p,x=z(()=>{"use strict";Xt=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",`
|
|
2
|
+
var to=Object.defineProperty;var $=(i,t)=>()=>(i&&(t=i(i=0)),t);var oo=(i,t)=>{for(var e in t)to(i,e,{get:t[e],enumerable:!0})};function no(i){return i instanceof R}function g(i){return no(i)?{message:i.message,exitCode:i.exitCode,details:i.details}:i instanceof Error?{message:i.message,exitCode:1}:{message:String(i),exitCode:1}}var R,de,me,U,fe,ge,Z,A=$(()=>{"use strict";R=class extends Error{constructor(e,n=1,o){super(e);this.exitCode=n;this.details=o;this.name="CliError"}},de=class extends R{constructor(t="Authentication failed",e){super(t,2,e),this.name="AuthenticationError"}},me=class extends R{constructor(t="Resource not found",e){super(t,3,e),this.name="NotFoundError"}},U=class extends R{constructor(t="Validation failed",e){super(t,4,e),this.name="ValidationError"}},fe=class extends R{constructor(t="Operation timed out",e){super(t,5,e),this.name="TimeoutError"}},ge=class extends R{constructor(t="Insufficient balance",e){super(t,6,e),this.name="InsufficientBalanceError"}},Z=class extends R{constructor(t="Resource conflict",e){super(t,7,e),this.name="ConflictError"}}});function s(i){return{pattern:i,build:e=>{if(!e)return i;let n=i;for(let[o,r]of Object.entries(e))r!==void 0&&(n=n.replace(`:${o}?`,r),n=n.replace(`:${o}`,r));return n=n.replace(/\/:[^/]+\?/g,""),n=n.replace(/\/+/g,"/"),n.length>1&&n.endsWith("/")&&(n=n.slice(0,-1)),n}}}var he=$(()=>{"use strict"});var F,We=$(()=>{"use strict";he();F={dashboard:s("/dashboard"),managePlan:s("/dashboard/manage-plan"),plans:s("/dashboard/plans"),jobSimple:s("/dashboard/jobs/:jobId"),admin:s("/dashboard/admin"),organizations:s("/dashboard/organizations"),organization:s("/dashboard/organizations/:organizationId"),organizationMembers:s("/dashboard/organizations/:organizationId/members"),organizationSettings:s("/dashboard/organizations/:organizationId/settings"),organizationUsage:s("/dashboard/organizations/:organizationId/usage"),organizationProjects:s("/dashboard/organizations/:organizationId/projects"),organizationJobs:s("/dashboard/organizations/:organizationId/jobs"),organizationApiKeys:s("/dashboard/organizations/:organizationId/api-keys"),organizationIntegrations:s("/dashboard/organizations/:organizationId/integrations"),organizationGitHub:s("/dashboard/organizations/:organizationId/github"),organizationSso:s("/dashboard/organizations/:organizationId/sso"),organizationSlack:s("/dashboard/organizations/:organizationId/slack"),organizationSentry:s("/dashboard/organizations/:organizationId/sentry"),organizationSchedules:s("/dashboard/organizations/:organizationId/schedules"),organizationImportGitHub:s("/dashboard/organizations/:organizationId/import"),settings:s("/dashboard/settings"),settingsAccount:s("/dashboard/settings/account"),tester:s("/tester"),testerApply:s("/tester/apply"),testerJobs:s("/tester/jobs"),testerSettings:s("/tester/settings"),testerTest:s("/tester/test/:jobId"),project:s("/dashboard/:projectId"),playground:s("/dashboard/:projectId/playground"),jobs:s("/dashboard/:projectId/jobs"),job:s("/dashboard/:projectId/jobs/:jobId"),templates:s("/dashboard/:projectId/templates"),template:s("/dashboard/:projectId/templates/:templateId"),issues:s("/dashboard/:projectId/issues"),issue:s("/dashboard/:projectId/issues/:issueNumber"),issueSessions:s("/dashboard/:projectId/issue-sessions"),issueSession:s("/dashboard/:projectId/issue-sessions/:sessionId"),projectSettings:s("/dashboard/:projectId/settings"),schedules:s("/dashboard/:projectId/schedules"),flowCharts:s("/dashboard/:projectId/flowcharts"),flowChart:s("/dashboard/:projectId/flowcharts/:flowchartId"),flowChartView:s("/view/flowchart/:projectId/:flowchartId"),invite:s("/invite/:token"),quickStart:s("/start"),publicJob:s("/j/:jobId/:token"),statuspage:s("/statuspage"),docs:s("/docs/setup"),pricing:s("/pricing"),home:s("/"),learn:s("/learn"),learnLesson:s("/learn/:lessonSlug")}});var I,Ve=$(()=>{"use strict";he();I={jobs:s("/jobs"),job:s("/jobs/:jobId"),jobCancel:s("/jobs/:jobId/cancel"),jobCreateIssue:s("/jobs/:jobId/create-issue"),jobStatus:s("/jobs/:jobId/status"),jobArtifact:s("/jobs/:jobId/artifacts/:artifactType"),jobFeedback:s("/jobs/:jobId/feedback"),jobShare:s("/jobs/:jobId/share"),jobClaim:s("/jobs/:jobId/claim"),run:s("/run"),publicJob:s("/public/jobs/:jobId/:token"),testerJob:s("/tester/jobs/:jobId"),testerJobUploadUrls:s("/tester/jobs/:jobId/upload-urls"),testerJobProcessResults:s("/tester/jobs/:jobId/process-results"),testerJobProcessingStatus:s("/tester/jobs/:jobId/processing/:processingJobId"),testerJobExtractFrames:s("/tester/jobs/:jobId/extract-frames"),testerJobAbort:s("/tester/jobs/:jobId/abort"),testerJobRequestExtension:s("/tester/jobs/:jobId/request-extension"),testerJobInvalid:s("/tester/jobs/:jobId/invalid"),testerJobPhase:s("/tester/jobs/:jobId/phase"),livekitTesterToken:s("/tester/jobs/:jobId/livekit-token"),livekitEndSession:s("/tester/jobs/:jobId/livekit-end"),livekitTranscript:s("/tester/jobs/:jobId/livekit-transcript"),testerLivekitRecording:s("/tester/jobs/:jobId/livekit-recording"),livekitViewerToken:s("/jobs/:jobId/livekit-viewer-token"),livekitStatus:s("/jobs/:jobId/livekit-status"),livekitRecording:s("/jobs/:jobId/livekit-recording"),keys:s("/keys"),key:s("/keys/:keyId"),keyRevoke:s("/keys/:keyId/revoke"),keyInfo:s("/key-info"),pats:s("/pats"),pat:s("/pats/:patId"),patRevoke:s("/pats/:patId/revoke"),patInfo:s("/pat-info"),projects:s("/projects"),project:s("/projects/:projectId"),projectJobs:s("/projects/:projectId/jobs"),projectApiKeys:s("/projects/:projectId/api-keys"),projectTransfer:s("/projects/:projectId/transfer"),projectTemplates:s("/projects/:projectId/templates"),projectTemplate:s("/projects/:projectId/templates/:templateId"),projectFlowCharts:s("/projects/:projectId/flowcharts"),projectFlowChartsUpload:s("/projects/:projectId/flowcharts/upload"),projectFlowChart:s("/projects/:projectId/flowcharts/:flowchartId"),projectFlowChartData:s("/projects/:projectId/flowcharts/:flowchartId/data"),projectFlowChartChat:s("/projects/:projectId/flowcharts/:flowchartId/chat"),projectFlowChartShare:s("/projects/:projectId/flowcharts/:flowchartId/share"),projectSchedules:s("/projects/:projectId/schedules"),projectSchedule:s("/projects/:projectId/schedules/:scheduleId"),projectScheduleExecutions:s("/projects/:projectId/schedules/:scheduleId/executions"),bulkCreateProjects:s("/projects/bulk"),githubOAuthAuthorize:s("/github/oauth/authorize"),githubCallback:s("/github/oauth/callback"),githubLink:s("/github/link"),githubIssuesByRepo:s("/github/issues/:owner/:repo"),githubIssueByRepo:s("/github/issues/:owner/:repo/:issueNumber"),githubIssues:s("/github/issues"),githubIssue:s("/github/issues/:issueNumber"),githubIssueComments:s("/github/issues/:issueNumber/comments"),githubIssueLabels:s("/github/issues/labels"),githubIssueAssignees:s("/github/issues/assignees"),githubIssueTest:s("/github/issues/test"),githubIssuesBulkTest:s("/github/issues/bulk-test"),githubTestSessions:s("/github/issues/test-sessions"),githubTestSession:s("/github/issues/test-sessions/:sessionId"),githubTestSessionSeen:s("/github/issues/test-sessions/:sessionId/seen"),githubTestSessionsCounts:s("/github/issues/test-sessions/counts"),githubBulkTest:s("/github/bulk-test"),githubWebhooks:s("/github/webhooks"),authSync:s("/auth/sync"),authMe:s("/auth/me"),authStartup:s("/auth/startup"),authDeleteAccount:s("/auth/account"),authDeletionPreview:s("/auth/account/deletion-preview"),authAvatarUploadUrl:s("/auth/avatar-upload-url"),authAvatar:s("/auth/avatar"),billingBalance:s("/billing/balance"),billingHasCredits:s("/billing/has-credits"),billingPortal:s("/billing/portal"),billingCheckout:s("/billing/checkout"),billingSubscription:s("/billing/subscription"),billingChangePlan:s("/billing/change-plan"),polarWebhooks:s("/polar/webhooks"),health:s("/health"),status:s("/status"),templates:s("/templates"),issueAnalyzer:s("/issue-analyzer"),prAnalyzer:s("/pr-analyzer"),logs:s("/logs"),relevantIssuesDiscover:s("/relevant-issues/discover"),relevantIssuesLatest:s("/relevant-issues/latest"),onboarding:s("/onboarding"),onboardingComplete:s("/onboarding/complete"),onboardingCheck:s("/onboarding/check"),search:s("/search"),testerApply:s("/tester/apply"),testerProfile:s("/tester/profile"),testerPublicProfile:s("/testers/:testerId/profile"),testerDownloadApp:s("/tester/download-app"),testerDownloadExtension:s("/tester/download-extension"),testerDownloadMobileApp:s("/tester/download-mobile-app"),testerAvailableJobs:s("/tester/jobs/available"),testerActiveJob:s("/tester/active-job"),testerActiveJobRelease:s("/tester/active-job/release"),testerAvatarUploadUrl:s("/tester/profile/avatar-upload-url"),testerAppVersion:s("/tester/app-version"),testerPushToken:s("/tester/push-token"),testerDemoJob:s("/tester/demo-job"),extensionTokens:s("/tester/extension-tokens"),extensionToken:s("/tester/extension-tokens/:tokenId"),extensionStream:s("/extension/stream"),testerJobCorrelate:s("/tester/jobs/:jobId/correlate"),testerJobDataStatus:s("/tester/jobs/:jobId/data-status"),testerJobExtensionToken:s("/tester/jobs/:jobId/extension-token"),repoTemplates:s("/repos/:owner/:repo/templates"),repoTemplate:s("/repos/:owner/:repo/templates/:templateName"),organizations:s("/organizations"),organization:s("/organizations/:organizationId"),organizationMembers:s("/organizations/:organizationId/members"),organizationInvite:s("/organizations/:organizationId/invite"),organizationMember:s("/organizations/:organizationId/members/:userId"),organizationRestoreMember:s("/organizations/:organizationId/members/:userId/restore"),organizationProjects:s("/organizations/:organizationId/projects"),organizationJobs:s("/organizations/:organizationId/jobs"),organizationTransferOwnership:s("/organizations/:organizationId/transfer-ownership"),organizationApiKeys:s("/organizations/:organizationId/api-keys"),organizationApiKey:s("/organizations/:organizationId/api-keys/:keyId"),organizationGitHubInstallations:s("/organizations/:organizationId/github/installations"),organizationGitHubInstallation:s("/organizations/:organizationId/github/installations/:installationId"),organizationGitHubRepos:s("/organizations/:organizationId/github/repos"),organizationGitHubInstallationRefresh:s("/organizations/:organizationId/github/installations/:installationId/refresh"),organizationGitHubRepoCheckAccess:s("/organizations/:organizationId/github/repos/check-access"),organizationGitHubRepoFindUrl:s("/organizations/:organizationId/github/repos/find-url"),organizationSchedules:s("/organizations/:organizationId/schedules"),organizationBilling:s("/organizations/:organizationId/billing"),transfersPending:s("/transfers/pending"),transfersOutgoing:s("/transfers/outgoing"),transferAccept:s("/transfers/:transferId/accept"),transferReject:s("/transfers/:transferId/reject"),transferCancel:s("/transfers/:transferId/cancel"),invite:s("/invites/:token"),inviteRedeem:s("/invites/:token/redeem"),agreements:s("/agreements"),agreementCheck:s("/agreements/check"),organizationSlackInstallations:s("/organizations/:organizationId/slack/installations"),organizationSlackInstallation:s("/organizations/:organizationId/slack/installations/:slackIntegrationId"),organizationSlackOAuthAuthorize:s("/organizations/:organizationId/slack/oauth/authorize"),slackOAuthCallback:s("/slack/oauth/callback"),organizationSlackChannels:s("/organizations/:organizationId/slack/channels"),organizationSlackInstallationChannels:s("/organizations/:organizationId/slack/installations/:slackIntegrationId/channels"),organizationSlackDefaultChannel:s("/organizations/:organizationId/slack/installations/:slackIntegrationId/default-channel"),organizationSsoConnections:s("/organizations/:organizationId/sso/connections"),organizationSsoConnection:s("/organizations/:organizationId/sso/connections/:connectionId"),enterpriseInquiry:s("/enterprise/inquiry"),changelog:s("/changelog"),changelogUnreadCount:s("/changelog/unread-count"),changelogMarkRead:s("/changelog/mark-read"),testSessions:s("/test-session/sessions"),testSessionEnd:s("/test-session/sessions/:sessionId/end"),testSessionEvents:s("/test-session/sessions/:sessionId/events"),testSessionJobStatus:s("/test-session/jobs/:jobId/status"),testSessionShortCode:s("/test-session/short-codes/:code"),organizationSentryConnections:s("/organizations/:organizationId/sentry/connections"),organizationSentryConnection:s("/organizations/:organizationId/sentry/connections/:sentryConnectionId"),organizationSentryTestConnection:s("/organizations/:organizationId/sentry/connections/:sentryConnectionId/test"),organizationSentryProjects:s("/organizations/:organizationId/sentry/connections/:sentryConnectionId/projects"),jobSentryEvents:s("/jobs/:jobId/sentry-events"),feedbackSubmissions:s("/feedback-submissions"),utmVisitors:s("/utm-visitors"),mppJobs:s("/mpp/jobs"),mppJobResult:s("/mpp/jobs/:jobId/result"),mppWalletDeposit:s("/mpp/wallet/deposit"),mppWalletBalance:s("/mpp/wallet/balance")}});var Ee=$(()=>{"use strict";he();We();Ve()});import ao from"axios";var y,x=$(()=>{"use strict";A();Ee();y=class{client;config;constructor(t){if(this.config=t,!t.apiUrl)throw new Error("apiUrl is required in CLI config");this.client=ao.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,n=t.response.data,o=n?.error||n?.message||t.message;switch(e){case 401:case 403:return new de(o,n);case 402:return new ge(o,n);case 404:return new me(o,n);case 409:return new Z(o,n);case 400:case 422:return new U(o,n);default:return new R(o,1,n)}}return t.code==="ECONNABORTED"?new R("Request timeout",5):t.code==="ENOTFOUND"||t.code==="ECONNREFUSED"?new R("Cannot connect to Runhuman API",1):new R(t.message,1)}async createJob(t){return(await this.client.post(I.jobs.build(),t)).data}async getJob(t){return(await this.client.get(I.job.build({jobId:t}))).data}async listJobs(t){return(await this.client.get(I.jobs.build(),{params:t})).data}async cancelJob(t){await this.client.post(I.jobCancel.build({jobId:t}))}async deleteJob(t){await this.client.delete(I.job.build({jobId:t}))}async listProjects(t){return(await this.client.get(I.projects.build(),{params:t})).data}async getProject(t){return(await this.client.get(I.project.build({projectId:t}))).data}async createProject(t){return(await this.client.post(I.projects.build(),t)).data}async updateProject(t,e){return(await this.client.patch(I.project.build({projectId:t}),e)).data}async deleteProject(t){await this.client.delete(I.project.build({projectId:t}))}async listApiKeys(t){return(await this.client.get(I.organizationApiKeys.build({organizationId:t}))).data}async createApiKey(t,e){return(await this.client.post(I.organizationApiKeys.build({organizationId:t}),{name:e})).data}async getApiKey(t){return(await this.client.get(I.key.build({keyId:t}))).data}async deleteApiKey(t){await this.client.delete(I.key.build({keyId:t}))}async listOrganizations(t){return(await this.client.get(I.organizations.build(),{params:t})).data}async getOrganization(t){return(await this.client.get(I.organization.build({organizationId:t}))).data}async getOrganizationBilling(t){return(await this.client.get(I.organizationBilling.build({organizationId:t}))).data}async listOrganizationProjects(t,e){return(await this.client.get(I.organizationProjects.build({organizationId:t}),{params:e})).data}async listTemplates(t){return(await this.client.get(I.projectTemplates.build({projectId:t}))).data}async getTemplate(t,e){return(await this.client.get(I.projectTemplate.build({projectId:t,templateId:e}))).data}async createTemplate(t,e){return(await this.client.post(I.projectTemplates.build({projectId:t}),e)).data}async updateTemplate(t,e,n){return(await this.client.patch(I.projectTemplate.build({projectId:t,templateId:e}),n)).data}async deleteTemplate(t,e){await this.client.delete(I.projectTemplate.build({projectId:t,templateId:e}))}async listSchedules(t,e){return(await this.client.get(I.projectSchedules.build({projectId:t}),{params:e})).data}async createSchedule(t,e){return(await this.client.post(I.projectSchedules.build({projectId:t}),e)).data}async getSchedule(t,e){return(await this.client.get(I.projectSchedule.build({projectId:t,scheduleId:e}))).data}async updateSchedule(t,e,n){return(await this.client.patch(I.projectSchedule.build({projectId:t,scheduleId:e}),n)).data}async deleteSchedule(t,e){await this.client.delete(I.projectSchedule.build({projectId:t,scheduleId:e}))}async listScheduleExecutions(t,e,n){return(await this.client.get(I.projectScheduleExecutions.build({projectId:t,scheduleId:e}),{params:n})).data}async getCurrentUser(){return(await this.client.get(I.authMe.build())).data}async listGithubRepos(t,e){return(await this.client.get(I.organizationGitHubRepos.build({organizationId:t}),{params:e})).data}async listGithubIssues(t,e,n){return(await this.client.get(I.githubIssuesByRepo.build({owner:t,repo:e}),{params:n})).data}async getGithubIssue(t,e,n){return(await this.client.get(I.githubIssueByRepo.build({owner:t,repo:e,issueNumber:String(n)}))).data}async initiateTransfer(t,e){let n=await this.client.post(I.projectTransfer.build({projectId:t}),{toOrganizationId:e});return n.status===200?{type:"immediate",project:n.data}:{type:"pending",transfer:n.data}}async listPendingTransfers(){return(await this.client.get(I.transfersPending.build())).data}async listOutgoingTransfers(){return(await this.client.get(I.transfersOutgoing.build())).data}async acceptTransfer(t){return(await this.client.post(I.transferAccept.build({transferId:t}),{})).data}async rejectTransfer(t){return(await this.client.post(I.transferReject.build({transferId:t}),{})).data}async cancelTransfer(t){return(await this.client.post(I.transferCancel.build({transferId:t}),{})).data}}});import{cosmiconfig as so}from"cosmiconfig";import{homedir as co}from"os";import{join as q}from"path";import{readFileSync as ee,writeFileSync as J,existsSync as M,mkdirSync as ke,chmodSync as lo}from"fs";function mo(i){return po.includes(i)}var uo,_,W,te,po,h,k=$(()=>{"use strict";uo="runhuman",_=q(co(),".config","runhuman"),W=q(_,"config.json"),te=q(_,"credentials.json"),po=["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(),n=e?.accessToken?{apiKey:e.accessToken}:{},o=Object.fromEntries(Object.entries(t).filter(([,c])=>c!==void 0));return{...this.getDefaults(),...this.globalConfig,...this.projectConfig,...n,...this.envConfig,...o}}getDefaults(){return{apiUrl:"https://runhuman.com",outputFormat:"pretty",color:!0,autoOpenBrowser:!0,defaultDuration:5,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&&mo(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 so(uo).search(this.cwd))?.config||null}catch{return null}}loadGlobalConfig(){try{if(!M(W))return null;let t=ee(W,"utf-8");return JSON.parse(t)}catch{return null}}async get(t){return(await this.loadConfig())[t]}async set(t,e,n=!1){n?await this.setGlobalConfig(t,e):await this.setProjectConfig(t,e)}async setGlobalConfig(t,e){M(_)||ke(_,{recursive:!0});let n={};if(M(W)){let o=ee(W,"utf-8");n=JSON.parse(o)}n[t]=e,J(W,JSON.stringify(n,null,2))}async setProjectConfig(t,e){let n=q(this.cwd,".runhumanrc"),o={};if(M(n)){let r=ee(n,"utf-8");o=JSON.parse(r)}o[t]=e,J(n,JSON.stringify(o,null,2))}async saveProjectConfig(t){let e=q(this.cwd,".runhumanrc"),n={};if(M(e)){let r=ee(e,"utf-8");n=JSON.parse(r)}let o={...n,...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")&&M(W)&&J(W,"{}"),t==="project"||t==="all"){let e=q(this.cwd,".runhumanrc");M(e)&&J(e,"{}")}}saveCredentials(t){M(_)||ke(_,{recursive:!0}),J(te,JSON.stringify(t,null,2));try{process.platform!=="win32"&&lo(te,384)}catch{}}loadCredentials(){try{if(!M(te))return null;let t=ee(te,"utf-8");return JSON.parse(t)}catch{return null}}clearCredentials(){M(te)&&J(te,"{}")}saveUserInfo(t){let e=q(_,"user.json");M(_)||ke(_,{recursive:!0}),J(e,JSON.stringify(t,null,2))}loadUserInfo(){try{let t=q(_,"user.json");if(!M(t))return null;let e=ee(t,"utf-8");return JSON.parse(e)}catch{return null}}clearUserInfo(){let t=q(_,"user.json");M(t)&&J(t,"{}")}}});import V from"chalk";import fo from"cli-table3";var go,xe,u,T=$(()=>{"use strict";go=24,xe=60,u=class{constructor(t={}){this.options=t}output(t){this.options.json?console.log(JSON.stringify(t,null,2)):t.success?t.data&&!this.options.quiet&&this.outputPretty(t.data):this.outputError(t.error?.message||"An error occurred")}outputPretty(t){typeof t=="string"?console.log(t):(Array.isArray(t),console.log(JSON.stringify(t,null,2)))}outputError(t,e){if(this.options.json){let n={success:!1,error:{message:t,details:e},timestamp:new Date().toISOString()};console.error(JSON.stringify(n,null,2))}else console.error(this.color("red",`
|
|
3
3
|
Error: ${t}
|
|
4
4
|
`)),e?.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(
|
|
7
|
-
`+"=".repeat(
|
|
8
|
-
`))}detail(t,e){this.options.json||this.options.quiet||console.log(` ${t}:`.padEnd(
|
|
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(xe))}sectionHeader(t){this.options.json||this.options.quiet||(console.log(`
|
|
7
|
+
`+"=".repeat(xe)),console.log(t),console.log("=".repeat(xe)+`
|
|
8
|
+
`))}detail(t,e){this.options.json||this.options.quiet||console.log(` ${t}:`.padEnd(go)+String(e))}heading(t){this.options.json||this.options.quiet||console.log(`
|
|
9
9
|
${t}
|
|
10
|
-
`)}hints(t){if(this.options.json||this.options.quiet)return;let
|
|
11
|
-
`);let e=new
|
|
12
|
-
`);super(`"${e}" matches multiple ${
|
|
13
|
-
${
|
|
14
|
-
Please provide more characters.`,1);this.prefix=e;this.resourceType=
|
|
10
|
+
`)}hints(t){if(this.options.json||this.options.quiet)return;let n=Math.max(...t.map(([o])=>o.length))+4;for(let[o,r]of t)console.log(` ${o}:`.padEnd(n)+` ${r}`);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(n=>`${n.id} ${n.status} ${n.url}`).join(`
|
|
11
|
+
`);let e=new fo({head:this.tableHead(["Job ID","Status","URL","Created","Duration","Cost"]),colWidths:[15,12,30,14,10,10]});return t.forEach(n=>{e.push([n.id,this.formatStatus(n.status),n.url?this.truncate(n.url,28):"-",n.createdAt?this.formatDate(n.createdAt):"-",n.testDurationSeconds?this.formatDuration(n.testDurationSeconds):"-",n.costUsd?`$${n.costUsd.toFixed(3)}`:"-"])}),e.toString()}formatStatus(t){let e={pending:"yellow",queued:"yellow",waiting:"blue",working:"blue",completed:"green",incomplete:"yellow",abandoned:"red",rejected:"gray",error:"red",failed:"red"},n=t.replaceAll("_"," ");return this.color(e[t]||"white",n)}formatDuration(t){if(t<60)return`${t}s`;let e=Math.floor(t/60),n=t%60;if(e<60)return`${e}m ${n}s`;let o=Math.floor(e/60),r=e%60;return`${o}h ${r}m ${n}s`}formatDate(t){let e=new Date(t),o=new Date().getTime()-e.getTime(),r=Math.floor(o/1e3),c=Math.floor(r/60),p=Math.floor(c/60),l=Math.floor(p/24);return r<60?`${r}s ago`:c<60?`${c}m ago`:p<24?`${p}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: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,n){return{success:!1,error:{message:t,code:e,details:n},timestamp:new Date().toISOString()}}}});function B(i){return i.replace(/-/g,"").toLowerCase()}function Pe(i){let t=i.replace(/-/g,"");return bo.test(t)}async function w(i,t,e,n){if(n?.requireFullId&&!Pe(e)){let l=Be[t],a=await l(i),d=B(e),m=a.filter(f=>B(f.id).startsWith(d));throw new ve(e,t,n.requireFullId.reason,m)}if(Ue.test(e))return e;let o=Be[t],r=await o(i),c=B(e),p=r.filter(l=>B(l.id).startsWith(c));if(p.length===0)throw new ae(e,t);if(p.length===1)return p[0].id;throw new ie(e,t,p)}var Ue,bo,ie,ae,ve,Be,P=$(()=>{"use strict";A();Ue=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,bo=/^[0-9a-f]{32}$/i,ie=class extends R{constructor(e,n,o){let r=o.map(c=>` ${c.id} ${c.label}`).join(`
|
|
12
|
+
`);super(`"${e}" matches multiple ${n}s:
|
|
13
|
+
${r}
|
|
14
|
+
Please provide more characters.`,1);this.prefix=e;this.resourceType=n;this.matches=o;this.name="AmbiguousIdError"}},ae=class extends R{constructor(e,n){super(`No ${n} found matching "${e}".`,3);this.prefix=e;this.resourceType=n;this.name="NoMatchError"}},ve=class extends R{constructor(e,n,o,r){let c=`Full ${n} ID required for ${o} \u2014 short prefixes are not allowed for this operation.`;if(c+=`
|
|
15
15
|
|
|
16
|
-
Provided: "${e}"`,
|
|
16
|
+
Provided: "${e}"`,r.length===1)c+=`
|
|
17
17
|
|
|
18
|
-
This prefix matches: ${
|
|
19
|
-
If this is correct, re-run the command with the full ID.`;else if(
|
|
18
|
+
This prefix matches: ${r[0].id} ${r[0].label}`,c+=`
|
|
19
|
+
If this is correct, re-run the command with the full ID.`;else if(r.length>1){let p=r.map(l=>` ${l.id} ${l.label}`).join(`
|
|
20
20
|
`);c+=`
|
|
21
21
|
|
|
22
|
-
This prefix matches multiple ${
|
|
23
|
-
${
|
|
24
|
-
Use the full ID of the intended ${
|
|
22
|
+
This prefix matches multiple ${n}s:
|
|
23
|
+
${p}`,c+=`
|
|
24
|
+
Use the full ID of the intended ${n}.`}else c+=`
|
|
25
25
|
|
|
26
|
-
No ${
|
|
27
|
-
Results Summary:`);for(let[d,m]of Object.entries(a.result))console.log(` ${d}: ${m}`)}console.log(),e.hints([["Full results",`runhuman results ${i}`]])}return}if(a.status==="error"||a.status==="incomplete"||a.status==="abandoned")throw
|
|
26
|
+
No ${n} found matching this prefix.`;super(c,1);this.prefix=e;this.resourceType=n;this.reason=o;this.matches=r;this.name="FullIdRequiredError"}};Be={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 n of t){let{items:o}=await i.listApiKeys(n.id);for(let r of o)e.push({id:r.id,label:r.name})}return e},async template(i){let{items:t}=await i.listProjects({limit:200}),e=[];for(let n of t){let{items:o}=await i.listTemplates(n.id);for(let r of o)e.push({id:r.id,label:r.name})}return e},async schedule(i){let{items:t}=await i.listProjects({limit:200}),e=[];for(let n of t){let{items:o}=await i.listSchedules(n.id,{limit:200});for(let r of o)e.push({id:r.id,label:r.name})}return e},async transfer(i){let[t,e]=await Promise.all([i.listPendingTransfers(),i.listOutgoingTransfers()]),n=[...t.items,...e.items],o=new Set,r=[];for(let c of n)o.has(c.id)||(o.add(c.id),r.push({id:c.id,label:c.projectName}));return r}}});function Ne(i){return{id:i.id,label:`${i.status.padEnd(10)} ${i.description??""}`.trim()}}function ot(i){return{id:i.id,label:i.name}}async function nt(i,t,e,n){let o=B(i);for(let r of e){let p=(await r.fetch()).filter(l=>B(l.id).startsWith(o));if(p.length===1)return n.info(`Resolved ${t} in ${r.label}`),p[0].id;if(p.length>1)throw new ie(i,t,p)}throw new ae(i,t)}function rt(i){return Ue.test(i)||Pe(i)}async function H(i,t,e,n,o){if(rt(t))return t;let r=qn(i,e,n);return nt(t,"job",r,o)}function qn(i,t,e){if(t.projectId)return[{label:`project ${t.projectId}`,fetch:()=>Ze(i,t.projectId)}];if(t.organizationId)return[{label:`organization ${t.organizationId}`,fetch:()=>et(i,t.organizationId)}];let n=[];return e.project&&n.push({label:"default project",fetch:()=>Ze(i,e.project)}),e.organization&&n.push({label:"default organization",fetch:()=>et(i,e.organization)}),n.push({label:"all organizations",fetch:()=>Kn(i)}),n}async function Ze(i,t){let{items:e}=await i.listJobs({projectId:t,limit:200});return e.map(Ne)}async function et(i,t){let{items:e}=await i.listJobs({organizationId:t,limit:200});return e.map(Ne)}async function Kn(i){let{items:t}=await i.listOrganizations({limit:200}),e=[];for(let n of t){let{items:o}=await i.listJobs({organizationId:n.id,limit:200});e.push(...o.map(Ne))}return e}async function le(i,t,e,n,o){if(rt(t))return t;let r=Hn(i,e,n);return nt(t,"project",r,o)}function Hn(i,t,e){if(t.organizationId)return[{label:`organization ${t.organizationId}`,fetch:()=>tt(i,t.organizationId)}];let n=[];return e.organization&&n.push({label:"default organization",fetch:()=>tt(i,e.organization)}),n.push({label:"all organizations",fetch:()=>Gn(i)}),n}async function tt(i,t){let{items:e}=await i.listOrganizationProjects(t,{limit:200});return e.map(ot)}async function Gn(i){let{items:t}=await i.listProjects({limit:200});return t.map(ot)}var oe=$(()=>{"use strict";P()});var De={};oo(De,{waitCommand:()=>we,waitForJob:()=>it});import{Command as Wn}from"commander";import Vn from"ora";async function it(i,t,e,n=600){let o=Date.now(),r=n*1e3,c=1e4,p=null;for(e.options.json||(p=Vn("Waiting for job completion...").start());;){let l=Date.now()-o;if(l>=r)throw p&&p.fail("Timeout waiting for job completion"),new fe(`Job did not complete within ${n} seconds`);let a=await t.getJob(i);if(p){let d=e.formatDuration(Math.floor(l/1e3));p.text=`Waiting for job completion... (${d} elapsed, status: ${a.status})`}if(a.status==="completed"){if(p&&p.succeed("Test Completed!"),!e.options.json){if(console.log(),e.detail("Duration",a.testDurationSeconds?e.formatDuration(a.testDurationSeconds):"N/A"),e.detail("Cost","$"+(a.costUsd||0).toFixed(3)),a.testerAlias&&e.detail("Tester",a.testerAlias),a.result){console.log(`
|
|
27
|
+
Results Summary:`);for(let[d,m]of Object.entries(a.result))console.log(` ${d}: ${m}`)}console.log(),e.hints([["Full results",`runhuman results ${i}`]])}return}if(a.status==="error"||a.status==="incomplete"||a.status==="abandoned")throw p&&p.fail(`Test ${a.status}`),new Error(`Job ${a.status}: ${a.testerResponse||"No details available"}`);if(a.status==="rejected")throw p&&p.fail("Test was rejected"),new Error("Job was rejected");await new Promise(d=>setTimeout(d,c))}}function we(){let i=new Wn("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("--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}),r=new u({json:e.json,color:o.color}),c=new y(o),p={projectId:e.project?await w(c,"project",e.project):void 0,organizationId:e.organization?await w(c,"organization",e.organization):void 0},l=await H(c,t,p,o,r);if(await it(l,c,r,e.timeout||600),e.json){let a=await c.getJob(l),d=u.result(a);r.output(d)}}catch(n){let o=g(n);new u({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}var je=$(()=>{"use strict";x();k();T();A();oe();P()});import{Command as Wt}from"commander";x();k();T();A();import{Command as Bn}from"commander";import Yn from"ora";A();var ho={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 yo(i,t,e){return i?"flag":process.env[e.envVar]?"env":"global config"}function C(i,t,e,n){let o=ho[i],r=t||e[o.configKey];if(!r)throw new U(`${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=
|
|
31
|
-
${
|
|
30
|
+
\u2022 Set via env: export ${o.envVar}=<id>`);let c=yo(t,e,o);return c!=="flag"&&n.info(`Using default ${i}: ${r} [from ${c}]`),r}P();Ee();var Re=["ios","android","pc","mac"],Oe=["english","spanish"];var se={type:"multi-select",matchMode:"any",values:Re,labels:{ios:"iOS",android:"Android",pc:"PC",mac:"Mac"},slackLabels:{ios:"iOS",android:"Android",pc:"PC",mac:"Mac"},categoryName:"device",sectionLabel:"Device",tooltip:"Tester must have at least one of the selected devices"},ce={type:"multi-select",matchMode:"all",values:Oe,labels:{english:"English",spanish:"Spanish"},slackLabels:{english:"English speaker",spanish:"Spanish speaker"},categoryName:"language",sectionLabel:"Language",tooltip:"Tester must speak all selected languages"};var K=["daily","weekly","biweekly","monthly","once"],ye={daily:"Daily",weekly:"Weekly",biweekly:"Biweekly",monthly:"Monthly",once:"One-time"};var be={0:"Sun",1:"Mon",2:"Tue",3:"Wed",4:"Thu",5:"Fri",6:"Sat"};function Me(){let i=new Bn("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 by name (resolved from repo, project, or built-in)").option("--template-file <path>","Path to a .md template file (YAML frontmatter + markdown)").option("--duration <minutes>","Target duration (1-60 minutes)",parseInt).option("--device-class <class>","Device class: desktop|mobile").option("--schema <file>","Path to JSON schema file for structured output").option("--schema-inline <json>","Inline JSON schema").option("--metadata <json>","Metadata for tracking (JSON string)").option("--github-repo <owner/repo>","GitHub repo for context").option("--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("--require-apk-install","Require tester to be able to install APK files").option("--auto-create-github-issues","Automatically create GitHub issues from AI-extracted findings (requires --github-repo)").option("--max-extension-minutes <minutes>","Total extension time in minutes (default: 15, 0 to disable)",parseInt).option("--max-extension-count <count>","Number of extensions (default: 3, 0 to disable)",parseInt).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}),r=new u({json:e.json,quiet:e.quiet,color:o.color}),c=new y(o);if(e.template&&e.templateFile)throw new U("Cannot use both --template and --template-file. Choose one.");let p=e.template||e.templateFile;if(!t&&!p&&!o.defaultUrl)throw new U("URL is required (provide as argument, via --template/--template-file, or set defaultUrl in config)");if(!e.description&&!p)throw new U("Description is required (use -d flag or --template/--template-file)");let l=C("project",e.project,o,r),d={projectId:await w(c,"project",l),url:t||o.defaultUrl||void 0,description:e.description||void 0,targetDurationMinutes:e.duration||o.defaultDuration,deviceClass:e.deviceClass||o.defaultDeviceClass};if(e.schema){let j=await(await import("fs/promises")).readFile(e.schema,"utf-8");d.outputSchema=JSON.parse(j)}else e.schemaInline&&(d.outputSchema=JSON.parse(e.schemaInline));if(e.metadata&&(d.metadata=JSON.parse(e.metadata)),e.githubRepo&&(d.githubRepo=e.githubRepo),e.template&&(d.template=e.template),e.templateFile){let b=await import("fs/promises");d.templateContent=await b.readFile(e.templateFile,"utf-8")}if(e.requiredDevices){let b=e.requiredDevices.split(",");for(let j of b)if(!se.values.includes(j))throw new U(`Invalid device "${j}". Valid values: ${se.values.join(", ")}`);d.requiredDevices=b}if(e.requiredLanguages){let b=e.requiredLanguages.split(",");for(let j of b)if(!ce.values.includes(j))throw new U(`Invalid language "${j}". Valid values: ${ce.values.join(", ")}`);d.requiredLanguages=b}e.requireSocialVideos&&(d.requireSocialVideos=!0),e.requireApkInstall&&(d.requireApkInstall=!0),e.autoCreateGithubIssues&&(d.autoCreateGithubIssues=!0),e.maxExtensionMinutes!==void 0&&(d.maxExtensionMinutes=e.maxExtensionMinutes),e.maxExtensionCount!==void 0&&(d.maxExtensionCount=e.maxExtensionCount);let m=e.json?null:Yn("Creating QA test job...").start(),f=await c.createJob(d);if(m&&m.succeed("Job created successfully!"),e.json){let b=u.result({jobId:f.jobId,status:f.status,message:f.message,dashboardUrl:`${o.apiUrl}${F.jobSimple.build({jobId:f.jobId})}`,estimatedCompletionTime:f.estimatedCompletionTime});r.output(b)}else e.quiet?console.log(f.jobId):(r.detail("Job ID",f.jobId),r.detail("Status",f.status),d.url&&r.detail("URL",d.url),r.detail("Dashboard",`${o.apiUrl}${F.jobSimple.build({jobId:f.jobId})}`),console.log(),r.hints([["Track progress",`runhuman status ${f.jobId}`],["Wait for result",`runhuman wait ${f.jobId}`]]));if(e.sync){let{waitForJob:b}=await Promise.resolve().then(()=>(je(),De));await b(f.jobId,c,r,e.wait||300)}}catch(n){let o=g(n);new u({json:e.json,quiet:!1}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}x();k();T();import{Command as Xn}from"commander";A();oe();P();function ze(){let i=new Xn("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}),r=new u({json:e.json,color:o.color}),c=new y(o),p={projectId:e.project?await w(c,"project",e.project):void 0,organizationId:e.organization?await w(c,"organization",e.organization):void 0},l=await H(c,t,p,o,r),a=await c.getJob(l);if(e.json){let d=u.result(a);r.output(d)}else r.heading(`Job Status: ${l}`),r.detail("Status",r.formatStatus(a.status)),a.testerAlias&&r.detail("Tester",a.testerAlias),a.url&&r.detail("URL",a.url),a.description&&r.detail("Description",a.description),a.createdAt&&r.detail("Created",r.formatTimestamp(a.createdAt)),a.completedAt&&r.detail("Completed",r.formatTimestamp(a.completedAt)),a.testDurationSeconds&&r.detail("Duration",r.formatDuration(a.testDurationSeconds)),a.costUsd&&r.detail("Cost",`$${a.costUsd.toFixed(3)}`),a.deviceClass&&r.detail("Device Class",a.deviceClass),a.requiredDevices?.length&&r.detail("Required Devices",a.requiredDevices.join(", ")),a.requiredLanguages?.length&&r.detail("Required Langs",a.requiredLanguages.join(", ")),a.requireSocialVideos&&r.detail("Social Videos","required"),a.requireApkInstall&&r.detail("APK Install","required"),a.maxExtensionMinutes!==void 0&&a.maxExtensionCount!==void 0&&a.extensionsUsed!==void 0&&r.detail("Extensions",`${a.extensionsUsed}/${a.maxExtensionCount} used (${a.maxExtensionMinutes} min total)`),r.detail("Dashboard",`${o.apiUrl}${F.jobSimple.build({jobId:l})}`),console.log(),a.status==="pending"||a.status==="queued"||a.status==="waiting"||a.status==="working"?r.hints([["Wait for completion",`runhuman wait ${l}`]]):a.status==="completed"&&r.hints([["View results",`runhuman results ${l}`]])}catch(n){let o=new u({json:e.json}),r=g(n);o.outputError(r.message,r.details),process.exit(r.exitCode)}}),i}je();x();k();T();A();oe();P();import{Command as Zn}from"commander";var at="=".repeat(60);function N(i){return`
|
|
31
|
+
${at}
|
|
32
32
|
${i}
|
|
33
|
-
${
|
|
34
|
-
`}function
|
|
35
|
-
`;let e=t.map(
|
|
33
|
+
${at}
|
|
34
|
+
`}function ue(i){return new Date(i).toLocaleTimeString()}function Qn(i){let t=Math.floor(i/60),e=Math.floor(i%60);return`${String(t).padStart(2,"0")}:${String(e).padStart(2,"0")}`}function st(i){let t=i.transcription?.sessions.flatMap(n=>n.segments)??[];if(t.length===0)return N("Transcript")+` No transcript available
|
|
35
|
+
`;let e=t.map(n=>{let o=n.words[0];return` [${o?Qn(o.start):"00:00"}] ${n.text}`});return N("Transcript")+e.join(`
|
|
36
36
|
`)+`
|
|
37
|
-
`}function
|
|
38
|
-
`;let e=t.map(o=>{let
|
|
37
|
+
`}function ct(i){let t=i.testerData?.consoleMessages;if(!t?.length)return N("Console Logs")+` No console logs recorded
|
|
38
|
+
`;let e=t.map(o=>{let r=ue(o.timestamp);return` [${o.type}] ${r} ${o.message}`}),n=N("Console Logs")+e.join(`
|
|
39
39
|
`)+`
|
|
40
|
-
`;if(i.testerData?.truncationInfo){let o=i.testerData.truncationInfo;
|
|
40
|
+
`;if(i.testerData?.truncationInfo){let o=i.testerData.truncationInfo;n+=`
|
|
41
41
|
(Truncated: ${JSON.stringify(o)})
|
|
42
|
-
`}return
|
|
43
|
-
`;let e=t.map(
|
|
42
|
+
`}return n}function lt(i){let t=i.testerData?.networkRequests;if(!t?.length)return N("Network Requests")+` No network requests recorded
|
|
43
|
+
`;let e=t.map(n=>{let o=ue(n.timestamp),r=n.status??"---";return` ${n.method} ${r} ${n.url} (${o})`});return N("Network Requests")+e.join(`
|
|
44
44
|
`)+`
|
|
45
|
-
`}function
|
|
46
|
-
`;let e=t.map(
|
|
45
|
+
`}function ut(i){let t=i.testerData?.events;if(!t?.length)return N("User Events")+` No events recorded
|
|
46
|
+
`;let e=t.map(n=>{let o=ue(n.timestamp),r;switch(n.type){case"click":r=`Click at (${n.x}, ${n.y})${n.element?` on ${n.element}`:""}`;break;case"page_load":r=`${n.event}: ${n.url}`;break;case"form_submit":r=`Form submit (${n.formInfo.fieldCount} fields)`;break;case"spa_route_change":r=`Route: ${n.route}`;break;case"keyboard_input":r=`Typed in ${n.elementInfo.tagName}`;break;case"tab_change":r=`Tab: ${n.title||n.url}`;break}return` [${n.type}] ${o} ${r}`});return N("User Events")+e.join(`
|
|
47
47
|
`)+`
|
|
48
|
-
`}function
|
|
49
|
-
`;let t=i.keyMoments.map(e=>{let
|
|
48
|
+
`}function pt(i){if(!i.keyMoments?.length)return N("Key Moments")+` No key moments available
|
|
49
|
+
`;let t=i.keyMoments.map(e=>{let n=ue(e.timestamp);return` [${e.type}] ${n} ${e.description} (significance: ${e.significance})`});return N("Key Moments")+t.join(`
|
|
50
50
|
`)+`
|
|
51
|
-
`}function
|
|
52
|
-
`;let t=i.conversationHistory.map(e=>{let
|
|
51
|
+
`}function dt(i){if(!i.conversationHistory?.length)return N("Conversation History")+` No conversation messages
|
|
52
|
+
`;let t=i.conversationHistory.map(e=>{let n=ue(e.timestamp),o=e.role.charAt(0).toUpperCase()+e.role.slice(1);return` [${n}] ${o}: ${e.content}`});return N("Conversation History")+t.join(`
|
|
53
53
|
`)+`
|
|
54
|
-
`}function
|
|
55
|
-
`)});return
|
|
54
|
+
`}function mt(i){if(!i.extractedIssues?.length)return"";let t=i.extractedIssues.map((e,n)=>{let o=[` ${n+1}. [${e.severity.toUpperCase()}] ${e.title}`,` ${e.description}`];if(e.reproductionSteps?.length&&(o.push(" Steps:"),e.reproductionSteps.forEach((r,c)=>{let p=typeof r=="string"?r:r.text;o.push(` ${c+1}. ${p}`)})),e.relatedIssues?.length){let r=e.relatedIssues.map(c=>`#${c.issueNumber} (${c.relation}, ${Math.round(c.confidence*100)}%)`).join(", ");o.push(` Related: ${r}`)}return o.join(`
|
|
55
|
+
`)});return N(`Extracted Issues (${i.extractedIssues.length})`)+t.join(`
|
|
56
56
|
|
|
57
57
|
`)+`
|
|
58
|
-
`}function
|
|
59
|
-
${e.issueUrl}`});return
|
|
58
|
+
`}function ft(i){if(!i.createdIssues?.length)return"";let t=i.createdIssues.map(e=>{let n=e.isNew?"new":"comment on existing",o=e.matchReason?` \u2014 ${e.matchReason}`:"";return` #${e.issueNumber}: ${e.title} (${n}${o})
|
|
59
|
+
${e.issueUrl}`});return N(`Created GitHub Issues (${i.createdIssues.length})`)+t.join(`
|
|
60
60
|
|
|
61
61
|
`)+`
|
|
62
|
-
`}function
|
|
62
|
+
`}function $e(){let i=new Zn("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}),r=new u({json:e.json,color:o.color}),c=new y(o),p={projectId:e.project?await w(c,"project",e.project):void 0,organizationId:e.organization?await w(c,"organization",e.organization):void 0},l=await H(c,t,p,o,r),a=await c.getJob(l);if(e.json){let b=u.result(a);r.output(b);return}if(r.heading(`Test Results: ${l}`),r.sectionHeader("Job Information"),r.detail("Job ID",a.id),r.detail("Status",a.status),a.testerAlias&&r.detail("Tester",a.testerAlias),a.url&&r.detail("URL",a.url),a.description&&r.detail("Description",a.description),a.createdAt&&r.detail("Created",r.formatTimestamp(a.createdAt)),a.completedAt&&r.detail("Completed",r.formatTimestamp(a.completedAt)),a.testDurationSeconds&&r.detail("Duration",r.formatDuration(a.testDurationSeconds)),a.costUsd&&r.detail("Cost",`$${a.costUsd.toFixed(3)}`),a.deviceClass&&r.detail("Device Class",a.deviceClass),a.requiredDevices?.length&&r.detail("Required Devices",a.requiredDevices.join(", ")),a.requiredLanguages?.length&&r.detail("Required Langs",a.requiredLanguages.join(", ")),a.requireSocialVideos&&r.detail("Social Videos","required"),a.requireApkInstall&&r.detail("APK Install","required"),a.result&&Object.keys(a.result).length>0&&!e.raw){r.sectionHeader("Structured Results (Extracted)");for(let[b,j]of Object.entries(a.result)){let E=typeof j=="object"?JSON.stringify(j,null,2):String(j);console.log(` ${b}:`.padEnd(30)+E)}}a.testerResponse&&!e.schemaOnly&&(r.sectionHeader("Tester Feedback"),console.log(" "+a.testerResponse.split(`
|
|
63
63
|
`).join(`
|
|
64
|
-
`)));let d=
|
|
65
|
-
`))}catch(
|
|
66
|
-
`),
|
|
64
|
+
`)));let d=mt(a);d&&console.log(d);let m=ft(a);m&&console.log(m);let f=e.all;(f||e.transcript)&&console.log(st(a)),(f||e.consoleLogs)&&console.log(ct(a)),(f||e.network)&&console.log(lt(a)),(f||e.events)&&console.log(ut(a)),(f||e.keyMoments)&&console.log(pt(a)),(f||e.conversation)&&console.log(dt(a)),r.separator(),console.log(),a.richResultsGated&&(console.log(" Rich test data (transcript, logs, network) requires a paid plan."),console.log(` Upgrade at https://runhuman.com/pricing
|
|
65
|
+
`))}catch(n){let o=g(n);new u({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}x();k();T();A();import{Command as er}from"commander";P();function _e(){let i=new er("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}),r=new u({json:e.json,format:e.format,color:o.color}),c=new y(o),p=C("project",e.project,o,r),l=await w(c,"project",p),a={limit:e.limit||20,offset:e.offset||0,projectId:l};t&&t!=="all"&&(a.status=t);let{items:d,pagination:m}=await c.listJobs(a);if(e.json){let f=u.result({jobs:d,pagination:m});r.output(f)}else{if(m.total===0){r.heading("Recent Jobs (0)"),console.log(`No jobs found.
|
|
66
|
+
`),r.hints([["Create a job",'runhuman create <url> -d "Test description"']]);return}r.heading(`Recent Jobs (${d.length} of ${m.total})`),console.log(r.formatJobList(d)),console.log(),r.hints([["View details","runhuman status <jobId>"],["View results","runhuman results <jobId>"]])}}catch(n){let o=new u({json:e.json}),r=g(n);o.outputError(r.message,r.details),process.exit(r.exitCode)}}),i}x();k();T();A();oe();P();import{Command as tr}from"commander";import or from"inquirer";function Le(){let i=new tr("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}),r=new u({json:e.json,color:o.color}),c=new y(o),p={projectId:e.project?await w(c,"project",e.project):void 0,organizationId:e.organization?await w(c,"organization",e.organization):void 0},l=await H(c,t,p,o,r);if(!e.json&&!e.force){let{confirmed:a}=await or.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete job ${l}? This cannot be undone.`,default:!1}]);if(!a)return}if(await c.deleteJob(l),e.json){let a=u.result({success:!0,message:"Job deleted successfully",jobId:l});r.output(a)}else r.success("Job deleted successfully"),r.detail("Job ID",l),console.log()}catch(n){let o=g(n);new u({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}x();k();T();import{Command as nr}from"commander";import rr from"chokidar";A();P();import{existsSync as Ie,mkdirSync as ir,readFileSync as Fe,writeFileSync as ar,unlinkSync as sr}from"fs";import{join as gt}from"path";import{homedir as ht}from"os";var G=gt(ht(),".config","runhuman","watch.pid");function Je(){let i=new nr("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 lr();return}if(e.status){ur();return}yt()&&(console.log(`
|
|
67
67
|
Watch mode is already running`),console.log(` Use --stop to stop it first
|
|
68
|
-
`),process.exit(1));let o=await new h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),
|
|
68
|
+
`),process.exit(1));let o=await new h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=o.watch||{},c=t.length>0?t:r.patterns||["src/**/*"],p=e.ignore||r.ignore||["**/node_modules/**","**/dist/**","**/build/**","**/.git/**"],l=e.debounce||r.debounce||2e3,a=e.url||r.url||o.defaultUrl,d=e.description||r.description||"Auto-test from watch mode",m=e.template||r.template;a||(console.log(`
|
|
69
69
|
\u274C Error: URL required
|
|
70
70
|
`),console.log(`Provide via --url flag or set in .runhumanrc:
|
|
71
71
|
`),console.log(' runhuman watch --url "https://myapp.com"'),console.log(` OR add to .runhumanrc: { "watch": { "url": "..." } }
|
|
72
|
-
`),process.exit(1));let
|
|
72
|
+
`),process.exit(1));let f=new u({color:o.color}),b=new y(o),j=C("project",void 0,o,f),E=await w(b,"project",j),S=m;f.heading("Starting watch mode"),f.detail("Watching",c.join(", ")),f.detail("Ignoring",p.join(", ")),f.detail("Debounce",`${l}ms`),f.detail("URL",a),m&&f.detail("Template",m),console.log(`
|
|
73
73
|
Press Ctrl+C to stop
|
|
74
|
-
`)
|
|
75
|
-
Creating test job for ${
|
|
76
|
-
`);try{let
|
|
74
|
+
`),cr(process.pid);let D=null,z=new Set,X=rr.watch(c,{ignored:p,persistent:!0,ignoreInitial:!0});X.on("change",Q=>{z.add(Q),console.log(` Changed: ${Q}`),D&&clearTimeout(D),D=setTimeout(async()=>{let Ge=Array.from(z);z.clear(),console.log(`
|
|
75
|
+
Creating test job for ${Ge.length} file(s)...
|
|
76
|
+
`);try{let Te={projectId:E,url:a,description:`${d}
|
|
77
77
|
|
|
78
78
|
Changed files:
|
|
79
|
-
${
|
|
80
|
-
`)}`,template:
|
|
79
|
+
${Ge.map(eo=>`- ${eo}`).join(`
|
|
80
|
+
`)}`,template:S},re=await b.createJob(Te);console.log(` Job created: ${re.jobId}`),console.log(` Dashboard: ${o.apiUrl}${F.jobSimple.build({jobId:re.jobId})}
|
|
81
81
|
`),console.log(` Watching for more changes...
|
|
82
|
-
`)}catch(
|
|
82
|
+
`)}catch(Te){let re=g(Te);f.outputError(re.message,re.details),console.log(`
|
|
83
83
|
Watching for more changes...
|
|
84
|
-
`)}},l)}),
|
|
85
|
-
\u274C Watch error: ${
|
|
86
|
-
`)});let
|
|
84
|
+
`)}},l)}),X.on("error",Q=>{console.error(`
|
|
85
|
+
\u274C Watch error: ${Q instanceof Error?Q.message:String(Q)}
|
|
86
|
+
`)});let He=()=>{console.log(`
|
|
87
87
|
|
|
88
88
|
Stopping watch mode...
|
|
89
|
-
`),
|
|
89
|
+
`),X.close(),pe(),process.exit(0)};process.on("SIGINT",He),process.on("SIGTERM",He)}catch(n){let o=g(n);new u({}).outputError(o.message,o.details),pe(),process.exit(o.exitCode)}}),i}function cr(i){let t=gt(ht(),".config","runhuman");Ie(t)||ir(t,{recursive:!0}),ar(G,i.toString())}function pe(){Ie(G)&&sr(G)}function yt(){if(!Ie(G))return!1;try{let i=parseInt(Fe(G,"utf-8"));return process.kill(i,0),!0}catch{return pe(),!1}}async function lr(){if(!Ie(G)){console.log(`
|
|
90
90
|
Watch mode is not running
|
|
91
|
-
`);return}try{let i=parseInt(
|
|
91
|
+
`);return}try{let i=parseInt(Fe(G,"utf-8"));process.kill(i,"SIGTERM"),pe(),console.log(`
|
|
92
92
|
Watch mode stopped
|
|
93
|
-
`)}catch{
|
|
93
|
+
`)}catch{pe(),console.log(`
|
|
94
94
|
Watch process not found (already stopped)
|
|
95
|
-
`)}}function
|
|
95
|
+
`)}}function ur(){if(yt()){let i=parseInt(Fe(G,"utf-8"));console.log(`
|
|
96
96
|
Watch mode is running`),console.log(` PID: ${i}`),console.log(`
|
|
97
97
|
Stop with: runhuman watch --stop
|
|
98
98
|
`)}else console.log(`
|
|
99
99
|
Watch mode is not running
|
|
100
|
-
`)}
|
|
101
|
-
Please visit: ${d}`)}}else console.log(`Please visit: ${d}`);console.log(""),console.log("Waiting for authentication...")});let c=setTimeout(()=>{
|
|
100
|
+
`)}x();k();A();T();import{Command as gr}from"commander";import pr from"http";import{URL as dr}from"url";import mr from"open";async function Ce(i){let{apiUrl:t,autoOpenBrowser:e=!0}=i;return new Promise((n,o)=>{let r=pr.createServer((p,l)=>{if(!p.url){l.writeHead(400),l.end("Bad request");return}let a=new dr(p.url,"http://localhost");if(a.pathname==="/callback"){let d=a.searchParams.get("token"),m=a.searchParams.get("email"),f=a.searchParams.get("projectId")||"",b=a.searchParams.get("error");if(b){l.writeHead(200,{"Content-Type":"text/html"}),l.end(bt(b)),r.close(),o(new Error(b));return}if(d&&m){l.writeHead(200,{"Content-Type":"text/html"}),l.end(fr(m)),r.close(),n({token:d,email:m,projectId:f});return}l.writeHead(400,{"Content-Type":"text/html"}),l.end(bt("Missing token or email in callback")),r.close(),o(new Error("Missing token or email in callback"));return}l.writeHead(404),l.end("Not found")});r.listen(0,"127.0.0.1",async()=>{let p=r.address();if(!p||typeof p=="string"){o(new Error("Failed to start local server"));return}let a=`http://127.0.0.1:${p.port}/callback`,d=`${t}/cli/auth?callback=${encodeURIComponent(a)}`;if(console.log(""),e){console.log("Opening browser for authentication...");try{await mr(d)}catch{console.log("Could not open browser automatically."),console.log(`
|
|
101
|
+
Please visit: ${d}`)}}else console.log(`Please visit: ${d}`);console.log(""),console.log("Waiting for authentication...")});let c=setTimeout(()=>{r.close(),o(new Error("Authentication timed out. Please try again."))},60*1e3);r.on("close",()=>clearTimeout(c)),r.on("error",p=>{clearTimeout(c),o(new Error(`Failed to start local server: ${p.message}`))})})}function fr(i){return`<!DOCTYPE html>
|
|
102
102
|
<html>
|
|
103
103
|
<head>
|
|
104
104
|
<title>Runhuman CLI - Authenticated</title>
|
|
@@ -140,7 +140,7 @@ Please visit: ${d}`)}}else console.log(`Please visit: ${d}`);console.log(""),con
|
|
|
140
140
|
<p class="close-note">You can close this tab and return to the terminal.</p>
|
|
141
141
|
</div>
|
|
142
142
|
</body>
|
|
143
|
-
</html>`}function
|
|
143
|
+
</html>`}function bt(i){return`<!DOCTYPE html>
|
|
144
144
|
<html>
|
|
145
145
|
<head>
|
|
146
146
|
<title>Runhuman CLI - Authentication Failed</title>
|
|
@@ -183,42 +183,46 @@ Please visit: ${d}`)}}else console.log(`Please visit: ${d}`);console.log(""),con
|
|
|
183
183
|
<div class="error">${i}</div>
|
|
184
184
|
</div>
|
|
185
185
|
</body>
|
|
186
|
-
</html>`}function
|
|
187
|
-
`)}catch(e){let
|
|
188
|
-
`);for(let l of
|
|
189
|
-
`);for(let l of
|
|
190
|
-
`),process.exit(0)),await new h().reset(e);let o=new
|
|
186
|
+
</html>`}function wt(){let i=new gr("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,n=await e.loadConfig(),o=new u({json:t.json,color:n.color});if(t.token)await hr(e,o,t.token,t.json);else{let r=t.browser!==!1&&n.autoOpenBrowser!==!1;await yr(e,o,n.apiUrl,r,t.json)}}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}async function hr(i,t,e,n){i.saveCredentials({accessToken:e});let o=await i.loadConfig({apiKey:e}),c=await new y(o).getCurrentUser();i.saveUserInfo(c),n?t.output(u.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 yr(i,t,e,n,o){o||console.log("Logging in to Runhuman...");let r=await Ce({apiUrl:e||"https://runhuman.com",autoOpenBrowser:n});i.saveCredentials({accessToken:r.token}),await i.set("project",r.projectId,!0);let c=await i.loadConfig({apiKey:r.token}),l=await new y(c).getCurrentUser();i.saveUserInfo(l),o?t.output(u.result({success:!0,user:l,projectId:r.projectId})):(console.log(""),t.success("Successfully logged in!"),t.detail("Email",l.email),t.detail("Account ID",l.accountId),t.detail("Default Project",r.projectId),console.log()),process.exit(0)}k();A();T();import{Command as br}from"commander";function jt(){let i=new br("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,n=new u({json:t.json});if(e.clearCredentials(),e.clearUserInfo(),t.json){let o=u.result({success:!0,message:"Logged out successfully"});n.output(o)}else n.success("Logged out successfully!"),console.log(` Credentials have been cleared.
|
|
187
|
+
`)}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}x();k();A();T();import{Command as wr}from"commander";function It(){let i=new wr("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 n=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new u({json:t.json}),c=await new y(n).getCurrentUser();if(t.json){let p=u.result({user:c});o.output(p)}else o.heading("Current User"),o.detail("Email",c.email),o.detail("Account ID",c.accountId),n.project&&(console.log(),o.detail("Default Project",n.project)),console.log()}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}k();A();T();import{Command as jr}from"commander";function Ct(){let i=new jr("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),r=new u({json:e.json});if(e.json){let c=u.result({[t]:o});r.output(c)}else console.log(o!==void 0?o:"(not set)")}catch(n){let o=g(n);new u({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,n)=>{try{await new h().set(t,e,n.global);let r=new u({json:n.json});if(n.json){let c=u.result({success:!0,key:t,value:e,scope:n.global?"global":"project"});r.output(c)}else r.success(`Set ${t} = ${e}`+(n.global?" (global)":" (project)"))}catch(o){let r=g(o);new u({json:n.json}).outputError(r.message,r.details),process.exit(r.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 n=await new h().list(),o=new u({json:t.json});if(t.json){let r=u.result(n);o.output(r)}else{o.heading("Configuration");let r=["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):
|
|
188
|
+
`);for(let l of r){let a=n.effective[l],d=n.env?.[l],m=n.project?.[l],f=n.global?.[l],b=l==="apiKey",j=D=>b&&!t.showSecrets?Ir(String(D)):String(D),E=d?"env":m?"project":f?"global":a?"default":null;console.log(` ${l}:`),console.log(a?` \u2713 ${j(a)} [from ${E}]`:" (not set)");let S=c[l];d&&console.log(` \u251C\u2500 env (${S}): ${j(d)}`),m&&console.log(` \u251C\u2500 project (.runhumanrc): ${j(m)}`),f&&console.log(` \u2514\u2500 global (~/.config/runhuman): ${j(f)}`),console.log()}let p=Object.keys(n.effective).filter(l=>!r.includes(l));if(p.length>0){console.log(`Other settings:
|
|
189
|
+
`);for(let l of p){let a=n.effective[l];a!==void 0&&console.log(` ${l}:`.padEnd(24)+String(a))}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 n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.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.
|
|
190
|
+
`),process.exit(0)),await new h().reset(e);let o=new u({json:t.json});if(t.json){let r=u.result({success:!0,scope:e});o.output(r)}else o.success(`Reset ${e} configuration`)}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}function Ir(i){return i.length<=8?"****":i.substring(0,4)+"*".repeat(i.length-8)+i.substring(i.length-4)}A();T();import{Command as Cr}from"commander";import Sr from"inquirer";import{writeFileSync as Ar}from"fs";import{join as Tr}from"path";function St(){let i=new Cr("init");return i.description("Initialize a new Runhuman project with configuration").option("--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 u({json:t.json});!t.json&&!t.yes&&(console.log(`Welcome to Runhuman!
|
|
191
191
|
`),console.log("Let's set up your project. This will create:"),console.log(" - A configuration file (.runhumanrc)"),console.log(` - Setup your project defaults
|
|
192
|
-
`));let
|
|
192
|
+
`));let n=t.name,o=t.url,r=t.githubRepo;if(!t.yes&&!t.json){let l=await Sr.prompt([{type:"input",name:"projectName",message:"Project name:",default:"My Project",when:!n},{type:"input",name:"defaultUrl",message:"Default URL (optional):",when:!o},{type:"input",name:"githubRepo",message:"Link to GitHub repository (optional, e.g., owner/repo):",when:!r}]);n=n||l.projectName,o=o||l.defaultUrl,r=r||l.githubRepo}let c={defaultUrl:o||void 0,githubRepo:r||void 0,defaultDuration:5,defaultDeviceClass:"desktop"},p=Tr(process.cwd(),".runhumanrc");if(Ar(p,JSON.stringify(c,null,2)),t.json){let l=u.result({success:!0,configPath:p,config:c});e.output(l)}else e.success("Project initialized successfully!"),console.log(` Configuration saved to: .runhumanrc
|
|
193
193
|
`),console.log(` All set! Try creating your first test:
|
|
194
194
|
`),console.log(` runhuman create https://example.com -d "Test homepage"
|
|
195
195
|
`),console.log(` Learn more: https://runhuman.com/docs/cli
|
|
196
|
-
`)}catch(e){let
|
|
197
|
-
`),o.hints([["Create a project","runhuman projects create <name> -o <org-id>"]]);return}o.heading(`Your Projects (${l.length} of ${a.total})`);let
|
|
198
|
-
Showing ${j}-${I} of ${a.total} projects`);let S=[["View project","runhuman projects show <id>"],["Filter by org","runhuman orgs projects <org-id>"]];a.hasMore&&S.push(["Next page",`runhuman projects list --offset ${u+c}`]),console.log(),o.hints(S)}}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}),c=new b(o),u=T("organization",e.organization,o,n),l=await y(c,"organization",u),a=await c.createProject({name:t,organizationId:l,defaultUrl:e.defaultUrl,githubRepo:e.githubRepo});if(e.setDefault&&await r.set("project",a.id,!1),e.json){let d=p.result(a);n.output(d)}else n.success("Project created successfully!"),console.log(" Project ID: "+a.id),console.log(" Name: "+a.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}),c=new b(o),u=await se(c,t,{},o,n),l=await c.getProject(u),[a,d]=await Promise.all([c.getOrganization(l.organizationId),c.getOrganizationBilling(l.organizationId)]);if(e.json){let m=p.result({project:l,organization:a,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",a.name+" ("+a.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 "+a.id],["List org projects","runhuman orgs projects "+a.id],["Check org balance","runhuman orgs balance "+a.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}),c=new b(o),u=await se(c,t,{},o,n),l=await c.getProject(u);if(await r.set("project",u,e.global!==!1),e.json){let a=p.result({success:!0,projectId:u,projectName:l.name,scope:e.global!==!1?"global":"local"});n.output(a)}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("--name <text>","Update name").option("--default-url <url>","Update default URL").option("--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},u=new b(o),l=await se(u,t,{},o,n),a=await u.updateProject(l,c);if(e.json){let d=p.result(a);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}),c=new b(o),u=await se(c,t,{},o,n),l=await y(c,"organization",e.toOrg,{requireFullId:{reason:"transfer target"}}),[a,d]=await Promise.all([c.getProject(u),c.getOrganization(l)]);if(!e.json&&!e.force){let{confirmed:g}=await ht.prompt([{type:"confirm",name:"confirmed",message:`Transfer "${a.name}" to organization "${d.name}"? The current organization will lose access to this project.`,default:!1}]);if(!g)return}let m=await c.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",a.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}),c=new b(o),u=await y(c,"project",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.force){let{confirmed:l}=await ht.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 c.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}P();U();A();x();R();import{Command as ir}from"commander";import yt from"cli-table3";function jt(){let i=new ir("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:c,total:u}=await n.listOrganizations({limit:t.limit||20});if(t.json){let l=p.result({organizations:c,pagination:{total:u,limit:t.limit||20,offset:0,hasMore:c.length<u}});o.output(l)}else{o.heading(`Your Organizations (${c.length} of ${u})`);let l=new yt({head:o.tableHead(["Organization ID","Name","Projects","Created"]),colWidths:[25,30,12,15]});c.forEach(a=>{l.push([a.id,a.name,a.projectCount?.toString()||"0",o.formatShortDate(a.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}),c=new b(o),u=await y(c,"organization",t),l=await c.getOrganization(u);if(e.json){let a=p.result(l);n.output(a)}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}),c=new b(o),u=await y(c,"organization",t),[l,a]=await Promise.all([c.getOrganization(u),c.getOrganizationBilling(u)]);if(e.json){let d=p.result({balance:a});n.output(d)}else n.heading(`Organization Balance: ${l.name}`),n.detail("Current Balance","$"+a.balanceUsd.toFixed(2)),n.detail("Billing Active",a.hasBilling?"Yes":"No"),n.detail("Estimated Tests","~"+Math.floor(a.balance)+" tests at $1.00 per test"),console.log(),a.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}),c=new b(o),u=await y(c,"organization",t),l=await c.getOrganization(u);if(await r.set("organization",u,e.global!==!1),e.json){let a=p.result({success:!0,organizationId:u,organizationName:l.name,scope:e.global!==!1?"global":"local"});n.output(a)}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}),c=new b(o),u=await y(c,"organization",t),[l,{items:a,total:d}]=await Promise.all([c.getOrganization(u),c.listOrganizationProjects(u,{limit:e.limit||50})]);if(e.json){let m=p.result({organizationId:u,projects:a,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.
|
|
199
|
-
`),
|
|
200
|
-
`),o.hints([["Create a key",'runhuman keys create "Key Name" --organization '+
|
|
196
|
+
`)}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}x();k();A();T();import{Command as Er}from"commander";import At from"inquirer";P();oe();import kr from"cli-table3";function Tt(){let i=new Er("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 n=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new u({json:t.json,color:n.color}),r=new y(n),c=t.limit||20,p=t.offset||0,[{items:l,pagination:a},{items:d}]=await Promise.all([r.listProjects({limit:c,offset:p}),r.listOrganizations({})]),m=new Map(d.map(f=>[f.id,f.name]));if(t.json){let f=u.result({projects:l,pagination:a});o.output(f)}else{if(a.total===0){o.heading("Your Projects (0)"),console.log(`No projects found.
|
|
197
|
+
`),o.hints([["Create a project","runhuman projects create <name> -o <org-id>"]]);return}o.heading(`Your Projects (${l.length} of ${a.total})`);let f=new kr({head:o.tableHead(["Name","Organization","Project ID","Created"]),colWidths:[25,25,25,15]});l.forEach(S=>{let D=m.get(S.organizationId)||"(unknown)";f.push([S.name,D,S.id,o.formatShortDate(S.createdAt)])}),console.log(f.toString());let b=p+1,j=p+l.length;console.log(`
|
|
198
|
+
Showing ${b}-${j} of ${a.total} projects`);let E=[["View project","runhuman projects show <id>"],["Filter by org","runhuman orgs projects <org-id>"]];a.hasMore&&E.push(["Next page",`runhuman projects list --offset ${p+c}`]),console.log(),o.hints(E)}}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.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 n=new h,o=await n.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new u({json:e.json,color:o.color}),c=new y(o),p=C("organization",e.organization,o,r),l=await w(c,"organization",p),a=await c.createProject({name:t,organizationId:l,defaultUrl:e.defaultUrl,githubRepo:e.githubRepo});if(e.setDefault&&await n.set("project",a.id,!1),e.json){let d=u.result(a);r.output(d)}else r.success("Project created successfully!"),console.log(" Project ID: "+a.id),console.log(" Name: "+a.name),e.setDefault&&console.log(" Set as default project"),console.log()}catch(n){let o=g(n);new u({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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=await le(c,t,{},o,r),l=await c.getProject(p),[a,d]=await Promise.all([c.getOrganization(l.organizationId),c.getOrganizationBilling(l.organizationId)]);if(e.json){let m=u.result({project:l,organization:a,balance:{balance:d.balance,balanceUsd:d.balanceUsd}});r.output(m)}else r.heading("Project Details"),r.detail("Project ID",l.id),r.detail("Name",l.name),r.detail("Organization",a.name+" ("+a.id+")"),r.detail("Org Balance","$"+d.balanceUsd.toFixed(2)),l.defaultUrl&&r.detail("Default URL",l.defaultUrl),l.githubRepo&&r.detail("GitHub Repo",l.githubRepo),r.detail("Created",r.formatTimestamp(l.createdAt)),r.detail("Last Updated",r.formatTimestamp(l.updatedAt)),console.log(),r.hints([["View organization","runhuman orgs show "+a.id],["List org projects","runhuman orgs projects "+a.id],["Check org balance","runhuman orgs balance "+a.id]])}catch(n){let o=g(n);new u({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 n=new h,o=await n.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new u({json:e.json,color:o.color}),c=new y(o),p=await le(c,t,{},o,r),l=await c.getProject(p);if(await n.set("project",p,e.global!==!1),e.json){let a=u.result({success:!0,projectId:p,projectName:l.name,scope:e.global!==!1?"global":"local"});r.output(a)}else r.success(`Default project set to: ${l.name} (${p})`),e.global!==!1?console.log(" Scope: global (all directories)"):console.log(" Scope: local (current directory only)"),console.log()}catch(n){let o=g(n);new u({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("--name <text>","Update name").option("--default-url <url>","Update default URL").option("--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}),r=new u({json:e.json,color:o.color}),c={name:e.name,defaultUrl:e.defaultUrl,githubRepo:e.githubRepo},p=new y(o),l=await le(p,t,{},o,r),a=await p.updateProject(l,c);if(e.json){let d=u.result(a);r.output(d)}else r.success("Project updated successfully!")}catch(n){let o=g(n);new u({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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=await le(c,t,{},o,r),l=await w(c,"organization",e.toOrg,{requireFullId:{reason:"transfer target"}}),[a,d]=await Promise.all([c.getProject(p),c.getOrganization(l)]);if(!e.json&&!e.force){let{confirmed:f}=await At.prompt([{type:"confirm",name:"confirmed",message:`Transfer "${a.name}" to organization "${d.name}"? The current organization will lose access to this project.`,default:!1}]);if(!f)return}let m=await c.initiateTransfer(p,l);e.json?r.output(u.result(m)):m.type==="immediate"?(r.success(`Project transferred to ${d.name}!`),r.detail("Project",m.project.name),r.detail("New Organization",d.name),console.log(),r.hints([["View project","runhuman projects show "+p]])):(r.success("Transfer request sent (pending approval)."),r.detail("Transfer ID",m.transfer.id),r.detail("Project",a.name),r.detail("To Organization",d.name),console.log(),r.hints([["Check status","runhuman transfers list"],["Cancel transfer","runhuman transfers cancel "+m.transfer.id]]))}catch(n){let o=g(n);new u({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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=await w(c,"project",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.force){let{confirmed:l}=await At.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete project ${p} and all its data? This cannot be undone.`,default:!1}]);if(!l)return}if(await c.deleteProject(p),e.json){let l=u.result({success:!0});r.output(l)}else r.success("Project deleted successfully!")}catch(n){let o=g(n);new u({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}x();k();A();T();P();import{Command as xr}from"commander";import Et from"cli-table3";function kt(){let i=new xr("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 n=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new u({json:t.json,color:n.color}),r=new y(n),{items:c,total:p}=await r.listOrganizations({limit:t.limit||20});if(t.json){let l=u.result({organizations:c,pagination:{total:p,limit:t.limit||20,offset:0,hasMore:c.length<p}});o.output(l)}else{o.heading(`Your Organizations (${c.length} of ${p})`);let l=new Et({head:o.tableHead(["Organization ID","Name","Projects","Created"]),colWidths:[25,30,12,15]});c.forEach(a=>{l.push([a.id,a.name,a.projectCount?.toString()||"0",o.formatShortDate(a.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 n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=await w(c,"organization",t),l=await c.getOrganization(p);if(e.json){let a=u.result(l);r.output(a)}else r.heading("Organization Details"),r.detail("Organization ID",l.id),r.detail("Name",l.name),r.detail("Created",r.formatTimestamp(l.createdAt)),console.log(),r.hints([["List projects","runhuman orgs projects "+l.id],["Check balance","runhuman orgs balance "+l.id]])}catch(n){let o=g(n);new u({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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=await w(c,"organization",t),[l,a]=await Promise.all([c.getOrganization(p),c.getOrganizationBilling(p)]);if(e.json){let d=u.result({balance:a});r.output(d)}else r.heading(`Organization Balance: ${l.name}`),r.detail("Current Balance","$"+a.balanceUsd.toFixed(2)),r.detail("Billing Active",a.hasBilling?"Yes":"No"),r.detail("Estimated Tests","~"+Math.floor(a.balance)+" tests at $1.00 per test"),console.log(),a.hasBilling?r.hints([["Manage billing","https://runhuman.com/dashboard/organizations/"+p+"/billing"]]):r.hints([["Set up billing","https://runhuman.com/dashboard/organizations/"+p+"/billing"]])}catch(n){let o=g(n);new u({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 n=new h,o=await n.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new u({json:e.json,color:o.color}),c=new y(o),p=await w(c,"organization",t),l=await c.getOrganization(p);if(await n.set("organization",p,e.global!==!1),e.json){let a=u.result({success:!0,organizationId:p,organizationName:l.name,scope:e.global!==!1?"global":"local"});r.output(a)}else r.success(`Default organization set to: ${l.name} (${p})`),e.global!==!1?console.log(" Scope: global (all directories)"):console.log(" Scope: local (current directory only)"),console.log()}catch(n){let o=g(n);new u({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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=await w(c,"organization",t),[l,{items:a,total:d}]=await Promise.all([c.getOrganization(p),c.listOrganizationProjects(p,{limit:e.limit||50})]);if(e.json){let m=u.result({organizationId:p,projects:a,total:d});r.output(m)}else{if(d===0){r.heading(`Projects in ${l.name} (0 projects)`),console.log(`No projects found in this organization.
|
|
199
|
+
`),r.hints([["Create a project","runhuman projects create <name> --organization "+p],["View organization","runhuman orgs show "+p]]);return}r.heading(`Projects in ${l.name} (${a.length} of ${d})`);let m=new Et({head:r.tableHead(["Name","Project ID","Default URL","Created"]),colWidths:[25,25,30,15]});a.forEach(f=>{let b=f.defaultUrl?f.defaultUrl.length>27?f.defaultUrl.substring(0,24)+"...":f.defaultUrl:"-";m.push([f.name,f.id,b,r.formatShortDate(f.createdAt)])}),console.log(m.toString()),console.log(),r.hints([["View project details","runhuman projects show <project-id>"],["Create new project","runhuman projects create <name> --organization "+p],["View organization","runhuman orgs show "+p]])}}catch(n){let o=g(n);new u({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}x();k();A();T();import{Command as vr}from"commander";import Ur from"inquirer";P();import Pr from"cli-table3";function vt(){let i=new vr("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 n=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new u({json:t.json,color:n.color}),r=new y(n),c=C("organization",t.organization,n,o),p=await w(r,"organization",c),{items:l,total:a}=await r.listApiKeys(p);if(t.json){let d=u.result({keys:l,total:a});o.output(d)}else{if(a===0){o.heading("API Keys (0)"),console.log(`No API keys found for this organization.
|
|
200
|
+
`),o.hints([["Create a key",'runhuman keys create "Key Name" --organization '+p]]);return}o.heading(`API Keys (${l.length} of ${a})`);let d=new Pr({head:o.tableHead(["Key ID","Name","Key","Last Used","Created"]),colWidths:[20,25,20,20,20]});l.forEach(m=>{let f=t.showKeys?m.key:xt(m.key),b=m.lastUsedAt?o.formatShortDate(m.lastUsedAt):"Never";d.push([m.id,m.name,f,b,o.formatShortDate(m.createdAt)])}),console.log(d.toString()),console.log(),o.hints([["Create new key",'runhuman keys create "Key Name" --organization '+p],["Show full key","runhuman keys show <keyId>"]])}}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=C("organization",e.organization,o,r),l=await w(c,"organization",p),a=await c.createApiKey(l,t);if(e.json){let d=u.result(a);r.output(d)}else r.success("API Key created successfully!"),r.detail("Key ID",a.id),r.detail("Name",a.name),console.log(),r.detail("API Key",a.key),console.log(" "+"^".repeat(a.key.length)),console.log(` \u26A0\uFE0F Save this key! It won't be shown again.
|
|
201
201
|
`),console.log(" Use this key:"),console.log(" export RUNHUMAN_API_KEY="+a.key),console.log(` runhuman create https://myapp.com -d "Test"
|
|
202
202
|
`),console.log(" Store securely:"),console.log(" - Use environment variables (recommended)"),console.log(" - Use secret management tools"),console.log(` - Never commit to git!
|
|
203
|
-
`)}catch(
|
|
204
|
-
`),o.hints([["Create a template",'runhuman templates create "Template Name" --project '+
|
|
205
|
-
Output Schema:`),console.log(JSON.stringify(d.outputSchema,null,2))),console.log(),n.hints([["Use this template",'runhuman create https://myapp.com --template "'+d.name+'"']])}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("--name <name>","New template name").option("-d, --description <text>","New description").option("--duration <minutes>","New target duration in minutes (1-60)",parseInt).option("--device-class <class>","New device class (desktop/mobile)").option("--schema <path>","Path to new JSON schema file").option("--schema-inline <json>","Inline JSON schema").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),u=T("project",e.project,o,n),l=await y(c,"project",u),a=await y(c,"template",t),d;if(e.schema){let{readFileSync:I}=await import("fs"),S=I(e.schema,"utf-8");d=JSON.parse(S)}else e.schemaInline&&(d=JSON.parse(e.schemaInline));let m={name:e.name,testDescription:e.description,targetDurationMinutes:e.duration,deviceClass:e.deviceClass,outputSchema:d},g=Object.fromEntries(Object.entries(m).filter(([,I])=>I!==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(l,a,m);if(e.json){let I=p.result(j);n.output(I)}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}),c=new b(o),u=T("project",e.project,o,n),l=await y(c,"project",u),a=await y(c,"template",t);if(!e.json&&!e.force){let{confirmed:d}=await ur.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete template ${a}? This cannot be undone.`,default:!1}]);if(!d)return}if(await c.deleteTemplate(l,a),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}P();U();A();x();import{Command as dr}from"commander";R();import St from"cli-table3";function At(){let i=new dr("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}),c=new b(o),u=T("project",e.project,o,n),l=await y(c,"project",u);if(!t.match(/^([^/]+)\/([^/]+)$/))throw new Error("Invalid repository format. Use: owner/repo");if(await c.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)
|
|
206
|
-
`)
|
|
203
|
+
`)}catch(n){let o=g(n);new u({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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=await w(c,"key",t),l=await c.getApiKey(p);if(e.json){let a=u.result(l);r.output(a)}else r.heading("API Key Details"),r.detail("Key ID",l.id),r.detail("Name",l.name),r.detail("API Key",e.showKey?l.key:xt(l.key)),r.detail("Created",r.formatTimestamp(l.createdAt)),l.lastUsedAt&&r.detail("Last Used",r.formatTimestamp(l.lastUsedAt)),console.log()}catch(n){let o=g(n);new u({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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=await w(c,"key",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.force){let{confirmed:l}=await Ur.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete API key ${p}? This cannot be undone.`,default:!1}]);if(!l)return}if(await c.deleteApiKey(p),e.json){let l=u.result({success:!0});r.output(l)}else r.success("API key deleted successfully!")}catch(n){let o=g(n);new u({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}function xt(i){return i.length<=12?"****":i.substring(0,8)+"*".repeat(i.length-12)+i.substring(i.length-4)}x();k();A();T();import{Command as Rr}from"commander";import Ut from"inquirer";P();import Or from"cli-table3";async function Nr(i,t,e){if(t.force)return!0;(!process.stdin.isTTY||t.json)&&(e.outputError(`A template named "${i}" already exists. Use --force to overwrite it.`),process.exit(7));let{overwrite:n}=await Ut.prompt([{type:"confirm",name:"overwrite",message:`A template named "${i}" already exists. Overwrite it?`,default:!1}]);return n}function Pt(){let i=new Rr("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 n=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new u({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),p=await w(r,"project",c),{items:l,pagination:a}=await r.listTemplates(p);if(t.json){let d=u.result({templates:l,pagination:a});o.output(d)}else{if(a.total===0){o.heading("Test Templates (0)"),console.log(`No templates found for this project.
|
|
204
|
+
`),o.hints([["Create a template",'runhuman templates create "Template Name" --project '+p]]);return}o.heading(`Test Templates (${l.length} of ${a.total})`);let d=new Or({head:o.tableHead(["ID","Name","Description","Created"]),colWidths:[30,25,35,20]});l.forEach(m=>{let f=m.testDescription&&m.testDescription.length>30?m.testDescription.substring(0,27)+"...":m.testDescription||"-";d.push([m.id,m.name,f,o.formatShortDate(m.createdAt)])}),console.log(d.toString()),console.log(),o.hints([["Create new template",'runhuman templates create "Template Name" --project '+p],["View template","runhuman templates show <templateId>"]])}}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.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 <minutes>","Target test duration in minutes (1-60)",parseInt).option("--device-class <class>","Device class (desktop/mobile)").option("--schema <path>","Path to JSON schema file").option("--schema-inline <json>","Inline JSON schema").option("-f, --force","Overwrite existing template with the same name").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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=C("project",e.project,o,r),l=await w(c,"project",p),a;if(e.schema){let{readFileSync:f}=await import("fs"),b=f(e.schema,"utf-8");a=JSON.parse(b)}else e.schemaInline&&(a=JSON.parse(e.schemaInline));let d={name:t,testDescription:e.description,targetDurationMinutes:e.duration,deviceClass:e.deviceClass,outputSchema:a},m;try{m=await c.createTemplate(l,d)}catch(f){if(!(f instanceof Z))throw f;if(!await Nr(t,e,r)){console.log("Cancelled.");return}let{items:j}=await c.listTemplates(l),E=j.find(S=>S.name.toLowerCase()===t.toLowerCase());if(!E)throw new Error(`Template "${t}" not found despite 409 conflict`);m=await c.updateTemplate(l,E.id,d),e.json?r.output(u.result(m)):(r.success("Template overwritten successfully!"),r.detail("Template ID",m.id),r.detail("Name",m.name));return}e.json?r.output(u.result(m)):(r.success("Template created successfully!"),r.detail("Template ID",m.id),r.detail("Name",m.name),m.testDescription&&r.detail("Description",m.testDescription),console.log(),r.hints([["Use this template",'runhuman create https://myapp.com --template "'+m.name+'"']]))}catch(n){let o=g(n);new u({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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=C("project",e.project,o,r),l=await w(c,"project",p),a=await w(c,"template",t),d=await c.getTemplate(l,a);if(e.json){let m=u.result(d);r.output(m)}else r.heading("Template Details"),r.detail("Template ID",d.id),r.detail("Name",d.name),r.detail("Description",d.testDescription||"-"),r.detail("Project",d.projectId),d.targetDurationMinutes&&r.detail("Duration",d.targetDurationMinutes+" minutes"),d.deviceClass&&r.detail("Device Class",d.deviceClass),r.detail("Created",r.formatTimestamp(d.createdAt)),d.outputSchema&&(console.log(`
|
|
205
|
+
Output Schema:`),console.log(JSON.stringify(d.outputSchema,null,2))),console.log(),r.hints([["Use this template",'runhuman create https://myapp.com --template "'+d.name+'"']])}catch(n){let o=g(n);new u({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("--name <name>","New template name").option("-d, --description <text>","New description").option("--duration <minutes>","New target duration in minutes (1-60)",parseInt).option("--device-class <class>","New device class (desktop/mobile)").option("--schema <path>","Path to new JSON schema file").option("--schema-inline <json>","Inline JSON schema").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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=C("project",e.project,o,r),l=await w(c,"project",p),a=await w(c,"template",t),d;if(e.schema){let{readFileSync:j}=await import("fs"),E=j(e.schema,"utf-8");d=JSON.parse(E)}else e.schemaInline&&(d=JSON.parse(e.schemaInline));let m={name:e.name,testDescription:e.description,targetDurationMinutes:e.duration,deviceClass:e.deviceClass,outputSchema:d},f=Object.fromEntries(Object.entries(m).filter(([,j])=>j!==void 0));if(Object.keys(f).length===0)throw new Error("No updates provided. Use --name, --description, --duration, --device-class, or --schema");let b=await c.updateTemplate(l,a,m);if(e.json){let j=u.result(b);r.output(j)}else r.success("Template updated successfully!"),r.detail("Template ID",b.id),r.detail("Name",b.name),console.log()}catch(n){let o=g(n);new u({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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=C("project",e.project,o,r),l=await w(c,"project",p),a=await w(c,"template",t);if(!e.json&&!e.force){let{confirmed:d}=await Ut.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete template ${a}? This cannot be undone.`,default:!1}]);if(!d)return}if(await c.deleteTemplate(l,a),e.json){let d=u.result({success:!0});r.output(d)}else r.success("Template deleted successfully!")}catch(n){let o=g(n);new u({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}x();k();A();T();import{Command as Gr}from"commander";P();function qe(i,t){return`${String(i).padStart(2,"0")}:${String(t).padStart(2,"0")}`}function Se(i){let t=i.match(/^(\d{1,2}):(\d{2})$/);if(!t)throw new Error(`Invalid time format "${i}". Use HH:MM (e.g., 09:00, 14:30).`);let e=parseInt(t[1],10),n=parseInt(t[2],10);if(e<0||e>23)throw new Error(`Hour must be 0-23, got ${e}.`);if(![0,15,30,45].includes(n))throw new Error(`Minute must be 0, 15, 30, or 45, got ${n}.`);return{hour:e,minute:n}}function Ae(i){let t={sun:0,mon:1,tue:2,wed:3,thu:4,fri:5,sat:6};return i.split(",").map(n=>n.trim().toLowerCase()).map(n=>{let o=t[n];if(o===void 0)throw new Error(`Unknown weekday "${n}". Use: sun, mon, tue, wed, thu, fri, sat.`);return o})}function Dr(i){return i.length===0?"-":i.map(t=>be[t]).join(", ")}function Ke(i,t,e,n){let o=ye[i];switch(i){case"weekly":case"biweekly":return t.length>0?`${o} on ${Dr(t)}`:o;case"monthly":return e===0?`${o} on last day`:e?`${o} on day ${e}`:o;case"once":return n?`${o} on ${n}`:o;default:return o}}function ne(i,t){i.detail("Schedule ID",t.id),i.detail("Name",t.name),t.templateName?i.detail("Template",t.templateName):i.detail("Template ID",t.templateId),i.detail("Status",t.status),i.detail("Frequency",Ke(t.frequency,t.weekdays,t.dayOfMonth,t.scheduledDate)),i.detail("Time",`${qe(t.scheduledHour,t.scheduledMinute)} ${t.timezone}`),i.detail("Next Run",i.formatTimestamp(t.nextExecutionAt)),t.lastExecutedAt&&i.detail("Last Run",i.formatTimestamp(t.lastExecutedAt)),t.urlOverride&&i.detail("URL Override",t.urlOverride),i.detail("Created",i.formatTimestamp(t.createdAt))}x();k();A();T();import{Command as Mr}from"commander";P();function Rt(){return new Mr("create").alias("new").description("Create a recurring test schedule").argument("<name>","Schedule name").requiredOption("-t, --template <id>","Template ID to run (required)").option("-p, --project <id>","Project ID (required)").option("--frequency <freq>","Frequency: daily, weekly, biweekly, monthly, once (default: weekly)").option("--time <HH:MM>","Time of day in 24h format, quarter-hour only (default: 09:00)").option("--timezone <tz>","IANA timezone (default: America/New_York)").option("--weekdays <days>","Comma-separated weekdays for weekly/biweekly (e.g., mon,wed,fri)").option("--day-of-month <n>","Day of month for monthly (1-28, or 0 for last day)",parseInt).option("--date <YYYY-MM-DD>","Specific date for one-time schedules").option("--url <url>","URL override for this schedule").option("--auto-github-issues","Auto-create GitHub issues from results").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(i,t)=>{try{let n=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new u({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),p=await w(r,"project",c),l=await w(r,"template",t.template),a=zr(t.frequency),{hour:d,minute:m}=t.time?Se(t.time):{hour:9,minute:0},f=t.timezone||"America/New_York",b={name:i,templateId:l,frequency:a,scheduledHour:d,scheduledMinute:m,timezone:f};t.weekdays&&(b.weekdays=Ae(t.weekdays)),t.dayOfMonth!==void 0&&(b.dayOfMonth=t.dayOfMonth),t.date&&(b.scheduledDate=t.date),t.url&&(b.urlOverride=t.url),t.autoGithubIssues&&(b.autoCreateGithubIssuesOverride=!0),$r(b);let j=await r.createSchedule(p,b);t.json?o.output(u.result(j)):(o.success("Schedule created successfully!"),ne(o,j),console.log(),o.hints([["View schedule",`runhuman schedules show ${j.id}`],["List schedules",`runhuman schedules list --project ${p}`]]))}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}function zr(i){if(!i)return"weekly";if(!K.includes(i))throw new U(`Invalid frequency "${i}". Must be one of: ${K.join(", ")}`);return i}function $r(i){if(i.frequency==="monthly"&&i.dayOfMonth===void 0)throw new U("Monthly schedules require --day-of-month (1-28, or 0 for last day).");if(i.frequency==="once"&&!i.scheduledDate)throw new U("One-time schedules require --date (YYYY-MM-DD).");if((i.frequency==="weekly"||i.frequency==="biweekly")&&(!i.weekdays||i.weekdays.length===0))throw new U(`${i.frequency} schedules require --weekdays (e.g., mon,wed,fri).`)}x();k();A();T();import{Command as _r}from"commander";P();function Ot(){return new _r("show").alias("info").alias("get").description("Show details of a specific schedule").argument("<scheduleId>","Schedule 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(i,t)=>{try{let n=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new u({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),p=await w(r,"project",c),l=await w(r,"schedule",i),a=await r.getSchedule(p,l);t.json?o.output(u.result(a)):(o.heading("Schedule Details"),ne(o,a),console.log(),o.hints([["View history",`runhuman schedules history ${a.id}`],["Pause schedule",`runhuman schedules pause ${a.id}`],["Update schedule",`runhuman schedules update ${a.id} --frequency daily`]]))}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}x();k();A();T();import{Command as Lr}from"commander";P();function Nt(){return new Lr("update").alias("edit").description("Update a schedule").argument("<scheduleId>","Schedule ID to update").option("-p, --project <id>","Project ID (required)").option("--name <name>","New schedule name").option("-t, --template <id>","New template ID").option("--frequency <freq>","New frequency: daily, weekly, biweekly, monthly, once").option("--time <HH:MM>","New time of day in 24h format, quarter-hour only").option("--timezone <tz>","New IANA timezone").option("--weekdays <days>","New comma-separated weekdays (e.g., mon,wed,fri)").option("--day-of-month <n>","New day of month (1-28, or 0 for last day)",parseInt).option("--date <YYYY-MM-DD>","New date for one-time schedules").option("--url <url>",'New URL override (use "" to clear)').option("--auto-github-issues <bool>","Auto-create GitHub issues (true/false)").option("--status <status>","Set status: active or paused").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(i,t)=>{try{let n=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new u({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),p=await w(r,"project",c),l=await w(r,"schedule",i),a=Fr(t);if(a.templateId&&(a.templateId=await w(r,"template",a.templateId)),Object.keys(a).length===0)throw new U("No updates provided. Use --name, --template, --frequency, --time, --timezone, --weekdays, --day-of-month, --date, --url, --status, or --auto-github-issues.");let d=await r.updateSchedule(p,l,a);t.json?o.output(u.result(d)):(o.success("Schedule updated successfully!"),ne(o,d),console.log())}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}function Fr(i){let t={};if(i.name&&(t.name=i.name),i.template&&(t.templateId=i.template),i.frequency){let e=i.frequency;if(!K.includes(e))throw new U(`Invalid frequency "${e}". Must be one of: ${K.join(", ")}`);t.frequency=e}if(i.time){let{hour:e,minute:n}=Se(i.time);t.scheduledHour=e,t.scheduledMinute=n}if(i.timezone&&(t.timezone=i.timezone),i.weekdays&&(t.weekdays=Ae(i.weekdays)),i.dayOfMonth!==void 0&&(t.dayOfMonth=i.dayOfMonth),i.date&&(t.scheduledDate=i.date),i.url!==void 0&&(t.urlOverride=i.url||null),i.autoGithubIssues!==void 0&&(t.autoCreateGithubIssuesOverride=i.autoGithubIssues==="true"),i.status){let e=i.status;if(e!=="active"&&e!=="paused")throw new U('Status must be "active" or "paused".');t.status=e}return t}x();k();A();T();import{Command as Jr}from"commander";import qr from"inquirer";P();function Dt(){return new Jr("delete").alias("rm").description("Delete a schedule permanently").argument("<scheduleId>","Schedule 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(i,t)=>{try{let n=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new u({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),p=await w(r,"project",c),l=await w(r,"schedule",i,{requireFullId:{reason:"deletion"}});if(!t.json&&!t.force){let{confirmed:a}=await qr.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete schedule ${l}? This cannot be undone.`,default:!1}]);if(!a)return}await r.deleteSchedule(p,l),t.json?o.output(u.result({success:!0})):o.success("Schedule deleted successfully!")}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}x();k();A();T();import{Command as Kr}from"commander";P();import Hr from"cli-table3";function Mt(){return new Kr("history").alias("executions").description("List execution history for a schedule").argument("<scheduleId>","Schedule ID").option("-p, --project <id>","Project ID (required)").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(i,t)=>{try{let n=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new u({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),p=await w(r,"project",c),l=await w(r,"schedule",i),a=t.limit||20,d=t.offset||0,{items:m,pagination:f}=await r.listScheduleExecutions(p,l,{limit:a,offset:d});if(t.json){o.output(u.result({executions:m,pagination:f}));return}if(f.total===0){o.heading("Execution History (0)"),console.log(`No executions recorded for this schedule yet.
|
|
206
|
+
`);return}o.heading(`Execution History (${m.length} of ${f.total})`);let b=new Hr({head:o.tableHead(["Execution ID","Status","Job ID","Scheduled For","Executed At"]),colWidths:[12,28,12,22,22]});for(let j of m)b.push([j.id.substring(0,8)+"\u2026",o.formatStatus(j.status),j.jobId?j.jobId.substring(0,8)+"\u2026":"-",o.formatTimestamp(j.scheduledAt),o.formatTimestamp(j.executedAt)]);console.log(b.toString()),f.hasMore?(console.log(`
|
|
207
|
+
Showing ${d+1}-${d+m.length} of ${f.total}`),o.hints([["Next page",`runhuman schedules history ${l} --offset ${d+a}`]])):console.log()}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}import Wr from"cli-table3";function $t(){let i=new Gr("schedules");return i.description("Manage recurring test schedules"),i.command("list").alias("ls").description("List schedules for a project").option("-p, --project <id>","Project ID (required)").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 n=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new u({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),p=await w(r,"project",c),l=t.limit||20,a=t.offset||0,{items:d,pagination:m}=await r.listSchedules(p,{limit:l,offset:a});if(t.json){o.output(u.result({schedules:d,pagination:m}));return}if(m.total===0){o.heading("Schedules (0)"),console.log(`No schedules found for this project.
|
|
208
|
+
`),o.hints([["Create a schedule",'runhuman schedules create "My Schedule" --template <id> --project '+p]]);return}o.heading(`Schedules (${d.length} of ${m.total})`);let f=new Wr({head:o.tableHead(["ID","Name","Template","Frequency","Time","Status","Next Run"]),colWidths:[12,18,18,22,8,10,20]});for(let b of d){let j=Ke(b.frequency,b.weekdays,b.dayOfMonth,b.scheduledDate);f.push([b.id.substring(0,8)+"\u2026",b.name.length>16?b.name.substring(0,13)+"\u2026":b.name,b.templateName.length>16?b.templateName.substring(0,13)+"\u2026":b.templateName,j.length>20?j.substring(0,17)+"\u2026":j,qe(b.scheduledHour,b.scheduledMinute),o.formatStatus(b.status),o.formatShortDate(b.nextExecutionAt)])}console.log(f.toString()),m.hasMore?(console.log(`
|
|
209
|
+
Showing ${a+1}-${a+d.length} of ${m.total}`),o.hints([["Next page",`runhuman schedules list --offset ${a+l} --project ${p}`],["View schedule","runhuman schedules show <scheduleId>"]])):(console.log(),o.hints([["View schedule","runhuman schedules show <scheduleId>"],["Create schedule",'runhuman schedules create "Name" --template <id> --project '+p]]))}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i.addCommand(Rt()),i.addCommand(Ot()),i.addCommand(Nt()),i.addCommand(Dt()),i.addCommand(Mt()),i.command("pause").description("Pause a schedule").argument("<scheduleId>","Schedule ID to pause").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)=>{await zt(t,"paused",e)}),i.command("resume").description("Resume a paused schedule").argument("<scheduleId>","Schedule ID to resume").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)=>{await zt(t,"active",e)}),i}async function zt(i,t,e){try{let o=await new h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new u({json:e.json,color:o.color}),c=new y(o),p=C("project",e.project,o,r),l=await w(c,"project",p),a=await w(c,"schedule",i),d=await c.updateSchedule(l,a,{status:t});if(e.json)r.output(u.result(d));else{let m=t==="paused"?"paused":"resumed";r.success(`Schedule ${m} successfully!`),r.detail("Schedule ID",d.id),r.detail("Name",d.name),r.detail("Status",d.status),console.log()}}catch(n){let o=g(n);new u({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}x();k();A();T();import{Command as Vr}from"commander";P();import _t from"cli-table3";function Lt(){let i=new Vr("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 n=new h,o=await n.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new u({json:e.json,color:o.color}),c=new y(o),p=C("project",e.project,o,r),l=await w(c,"project",p);if(!t.match(/^([^/]+)\/([^/]+)$/))throw new Error("Invalid repository format. Use: owner/repo");if(await c.updateProject(l,{githubRepo:t}),e.json){let d=u.result({success:!0,repository:t});r.output(d)}else r.success("GitHub repository linked successfully!"),r.detail("Repository",t),r.detail("Project",l),console.log(),await n.saveProjectConfig({githubRepo:t}),console.log(` Repository saved to project config (.runhumanrc)
|
|
210
|
+
`),r.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(n){let o=g(n);new u({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("--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 n=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new u({json:t.json,color:n.color}),r=new y(n),c=C("organization",t.organization,n,o),p=await w(r,"organization",c),l=await r.listGithubRepos(p,{search:t.search});if(t.json)o.output(u.result(l));else{o.heading(`GitHub Repositories (${l.items.length})`);let a=new _t({head:o.tableHead(["Repository","Added"]),colWidths:[45,20]});l.items.forEach(d=>{a.push([d.fullName,o.formatShortDate(d.createdAt)])}),console.log(a.toString()),console.log(),o.hints([["Test an issue","runhuman github test <issueNumber> --repo owner/repo"]])}}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i.command("issues").description("List GitHub issues for a repository").argument("<repo>","Repository in format owner/repo").option("--state <state>","Filter by state (open/closed/all)","open").option("-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}),r=new u({json:e.json,color:o.color}),c=t.match(/^([^/]+)\/([^/]+)$/);if(!c)throw new Error("Invalid repository format. Use: owner/repo");let[,p,l]=c,d=await new y(o).listGithubIssues(p,l,{state:e.state,labels:e.labels?.split(",")});if(e.json){let m=u.result({issues:d});r.output(m)}else{r.heading(`GitHub Issues for ${t} (${d.length})`);let m=new _t({head:r.tableHead(["#","Title","State","Labels","Created"]),colWidths:[8,40,10,20,15]});d.forEach(f=>{let b=f.labels?.join(", ")||"-",j=b.length>18?b.substring(0,15)+"...":b;m.push(["#"+f.number,f.title.length>38?f.title.substring(0,35)+"...":f.title,f.state,j,r.formatShortDate(f.createdAt)])}),console.log(m.toString()),console.log(),r.hints([["Test an issue","runhuman github test <issueNumber> --repo "+t]])}}catch(n){let o=g(n);new u({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}),r=e.repo||o.githubRepo;if(!r)throw new Error("Repository required (use --repo or set default with: runhuman github link)");if(!e.url)throw new Error("URL required (use --url)");let c=new u({json:e.json,color:o.color}),p=r.match(/^([^/]+)\/([^/]+)$/);if(!p)throw new Error("Invalid repository format. Use: owner/repo");let[,l,a]=p,d=new y(o),m=await d.getGithubIssue(l,a,parseInt(t)),f=C("project",void 0,o,c),j={projectId:await w(d,"project",f),url:e.url,description:`Test GitHub issue #${t}: ${m.title}
|
|
207
211
|
|
|
208
|
-
${m.body}`,metadata:{githubIssue:{owner:l,repo:a,number:parseInt(t),url:m.url}},template:e.template},
|
|
209
|
-
`);return}let
|
|
210
|
-
Creating ${
|
|
211
|
-
`);let
|
|
212
|
+
${m.body}`,metadata:{githubIssue:{owner:l,repo:a,number:parseInt(t),url:m.url}},template:e.template},E=await d.createJob(j);if(e.json){let S=u.result(E);c.output(S)}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:S}=await Promise.resolve().then(()=>(je(),De));await S(E.jobId,d,c,600)}}catch(n){let o=g(n);new u({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("--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 n=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=t.repo||n.githubRepo;if(!o)throw new Error("Repository required (use --repo or set default with: runhuman github link)");if(!t.url)throw new Error("URL required (use --url)");let r=new u({json:t.json,color:n.color}),c=o.match(/^([^/]+)\/([^/]+)$/);if(!c)throw new Error("Invalid repository format. Use: owner/repo");let[,p,l]=c,a=new y(n),d=await a.listGithubIssues(p,l,{state:t.state,labels:t.labels?.split(",")}),m=parseInt(t.limit),f=d.slice(0,m);if(f.length===0){console.log(`No issues found matching the criteria.
|
|
213
|
+
`);return}let b=C("project",void 0,n,r),j=await w(a,"project",b);console.log(`
|
|
214
|
+
Creating ${f.length} test jobs...
|
|
215
|
+
`);let E=[];for(let S of f){let D={projectId:j,url:t.url,description:`Test GitHub issue #${S.number}: ${S.title}
|
|
212
216
|
|
|
213
|
-
${
|
|
214
|
-
Created ${
|
|
215
|
-
`);else{let l=new
|
|
216
|
-
`);else{let l=new
|
|
217
|
-
Run `+
|
|
218
|
-
`);return}await
|
|
219
|
-
`),e.isRunhumanProject?await
|
|
220
|
-
Run `+
|
|
217
|
+
${S.body}`,metadata:{githubIssue:{owner:p,repo:l,number:S.number,url:S.url}},template:t.template};try{let z=await a.createJob(D);E.push({issue:S.number,jobId:z.jobId,status:"created"}),console.log(` ok Issue #${S.number} \u2192 Job ${z.jobId}`)}catch(z){let X=g(z);E.push({issue:S.number,error:X.message,status:"failed"}),console.log(` FAIL Issue #${S.number} \u2192 Failed: ${X.message}`)}}if(t.json){let S=u.result({jobs:E});r.output(S)}else{let S=E.filter(z=>z.status==="created").length,D=E.filter(z=>z.status==="failed").length;console.log(`
|
|
218
|
+
Created ${S} jobs`),D>0&&console.log(`Failed ${D} jobs`),console.log(),r.hints([["Check all jobs","runhuman list"]])}}catch(e){let n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}x();k();A();T();P();import{Command as Br}from"commander";import Ft from"cli-table3";function Jt(){let i=new Br("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 n=await new h().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=new u({json:t.json,color:n.color}),r=new y(n),[c,p]=await Promise.all([r.listPendingTransfers(),r.listOutgoingTransfers()]);if(t.json){let l=u.result({incoming:c.items,outgoing:p.items});o.output(l);return}if(o.heading(`Incoming Transfers (${c.total})`),c.items.length===0)console.log(`No pending incoming transfers.
|
|
219
|
+
`);else{let l=new Ft({head:o.tableHead(["Transfer ID","Project","From","To Org","Created"]),colWidths:[20,25,25,25,15]});c.items.forEach(a=>{l.push([a.id.substring(0,18),a.projectName,a.initiatedByEmail??"Unknown",a.toOrganizationName??"-",o.formatShortDate(a.createdAt)])}),console.log(l.toString()),console.log()}if(o.heading(`Outgoing Transfers (${p.total})`),p.items.length===0)console.log(`No pending outgoing transfers.
|
|
220
|
+
`);else{let l=new Ft({head:o.tableHead(["Transfer ID","Project","To Org","Status","Created"]),colWidths:[20,25,25,15,15]});p.items.forEach(a=>{l.push([a.id.substring(0,18),a.projectName,a.toOrganizationName??"-",a.status,o.formatShortDate(a.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 n=g(e);new u({json:t.json}).outputError(n.message,n.details),process.exit(n.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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=await w(c,"transfer",t);await c.acceptTransfer(p),e.json?r.output(u.result({success:!0,transferId:p})):(r.success("Transfer accepted!"),r.detail("Transfer ID",p),console.log())}catch(n){let o=g(n);new u({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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=await w(c,"transfer",t);await c.rejectTransfer(p),e.json?r.output(u.result({success:!0,transferId:p})):(r.success("Transfer rejected."),r.detail("Transfer ID",p),console.log())}catch(n){let o=g(n);new u({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}),r=new u({json:e.json,color:o.color}),c=new y(o),p=await w(c,"transfer",t);await c.cancelTransfer(p),e.json?r.output(u.result({success:!0,transferId:p})):(r.success("Transfer cancelled."),r.detail("Transfer ID",p),console.log())}catch(n){let o=g(n);new u({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}k();x();import{existsSync as Yr}from"fs";import{join as Xr}from"path";import{execSync as Qr}from"child_process";import Y from"inquirer";import O from"chalk";import Kt from"ora";T();async function Ht(){let i=new h,t=new u({color:!0});console.log(""),console.log(O.bold("Runhuman")+" - Human QA testing for your apps"),console.log("");let e=await qt(i);if(!e.isAuthenticated){let{shouldLogin:n}=await Y.prompt([{type:"confirm",name:"shouldLogin",message:"You're not logged in. Would you like to sign in?",default:!0}]);if(!n){console.log(`
|
|
221
|
+
Run `+O.cyan("runhuman login")+` when you're ready to sign in.
|
|
222
|
+
`);return}await Zr(i,t),Object.assign(e,await qt(i))}console.log(O.green("Logged in as "+e.userEmail)+`
|
|
223
|
+
`),e.isRunhumanProject?await ei(i,e):await ti(i,e)}async function qt(i){let t={isAuthenticated:!1,isGitRepo:!1,isRunhumanProject:!1},e=i.loadCredentials();if(e?.accessToken)try{let o=await i.loadConfig({apiKey:e.accessToken}),c=await new y(o).getCurrentUser();t.isAuthenticated=!0,t.userEmail=c.email}catch{t.isAuthenticated=!1}try{Qr("git rev-parse --is-inside-work-tree",{stdio:"ignore"}),t.isGitRepo=!0}catch{t.isGitRepo=!1}let n=Xr(process.cwd(),".runhumanrc");if(Yr(n)){t.isRunhumanProject=!0;try{let o=await i.loadConfig();t.projectConfig={defaultUrl:o.defaultUrl,githubRepo:o.githubRepo}}catch{}}return t}async function Zr(i,t){let e=await i.loadConfig(),n=e.apiUrl||"https://runhuman.com";console.log("");let o=Kt("Opening browser for authentication...").start();try{let r=await Ce({apiUrl:n,autoOpenBrowser:e.autoOpenBrowser!==!1});o.stop(),i.saveCredentials({accessToken:r.token});let c=await i.loadConfig({apiKey:r.token}),l=await new y(c).getCurrentUser();i.saveUserInfo(l),t.success("Successfully logged in!"),console.log("")}catch(r){throw o.stop(),r}}async function ei(i,t){console.log(O.dim("Runhuman project detected")),t.projectConfig?.defaultUrl&&console.log(O.dim("URL: "+t.projectConfig.defaultUrl)),console.log("");let{action:e}=await Y.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 Y.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await Gt(i,t);break;case"run-template":console.log(`
|
|
224
|
+
Run `+O.cyan("runhuman templates")+" to see available templates."),console.log("Then run "+O.cyan("runhuman create --template <name>")+` to use one.
|
|
221
225
|
`);break;case"list-jobs":console.log(`
|
|
222
|
-
Run `+
|
|
223
|
-
`);break;case"exit":break}}async function
|
|
224
|
-
Run: runhuman projects switch <project-id>`);return}let l=await new
|
|
226
|
+
Run `+O.cyan("runhuman list")+` to see your recent jobs.
|
|
227
|
+
`);break;case"exit":break}}async function ti(i,t){t.isGitRepo?console.log(O.dim("Git repository detected (not yet set up with Runhuman)")):console.log(O.dim("Not in a git repository")),console.log("");let{action:e}=await Y.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 Y.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await Gt(i,t);break;case"setup-repo":await oi(i);break;case"connect-github":await ni(i);break;case"exit":break}}async function Gt(i,t){let e=t.projectConfig?.defaultUrl||"",n=await Y.prompt([{type:"input",name:"url",message:"URL to test:",default:e||void 0,validate:r=>{if(!r.trim())return"URL is required";try{return new URL(r),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"description",message:"What should we test? (describe in plain English):",validate:r=>r.trim()?!0:"Description is required"}]),o=Kt("Creating test job...").start();try{let r=i.loadCredentials(),c=await i.loadConfig({apiKey:r?.accessToken});if(!c.project){o.fail("No project selected"),console.log(`
|
|
228
|
+
Run: runhuman projects switch <project-id>`);return}let l=await new y(c).createJob({projectId:c.project,url:n.url,description:n.description});o.succeed("Test job created!"),console.log(""),console.log(" Job ID: "+O.cyan(l.jobId)),console.log(" Status: "+l.status),console.log(""),console.log("A human tester will test your app shortly."),console.log("Run "+O.cyan(`runhuman wait ${l.jobId}`)+" to wait for results."),console.log("")}catch(r){throw o.fail("Failed to create test job"),r}}async function oi(i){console.log(""),console.log("Setting up Runhuman in the current repository..."),console.log("");let t=await Y.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(O.green("Created .runhumanrc")),console.log(""),console.log("Next steps:"),console.log(" 1. Install the GitHub App to enable @runhuman comments"),console.log(" "+O.cyan("runhuman github connect")),console.log(""),console.log(" 2. Create your first test:"),console.log(" "+O.cyan('runhuman create https://your-app.com -d "Test login flow"')),console.log("")}async function ni(i){let n=`${(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(" "+O.cyan(n)),console.log(""),console.log("After installation, you can comment "+O.bold("@runhuman")+" on any issue"),console.log("to trigger a QA test."),console.log("")}import{readFileSync as ri}from"fs";import{join as ii,dirname as ai}from"path";import{fileURLToPath as si}from"url";var ci=si(import.meta.url),li=ai(ci),ui=ii(li,"../package.json"),pi=JSON.parse(ri(ui,"utf-8")),di=pi.version,v=new Wt;v.name("runhuman").description("CLI for Runhuman - AI-orchestrated human QA testing").version(di);v.addCommand(Me());v.addCommand(ze());v.addCommand(we());v.addCommand($e());v.addCommand(_e());v.addCommand(Le());v.addCommand(Je());var L=new Wt("job");L.alias("jobs");L.description("Manage QA test jobs");L.addCommand(Me());L.addCommand(ze());L.addCommand(we());L.addCommand($e());L.addCommand(_e());L.addCommand(Le());L.addCommand(Je());v.addCommand(L);v.addCommand(wt());v.addCommand(jt());v.addCommand(It());v.addCommand(Ct());v.addCommand(St());var Vt=Tt();Vt.alias("project");v.addCommand(Vt);var Bt=kt();Bt.aliases(["org","organizations","organization"]);v.addCommand(Bt);var Yt=vt();Yt.alias("key");v.addCommand(Yt);var Xt=Pt();Xt.alias("template");v.addCommand(Xt);var Qt=$t();Qt.alias("schedule");v.addCommand(Qt);v.addCommand(Lt());var Zt=Jt();Zt.alias("transfer");v.addCommand(Zt);process.argv.slice(2).length?v.parse(process.argv):Ht().catch(i=>{console.error(i.message),process.exit(1)});
|