runhuman 1.10.1 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +97 -90
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,108 +1,112 @@
1
1
  #!/usr/bin/env node
2
- var fo=Object.defineProperty;var z=(i,t)=>()=>(i&&(t=i(i=0)),t);var ho=(i,t)=>{for(var e in t)fo(i,e,{get:t[e],enumerable:!0})};function bo(i){return i instanceof U}function f(i){return bo(i)?{message:i.message,exitCode:i.exitCode,details:i.details}:i instanceof Error?{message:i.message,exitCode:1}:{message:String(i),exitCode:1}}var U,pe,me,R,ge,fe,Q,he,S=z(()=>{"use strict";U=class extends Error{constructor(e,n=1,o){super(e);this.exitCode=n;this.details=o;this.name="CliError"}},pe=class extends U{constructor(t="Authentication failed",e){super(t,2,e),this.name="AuthenticationError"}},me=class extends U{constructor(t="Resource not found",e){super(t,3,e),this.name="NotFoundError"}},R=class extends U{constructor(t="Validation failed",e){super(t,4,e),this.name="ValidationError"}},ge=class extends U{constructor(t="Operation timed out",e){super(t,5,e),this.name="TimeoutError"}},fe=class extends U{constructor(t="Insufficient balance",e){super(t,6,e),this.name="InsufficientBalanceError"}},Q=class extends U{constructor(t="Resource conflict",e){super(t,7,e),this.name="ConflictError"}},he=class extends U{jobId;status;dashboardUrl;constructor(t,e,n,o){let r=`Job ${t} ended in terminal state "${e}"`,c=o?`${r}: ${o}`:r;super(c,8,{jobId:t,status:e,dashboardUrl:n,detail:o}),this.name="JobTerminalError",this.jobId=t,this.status=e,this.dashboardUrl=n}}});function a(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 be=z(()=>{"use strict"});var ye,Qe=z(()=>{"use strict";be();ye={dashboard:a("/dashboard"),managePlan:a("/dashboard/manage-plan"),plans:a("/dashboard/plans"),enterprisePlans:a("/dashboard/plans/enterprise"),jobSimple:a("/dashboard/jobs/:jobId"),admin:a("/admin"),adminSlack:a("/admin/slack"),adminJobs:a("/admin/jobs"),adminTests:a("/admin/tests"),adminTesters:a("/admin/testers"),adminActivity:a("/admin/activity"),adminSchedules:a("/admin/schedules"),adminBalances:a("/admin/balances"),adminUsers:a("/admin/users"),adminFeedback:a("/admin/feedback"),adminShortLinks:a("/admin/short-links"),adminPosts:a("/admin/posts"),organizations:a("/dashboard/organizations"),organization:a("/dashboard/organizations/:organizationId"),organizationMembers:a("/dashboard/organizations/:organizationId/members"),organizationSettings:a("/dashboard/organizations/:organizationId/settings"),organizationUsage:a("/dashboard/organizations/:organizationId/usage"),organizationProjects:a("/dashboard/organizations/:organizationId/projects"),organizationJobs:a("/dashboard/organizations/:organizationId/jobs"),organizationApiKeys:a("/dashboard/organizations/:organizationId/api-keys"),organizationIntegrations:a("/dashboard/organizations/:organizationId/integrations"),organizationGitHub:a("/dashboard/organizations/:organizationId/github"),organizationSso:a("/dashboard/organizations/:organizationId/sso"),organizationSlack:a("/dashboard/organizations/:organizationId/slack"),organizationSentry:a("/dashboard/organizations/:organizationId/sentry"),organizationPlayground:a("/dashboard/organizations/:organizationId/playground"),organizationTemplates:a("/dashboard/organizations/:organizationId/templates"),organizationSchedules:a("/dashboard/organizations/:organizationId/schedules"),organizationImportGitHub:a("/dashboard/organizations/:organizationId/import"),organizationAgentProfile:a("/dashboard/organizations/:organizationId/agent-profile"),settings:a("/dashboard/settings"),settingsAccount:a("/dashboard/settings/account"),settingsNotifications:a("/dashboard/settings/notifications"),tester:a("/tester"),testerApply:a("/tester/apply"),testerJobs:a("/tester/jobs"),testerHome:a("/tester/home"),testerSettings:a("/tester/settings"),testerScheduleView:a("/tester/schedule"),testerNotes:a("/tester/notes"),testerHandbook:a("/tester/handbook"),testerMyJobs:a("/tester/my-jobs"),testerTest:a("/tester/test/:jobId"),project:a("/dashboard/:projectId"),playground:a("/dashboard/:projectId/playground"),jobs:a("/dashboard/:projectId/jobs"),job:a("/dashboard/:projectId/jobs/:jobId"),templates:a("/dashboard/:projectId/templates"),template:a("/dashboard/:projectId/templates/:templateId"),issues:a("/dashboard/:projectId/issues"),issue:a("/dashboard/:projectId/issues/:issueNumber"),issueSessions:a("/dashboard/:projectId/issue-sessions"),issueSession:a("/dashboard/:projectId/issue-sessions/:sessionId"),projectSettings:a("/dashboard/:projectId/settings"),schedules:a("/dashboard/:projectId/schedules"),flowCharts:a("/dashboard/:projectId/flowcharts"),flowChart:a("/dashboard/:projectId/flowcharts/:flowchartId"),flowChartView:a("/view/flowchart/:projectId/:flowchartId"),invite:a("/invite/:token"),quickStart:a("/start"),publicJob:a("/j/:jobId/:token"),statuspage:a("/statuspage"),unsubscribe:a("/unsubscribe"),docs:a("/docs/setup"),pricing:a("/pricing"),home:a("/"),updates:a("/updates"),updateDetail:a("/updates/:postId"),learn:a("/learn"),learnLesson:a("/learn/:lessonSlug")}});var j,Ze=z(()=>{"use strict";be();j={jobs:a("/jobs"),job:a("/jobs/:jobId"),jobCancel:a("/jobs/:jobId/cancel"),jobCreateIssue:a("/jobs/:jobId/create-issue"),jobStatus:a("/jobs/:jobId/status"),jobArtifact:a("/jobs/:jobId/artifacts/:artifactType"),jobFeedback:a("/jobs/:jobId/feedback"),jobShare:a("/jobs/:jobId/share"),jobClaim:a("/jobs/:jobId/claim"),run:a("/run"),publicJob:a("/public/jobs/:jobId/:token"),testerJob:a("/tester/jobs/:jobId"),testerJobUploadUrls:a("/tester/jobs/:jobId/upload-urls"),testerJobProcessResults:a("/tester/jobs/:jobId/process-results"),testerJobProcessingStatus:a("/tester/jobs/:jobId/processing/:processingJobId"),testerJobExtractFrames:a("/tester/jobs/:jobId/extract-frames"),testerJobAbort:a("/tester/jobs/:jobId/abort"),testerJobRelease:a("/tester/jobs/:jobId/release"),testerJobRequestExtension:a("/tester/jobs/:jobId/request-extension"),testerJobInvalid:a("/tester/jobs/:jobId/invalid"),testerJobPhase:a("/tester/jobs/:jobId/phase"),testerJobRetranscribe:a("/tester/jobs/:jobId/retranscribe"),livekitTesterToken:a("/tester/jobs/:jobId/livekit-token"),livekitEndSession:a("/tester/jobs/:jobId/livekit-end"),livekitTranscript:a("/tester/jobs/:jobId/livekit-transcript"),testerLivekitRecording:a("/tester/jobs/:jobId/livekit-recording"),livekitViewerToken:a("/jobs/:jobId/livekit-viewer-token"),livekitStatus:a("/jobs/:jobId/livekit-status"),livekitRecording:a("/jobs/:jobId/livekit-recording"),keys:a("/keys"),key:a("/keys/:keyId"),keyRevoke:a("/keys/:keyId/revoke"),keyInfo:a("/key-info"),pats:a("/pats"),pat:a("/pats/:patId"),patRevoke:a("/pats/:patId/revoke"),patInfo:a("/pat-info"),projects:a("/projects"),project:a("/projects/:projectId"),projectJobs:a("/projects/:projectId/jobs"),projectApiKeys:a("/projects/:projectId/api-keys"),projectTransfer:a("/projects/:projectId/transfer"),projectTemplates:a("/projects/:projectId/templates"),projectTemplate:a("/projects/:projectId/templates/:templateId"),projectFlowCharts:a("/projects/:projectId/flowcharts"),projectFlowChartsUpload:a("/projects/:projectId/flowcharts/upload"),projectFlowChart:a("/projects/:projectId/flowcharts/:flowchartId"),projectFlowChartData:a("/projects/:projectId/flowcharts/:flowchartId/data"),projectFlowChartChat:a("/projects/:projectId/flowcharts/:flowchartId/chat"),projectFlowChartShare:a("/projects/:projectId/flowcharts/:flowchartId/share"),projectSchedules:a("/projects/:projectId/schedules"),projectSchedule:a("/projects/:projectId/schedules/:scheduleId"),projectScheduleExecutions:a("/projects/:projectId/schedules/:scheduleId/executions"),bulkCreateProjects:a("/projects/bulk"),githubOAuthAuthorize:a("/github/oauth/authorize"),githubCallback:a("/github/oauth/callback"),githubLink:a("/github/link"),githubIssuesByRepo:a("/github/issues/:owner/:repo"),githubIssueByRepo:a("/github/issues/:owner/:repo/:issueNumber"),githubIssues:a("/github/issues"),githubIssue:a("/github/issues/:issueNumber"),githubIssueComments:a("/github/issues/:issueNumber/comments"),githubIssueLabels:a("/github/issues/labels"),githubIssueAssignees:a("/github/issues/assignees"),githubIssueTest:a("/github/issues/test"),githubIssuesBulkTest:a("/github/issues/bulk-test"),githubTestSessions:a("/github/issues/test-sessions"),githubTestSession:a("/github/issues/test-sessions/:sessionId"),githubTestSessionSeen:a("/github/issues/test-sessions/:sessionId/seen"),githubTestSessionsCounts:a("/github/issues/test-sessions/counts"),githubBulkTest:a("/github/bulk-test"),githubWebhooks:a("/github/webhooks"),ottoGithubWebhook:a("/webhooks/otto-github"),authSync:a("/auth/sync"),authMe:a("/auth/me"),authStartup:a("/auth/startup"),authDeleteAccount:a("/auth/account"),authDeletionPreview:a("/auth/account/deletion-preview"),authAvatarUploadUrl:a("/auth/avatar-upload-url"),authAvatar:a("/auth/avatar"),billingBalance:a("/billing/balance"),billingHasCredits:a("/billing/has-credits"),billingPortal:a("/billing/portal"),billingCheckout:a("/billing/checkout"),billingSubscription:a("/billing/subscription"),billingChangePlan:a("/billing/change-plan"),billingEnterpriseCheck:a("/billing/enterprise-check"),billingAddonCheckout:a("/billing/addon/checkout"),billingAddonCancel:a("/billing/addon/cancel"),polarWebhooks:a("/polar/webhooks"),health:a("/health"),status:a("/status"),templates:a("/templates"),issueAnalyzer:a("/issue-analyzer"),prAnalyzer:a("/pr-analyzer"),logs:a("/logs"),relevantIssuesDiscover:a("/relevant-issues/discover"),relevantIssuesLatest:a("/relevant-issues/latest"),onboarding:a("/onboarding"),onboardingComplete:a("/onboarding/complete"),onboardingCheck:a("/onboarding/check"),search:a("/search"),testerApply:a("/tester/apply"),testerProfile:a("/tester/profile"),testerPublicProfile:a("/testers/:testerId/profile"),testerDownloadApp:a("/tester/download-app"),testerDownloadExtension:a("/tester/download-extension"),testerDownloadMobileApp:a("/tester/download-mobile-app"),testerAvailableJobs:a("/tester/jobs/available"),testerQueue:a("/tester/queue"),testerPresence:a("/tester/presence"),testerActiveJob:a("/tester/active-job"),testerActiveJobRelease:a("/tester/active-job/release"),testerAvatarUploadUrl:a("/tester/profile/avatar-upload-url"),testerConfig:a("/tester/config"),testerAppVersion:a("/tester/app-version"),testerVersions:a("/tester/versions"),testerInstallReleases:a("/tester/install-releases"),testerInstallReleasesMarkRead:a("/tester/install-releases/mark-read"),testerPushToken:a("/tester/push-token"),testerNotificationPreferences:a("/tester/notification-preferences"),testerDemoJob:a("/tester/demo-job"),testerSchedule:a("/tester/schedule"),adminTesterSchedules:a("/admin/testers/schedules"),adminTesterSchedule:a("/admin/testers/:testerId/schedule"),adminTesterScheduleOverrides:a("/admin/testers/schedule-overrides"),adminTesterScheduleOverride:a("/admin/testers/schedule-overrides/:overrideId"),testerNotes:a("/tester/notes"),testerNote:a("/tester/notes/:noteId"),testerNoteArchive:a("/tester/notes/:noteId/archive"),testerNoteOrgs:a("/tester/notes/orgs"),testerNoteProjects:a("/tester/notes/orgs/:organizationId/projects"),testerJobNotes:a("/tester/jobs/:jobId/notes"),extensionTokens:a("/tester/extension-tokens"),extensionToken:a("/tester/extension-tokens/:tokenId"),extensionStream:a("/extension/stream"),testerJobCorrelate:a("/tester/jobs/:jobId/correlate"),testerJobDataStatus:a("/tester/jobs/:jobId/data-status"),testerJobExtensionToken:a("/tester/jobs/:jobId/extension-token"),repoTemplates:a("/repos/:owner/:repo/templates"),repoTemplate:a("/repos/:owner/:repo/templates/:templateName"),organizations:a("/organizations"),organization:a("/organizations/:organizationId"),organizationMembers:a("/organizations/:organizationId/members"),organizationInvite:a("/organizations/:organizationId/invite"),organizationMember:a("/organizations/:organizationId/members/:userId"),organizationRestoreMember:a("/organizations/:organizationId/members/:userId/restore"),organizationProjects:a("/organizations/:organizationId/projects"),organizationJobs:a("/organizations/:organizationId/jobs"),organizationTransferOwnership:a("/organizations/:organizationId/transfer-ownership"),organizationApiKeys:a("/organizations/:organizationId/api-keys"),organizationApiKey:a("/organizations/:organizationId/api-keys/:keyId"),organizationGitHubInstallations:a("/organizations/:organizationId/github/installations"),organizationGitHubInstallation:a("/organizations/:organizationId/github/installations/:installationId"),organizationGitHubRepos:a("/organizations/:organizationId/github/repos"),organizationGitHubInstallationRefresh:a("/organizations/:organizationId/github/installations/:installationId/refresh"),organizationGitHubRepoCheckAccess:a("/organizations/:organizationId/github/repos/check-access"),organizationGitHubRepoFindUrl:a("/organizations/:organizationId/github/repos/find-url"),organizationTemplates:a("/organizations/:organizationId/templates"),organizationSchedules:a("/organizations/:organizationId/schedules"),organizationUsage:a("/organizations/:organizationId/usage"),organizationBilling:a("/organizations/:organizationId/billing"),transfersPending:a("/transfers/pending"),transfersOutgoing:a("/transfers/outgoing"),transferAccept:a("/transfers/:transferId/accept"),transferReject:a("/transfers/:transferId/reject"),transferCancel:a("/transfers/:transferId/cancel"),agreements:a("/agreements"),agreementCheck:a("/agreements/check"),organizationSlackInstallations:a("/organizations/:organizationId/slack/installations"),organizationSlackInstallation:a("/organizations/:organizationId/slack/installations/:slackIntegrationId"),organizationSlackOAuthAuthorize:a("/organizations/:organizationId/slack/oauth/authorize"),slackOAuthCallback:a("/slack/oauth/callback"),organizationSlackChannels:a("/organizations/:organizationId/slack/channels"),organizationSlackInstallationChannels:a("/organizations/:organizationId/slack/installations/:slackIntegrationId/channels"),organizationSlackDefaultChannel:a("/organizations/:organizationId/slack/installations/:slackIntegrationId/default-channel"),organizationSsoConnections:a("/organizations/:organizationId/sso/connections"),organizationSsoConnection:a("/organizations/:organizationId/sso/connections/:connectionId"),enterpriseInquiry:a("/enterprise/inquiry"),posts:a("/posts"),post:a("/posts/:postId"),postUnreadCount:a("/posts/unread-count"),postMarkRead:a("/posts/mark-read"),adminPosts:a("/admin/posts"),adminPost:a("/admin/posts/:postId"),adminPostPublish:a("/admin/posts/:postId/publish"),adminPostArchive:a("/admin/posts/:postId/archive"),adminPostUnarchive:a("/admin/posts/:postId/unarchive"),testSessions:a("/test-session/sessions"),testSessionEnd:a("/test-session/sessions/:sessionId/end"),testSessionEvents:a("/test-session/sessions/:sessionId/events"),testSessionJobStatus:a("/test-session/jobs/:jobId/status"),testSessionShortCode:a("/test-session/short-codes/:code"),organizationSentryConnections:a("/organizations/:organizationId/sentry/connections"),organizationSentryConnection:a("/organizations/:organizationId/sentry/connections/:sentryConnectionId"),organizationSentryTestConnection:a("/organizations/:organizationId/sentry/connections/:sentryConnectionId/test"),organizationSentryProjects:a("/organizations/:organizationId/sentry/connections/:sentryConnectionId/projects"),jobSentryEvents:a("/jobs/:jobId/sentry-events"),organizationAgentProfile:a("/organizations/:organizationId/agent-profile"),organizationAgentProfileAvailableRepos:a("/organizations/:organizationId/agent-profile/available-repos"),organizationAgentProfileGenerateObjectives:a("/organizations/:organizationId/agent-profile/generate-objectives"),organizationAgentProfileRunnerToken:a("/organizations/:organizationId/agent-profile/runner-token"),autonomyRunnerState:a("/autonomy-runner"),feedbackSubmissions:a("/feedback-submissions"),utmVisitors:a("/utm-visitors"),mppJobs:a("/mpp/jobs"),mppJobResult:a("/mpp/jobs/:jobId/result"),mppWalletDeposit:a("/mpp/wallet/deposit"),mppWalletBalance:a("/mpp/wallet/balance"),adminJob:a("/admin/jobs/:jobId"),adminJobReprocess:a("/admin/jobs/:jobId/reprocess"),adminJobProcessingStatus:a("/admin/jobs/:jobId/processing/:processingJobId"),testerMyJobs:a("/tester/my-jobs"),testerMyJobDetail:a("/tester/my-jobs/:jobId"),adminJobRetranscribe:a("/admin/jobs/:jobId/retranscribe"),adminJobReExtractIssues:a("/admin/jobs/:jobId/re-extract-issues"),adminJobTerminate:a("/admin/jobs/:jobId/terminate"),adminJobDuplicate:a("/admin/jobs/:jobId/duplicate"),adminJobReassign:a("/admin/jobs/:jobId/reassign"),notificationPreferences:a("/notifications/preferences"),notificationUnsubscribe:a("/notifications/unsubscribe"),notificationUnsubscribePreview:a("/notifications/unsubscribe/preview")}});var xe=z(()=>{"use strict";be();Qe();Ze()});import wo from"axios";var y,v=z(()=>{"use strict";S();xe();y=class{client;config;constructor(t){if(this.config=t,!t.apiUrl)throw new Error("apiUrl is required in CLI config");this.client=wo.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 pe(o,n);case 402:return new fe(o,n);case 404:return new me(o,n);case 409:return new Q(o,n);case 400:case 422:return new R(o,n);default:return new U(o,1,n)}}return t.code==="ECONNABORTED"?new U("Request timeout",5):t.code==="ENOTFOUND"||t.code==="ECONNREFUSED"?new U("Cannot connect to Runhuman API",1):new U(t.message,1)}get apiUrl(){return this.config.apiUrl}dashboardUrl(t){return`${this.apiUrl}${ye.jobSimple.build({jobId:t})}`}async createJob(t){return(await this.client.post(j.jobs.build(),t)).data}async getJob(t){return(await this.client.get(j.job.build({jobId:t}))).data}async listJobs(t){return(await this.client.get(j.jobs.build(),{params:t})).data}async cancelJob(t){await this.client.post(j.jobCancel.build({jobId:t}))}async deleteJob(t){await this.client.delete(j.job.build({jobId:t}))}async listProjects(t){return(await this.client.get(j.projects.build(),{params:t})).data}async getProject(t){return(await this.client.get(j.project.build({projectId:t}))).data}async createProject(t){return(await this.client.post(j.projects.build(),t)).data}async updateProject(t,e){return(await this.client.patch(j.project.build({projectId:t}),e)).data}async deleteProject(t){await this.client.delete(j.project.build({projectId:t}))}async listApiKeys(t){return(await this.client.get(j.organizationApiKeys.build({organizationId:t}))).data}async createApiKey(t,e){return(await this.client.post(j.organizationApiKeys.build({organizationId:t}),{name:e})).data}async getApiKey(t){return(await this.client.get(j.key.build({keyId:t}))).data}async deleteApiKey(t){await this.client.delete(j.key.build({keyId:t}))}async listOrganizations(t){return(await this.client.get(j.organizations.build(),{params:t})).data}async getOrganization(t){return(await this.client.get(j.organization.build({organizationId:t}))).data}async getOrganizationBilling(t){return(await this.client.get(j.organizationBilling.build({organizationId:t}))).data}async listOrganizationProjects(t,e){return(await this.client.get(j.organizationProjects.build({organizationId:t}),{params:e})).data}async listOrganizationMembers(t){return(await this.client.get(j.organizationMembers.build({organizationId:t}))).data}async inviteOrganizationMember(t,e){return(await this.client.post(j.organizationInvite.build({organizationId:t}),e)).data}async removeOrganizationMember(t,e){await this.client.delete(j.organizationMember.build({organizationId:t,userId:e}))}async listTemplates(t){return(await this.client.get(j.projectTemplates.build({projectId:t}))).data}async getTemplate(t,e){return(await this.client.get(j.projectTemplate.build({projectId:t,templateId:e}))).data}async createTemplate(t,e){return(await this.client.post(j.projectTemplates.build({projectId:t}),e)).data}async updateTemplate(t,e,n){return(await this.client.patch(j.projectTemplate.build({projectId:t,templateId:e}),n)).data}async deleteTemplate(t,e){await this.client.delete(j.projectTemplate.build({projectId:t,templateId:e}))}async listSchedules(t,e){return(await this.client.get(j.projectSchedules.build({projectId:t}),{params:e})).data}async createSchedule(t,e){return(await this.client.post(j.projectSchedules.build({projectId:t}),e)).data}async getSchedule(t,e){return(await this.client.get(j.projectSchedule.build({projectId:t,scheduleId:e}))).data}async updateSchedule(t,e,n){return(await this.client.patch(j.projectSchedule.build({projectId:t,scheduleId:e}),n)).data}async deleteSchedule(t,e){await this.client.delete(j.projectSchedule.build({projectId:t,scheduleId:e}))}async listScheduleExecutions(t,e,n){return(await this.client.get(j.projectScheduleExecutions.build({projectId:t,scheduleId:e}),{params:n})).data}async getCurrentUser(){return(await this.client.get(j.authMe.build())).data}async listGithubRepos(t,e){return(await this.client.get(j.organizationGitHubRepos.build({organizationId:t}),{params:e})).data}async listGithubIssues(t,e,n){return(await this.client.get(j.githubIssuesByRepo.build({owner:t,repo:e}),{params:n})).data}async getGithubIssue(t,e,n){return(await this.client.get(j.githubIssueByRepo.build({owner:t,repo:e,issueNumber:String(n)}))).data}async initiateTransfer(t,e){let n=await this.client.post(j.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(j.transfersPending.build())).data}async listOutgoingTransfers(){return(await this.client.get(j.transfersOutgoing.build())).data}async acceptTransfer(t){return(await this.client.post(j.transferAccept.build({transferId:t}),{})).data}async rejectTransfer(t){return(await this.client.post(j.transferReject.build({transferId:t}),{})).data}async cancelTransfer(t){return(await this.client.post(j.transferCancel.build({transferId:t}),{})).data}}});import{cosmiconfig as jo}from"cosmiconfig";import{homedir as Co}from"os";import{join as J}from"path";import{readFileSync as Z,writeFileSync as F,existsSync as M,mkdirSync as ke,chmodSync as So}from"fs";function Eo(i){return To.includes(i)}var Ao,L,G,ee,To,h,E=z(()=>{"use strict";Ao="runhuman",L=J(Co(),".config","runhuman"),G=J(L,"config.json"),ee=J(L,"credentials.json"),To=["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&&Eo(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 jo(Ao).search(this.cwd))?.config||null}catch{return null}}loadGlobalConfig(){try{if(!M(G))return null;let t=Z(G,"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(L)||ke(L,{recursive:!0});let n={};if(M(G)){let o=Z(G,"utf-8");n=JSON.parse(o)}n[t]=e,F(G,JSON.stringify(n,null,2))}async setProjectConfig(t,e){let n=J(this.cwd,".runhumanrc"),o={};if(M(n)){let r=Z(n,"utf-8");o=JSON.parse(r)}o[t]=e,F(n,JSON.stringify(o,null,2))}async saveProjectConfig(t){let e=J(this.cwd,".runhumanrc"),n={};if(M(e)){let r=Z(e,"utf-8");n=JSON.parse(r)}let o={...n,...t};F(e,JSON.stringify(o,null,2))}async list(){let t=await this.loadConfig();return{global:this.globalConfig,project:this.projectConfig,env:this.envConfig,effective:t}}async reset(t){if((t==="global"||t==="all")&&M(G)&&F(G,"{}"),t==="project"||t==="all"){let e=J(this.cwd,".runhumanrc");M(e)&&F(e,"{}")}}saveCredentials(t){M(L)||ke(L,{recursive:!0}),F(ee,JSON.stringify(t,null,2));try{process.platform!=="win32"&&So(ee,384)}catch{}}loadCredentials(){try{if(!M(ee))return null;let t=Z(ee,"utf-8");return JSON.parse(t)}catch{return null}}clearCredentials(){M(ee)&&F(ee,"{}")}saveUserInfo(t){let e=J(L,"user.json");M(L)||ke(L,{recursive:!0}),F(e,JSON.stringify(t,null,2))}loadUserInfo(){try{let t=J(L,"user.json");if(!M(t))return null;let e=Z(t,"utf-8");return JSON.parse(e)}catch{return null}}clearUserInfo(){let t=J(L,"user.json");M(t)&&F(t,"{}")}}});import V from"chalk";import vo from"cli-table3";var Ie,Pe,d,T=z(()=>{"use strict";Ie=24,Pe=60,d=class{constructor(t={}){this.options=t}output(t){this.options.json?console.log(JSON.stringify(t,null,2)):t.success?t.data&&!this.options.quiet&&this.outputPretty(t.data):this.outputError(t.error?.message||"An error occurred")}outputPretty(t){typeof t=="string"?console.log(t):(Array.isArray(t),console.log(JSON.stringify(t,null,2)))}outputError(t,e){if(this.options.json){let n={success:!1,error:{message:t,details:e},timestamp:new Date().toISOString()};console.error(JSON.stringify(n,null,2))}else console.error(this.color("red",`
2
+ var Uo=Object.defineProperty;var q=(i,t)=>()=>(i&&(t=i(i=0)),t);var _o=(i,t)=>{for(var e in t)Uo(i,e,{get:t[e],enumerable:!0})};function Do(i){return i instanceof _}function g(i){return Do(i)?{message:i.message,exitCode:i.exitCode,details:i.details}:i instanceof Error?{message:i.message,exitCode:1}:{message:String(i),exitCode:1}}var _,Te,Ae,A,X,Ee,ne,ve,T=q(()=>{"use strict";_=class extends Error{constructor(e,n=1,o){super(e);this.exitCode=n;this.details=o;this.name="CliError"}},Te=class extends _{constructor(t="Authentication failed",e){super(t,2,e),this.name="AuthenticationError"}},Ae=class extends _{constructor(t="Resource not found",e){super(t,3,e),this.name="NotFoundError"}},A=class extends _{constructor(t="Validation failed",e){super(t,4,e),this.name="ValidationError"}},X=class extends _{constructor(t="Operation timed out",e){super(t,5,e),this.name="TimeoutError"}},Ee=class extends _{constructor(t="Insufficient balance",e){super(t,6,e),this.name="InsufficientBalanceError"}},ne=class extends _{constructor(t="Resource conflict",e){super(t,7,e),this.name="ConflictError"}},ve=class extends _{jobId;status;dashboardUrl;constructor(t,e,n,o){let r=`Job ${t} ended in terminal state "${e}"`,c=o?`${r}: ${o}`:r;super(c,8,{jobId:t,status:e,dashboardUrl:n,detail:o}),this.name="JobTerminalError",this.jobId=t,this.status=e,this.dashboardUrl=n}}});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 ke=q(()=>{"use strict"});var xe,ut=q(()=>{"use strict";ke();xe={dashboard:s("/dashboard"),managePlan:s("/dashboard/manage-plan"),plans:s("/dashboard/plans"),enterprisePlans:s("/dashboard/plans/enterprise"),jobSimple:s("/dashboard/jobs/:jobId"),admin:s("/admin"),adminSlack:s("/admin/slack"),adminJobs:s("/admin/jobs"),adminTests:s("/admin/tests"),adminTesters:s("/admin/testers"),adminActivity:s("/admin/activity"),adminSchedules:s("/admin/schedules"),adminBalances:s("/admin/balances"),adminUsers:s("/admin/users"),adminFeedback:s("/admin/feedback"),adminShortLinks:s("/admin/short-links"),adminPosts:s("/admin/posts"),adminOttoChannels:s("/admin/otto-channels"),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"),organizationOttoGitHub:s("/dashboard/organizations/:organizationId/otto-github"),organizationPlayground:s("/dashboard/organizations/:organizationId/playground"),organizationTemplates:s("/dashboard/organizations/:organizationId/templates"),organizationSchedules:s("/dashboard/organizations/:organizationId/schedules"),organizationImportGitHub:s("/dashboard/organizations/:organizationId/import"),organizationAgentProfile:s("/dashboard/organizations/:organizationId/agent-profile"),settings:s("/dashboard/settings"),settingsAccount:s("/dashboard/settings/account"),settingsNotifications:s("/dashboard/settings/notifications"),tester:s("/tester"),testerApply:s("/tester/apply"),testerJobs:s("/tester/jobs"),testerHome:s("/tester/home"),testerSettings:s("/tester/settings"),testerScheduleView:s("/tester/schedule"),testerNotes:s("/tester/notes"),testerHandbook:s("/tester/handbook"),publicHandbookApplying:s("/handbook/applying"),publicHandbookPersona:s("/handbook/tester-persona"),testerMyJobs:s("/tester/my-jobs"),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"),inviteAccepted:s("/invite-accepted"),quickStart:s("/start"),publicJob:s("/j/:jobId/:token"),statuspage:s("/statuspage"),unsubscribe:s("/unsubscribe"),docs:s("/docs/setup"),pricing:s("/pricing"),home:s("/"),updates:s("/updates"),updateDetail:s("/updates/:postId"),learn:s("/learn"),learnLesson:s("/learn/:lessonSlug")}});var I,dt=q(()=>{"use strict";ke();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"),testerJobEnd:s("/tester/jobs/:jobId/end"),testerJobRequestExtension:s("/tester/jobs/:jobId/request-extension"),testerJobPhase:s("/tester/jobs/:jobId/phase"),testerJobRetranscribe:s("/tester/jobs/:jobId/retranscribe"),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"),ottoGithubWebhook:s("/webhooks/otto-github"),ottoGithubAppAuthorize:s("/otto/github/install/authorize"),ottoGithubAppCallback:s("/otto/github/install/callback"),ottoGithubAppWebhooks:s("/webhooks/otto-github-app"),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"),billingEnterpriseCheck:s("/billing/enterprise-check"),billingAddonCheckout:s("/billing/addon/checkout"),billingAddonCancel:s("/billing/addon/cancel"),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"),testerMobileAppVersions:s("/tester/mobile-app-versions"),testerAvailableJobs:s("/tester/jobs/available"),testerQueue:s("/tester/queue"),testerPresence:s("/tester/presence"),testerActiveJob:s("/tester/active-job"),testerActiveJobRelease:s("/tester/active-job/release"),testerAvatarUploadUrl:s("/tester/profile/avatar-upload-url"),testerConfig:s("/tester/config"),testerAppVersion:s("/tester/app-version"),testerVersions:s("/tester/versions"),testerInstallReleases:s("/tester/install-releases"),testerInstallReleasesMarkRead:s("/tester/install-releases/mark-read"),testerPushToken:s("/tester/push-token"),testerNotificationPreferences:s("/tester/notification-preferences"),testerDemoJob:s("/tester/demo-job"),testerActivity:s("/tester/activity"),testerSchedule:s("/tester/schedule"),adminTesterSchedules:s("/admin/testers/schedules"),adminTesterSchedule:s("/admin/testers/:testerId/schedule"),adminTesterScheduleOverrides:s("/admin/testers/schedule-overrides"),adminTesterScheduleOverride:s("/admin/testers/schedule-overrides/:overrideId"),adminTesterScheduleCsv:s("/admin/testers/schedule-csv"),testerNotes:s("/tester/notes"),testerNote:s("/tester/notes/:noteId"),testerNoteArchive:s("/tester/notes/:noteId/archive"),testerNoteOrgs:s("/tester/notes/orgs"),testerNoteProjects:s("/tester/notes/orgs/:organizationId/projects"),testerJobNotes:s("/tester/jobs/:jobId/notes"),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"),organizationInvitations:s("/organizations/:organizationId/invitations"),organizationInvitation:s("/organizations/:organizationId/invitations/:invitationId"),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"),organizationOttoGithubInstallations:s("/organizations/:organizationId/otto/github/installations"),organizationGitHubRepoFindUrl:s("/organizations/:organizationId/github/repos/find-url"),organizationTemplates:s("/organizations/:organizationId/templates"),organizationSchedules:s("/organizations/:organizationId/schedules"),organizationUsage:s("/organizations/:organizationId/usage"),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"),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"),posts:s("/posts"),post:s("/posts/:postId"),postUnreadCount:s("/posts/unread-count"),postMarkRead:s("/posts/mark-read"),adminPosts:s("/admin/posts"),adminPost:s("/admin/posts/:postId"),adminPostPublish:s("/admin/posts/:postId/publish"),adminPostArchive:s("/admin/posts/:postId/archive"),adminPostUnarchive:s("/admin/posts/:postId/unarchive"),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"),organizationAgentProfile:s("/organizations/:organizationId/agent-profile"),organizationAgentProfileAvailableRepos:s("/organizations/:organizationId/agent-profile/available-repos"),organizationAgentProfileGenerateObjectives:s("/organizations/:organizationId/agent-profile/generate-objectives"),organizationAgentProfileRunnerToken:s("/organizations/:organizationId/agent-profile/runner-token"),autonomyRunnerState:s("/autonomy-runner"),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"),adminJob:s("/admin/jobs/:jobId"),adminJobReprocess:s("/admin/jobs/:jobId/reprocess"),adminJobProcessingStatus:s("/admin/jobs/:jobId/processing/:processingJobId"),testerMyJobs:s("/tester/my-jobs"),testerMyJobDetail:s("/tester/my-jobs/:jobId"),adminJobRetranscribe:s("/admin/jobs/:jobId/retranscribe"),adminJobReExtractIssues:s("/admin/jobs/:jobId/re-extract-issues"),adminJobTerminate:s("/admin/jobs/:jobId/terminate"),adminJobReassign:s("/admin/jobs/:jobId/reassign"),adminJobUnarchive:s("/admin/jobs/:jobId/unarchive"),adminOttoChannelSettings:s("/admin/otto-channel-settings"),adminOttoChannelSettingsLookup:s("/admin/otto-channel-settings/lookup"),adminOttoChannelSetting:s("/admin/otto-channel-settings/:instanceKey/:channelId"),notificationPreferences:s("/notifications/preferences"),notificationUnsubscribe:s("/notifications/unsubscribe"),notificationUnsubscribePreview:s("/notifications/unsubscribe/preview")}});var Fe=q(()=>{"use strict";ke();ut();dt()});import Lo from"axios";var $o,y,R=q(()=>{"use strict";T();Fe();$o={default:3e4,jobCreate:12e4},y=class{client;config;timeouts;constructor(t,e=$o){if(this.config=t,this.timeouts=e,!t.apiUrl)throw new Error("apiUrl is required in CLI config");this.client=Lo.create({baseURL:t.apiUrl+"/api",timeout:e.default,headers:{"Content-Type":"application/json"}}),this.client.interceptors.request.use(n=>(this.config.apiKey&&(n.headers.Authorization=`Bearer ${this.config.apiKey}`),n.method==="delete"&&!n.data&&delete n.headers["Content-Type"],n)),this.client.interceptors.response.use(n=>n,n=>{throw this.handleError(n)})}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 Te(o,n);case 402:return new Ee(o,n);case 404:return new Ae(o,n);case 409:return new ne(o,n);case 400:case 422:return new A(o,n);default:return new _(o,1,n)}}return t.code==="ECONNABORTED"?this.buildTimeoutError(t):t.code==="ENOTFOUND"||t.code==="ECONNREFUSED"?new _("Cannot connect to Runhuman API",1):new _(t.message,1)}buildTimeoutError(t){let e=t.config?.meta?.operation,n=t.config?.timeout??this.timeouts.default,o=Math.round(n/1e3);return e==="createJob"?new X(`Job creation timed out after ${o}s. The job may have been created on the server \u2014 run \`runhuman list\` to check before retrying, or you may produce a duplicate.`):new X(`Request timed out after ${o}s`)}get apiUrl(){return this.config.apiUrl}dashboardUrl(t){return`${this.apiUrl}${xe.jobSimple.build({jobId:t})}`}async createJob(t){return(await this.client.post(I.jobs.build(),t,{timeout:this.timeouts.jobCreate,meta:{operation:"createJob"}})).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 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 listOrganizationMembers(t){return(await this.client.get(I.organizationMembers.build({organizationId:t}))).data}async inviteOrganizationMember(t,e){return(await this.client.post(I.organizationInvite.build({organizationId:t}),e)).data}async removeOrganizationMember(t,e){await this.client.delete(I.organizationMember.build({organizationId:t,userId:e}))}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}async listTesterNotes(t){return(await this.client.get(I.testerNotes.build(),{params:t?{...t,tags:t.tags&&t.tags.length>0?t.tags.join(","):void 0}:void 0})).data}async getTesterNote(t){return(await this.client.get(I.testerNote.build({noteId:t}))).data}async createTesterNote(t){return(await this.client.post(I.testerNotes.build(),t)).data}async updateTesterNote(t,e){return(await this.client.patch(I.testerNote.build({noteId:t}),e)).data}async archiveTesterNote(t){return(await this.client.post(I.testerNoteArchive.build({noteId:t}),{})).data}}});import{cosmiconfig as Fo}from"cosmiconfig";import{homedir as qo}from"os";import{join as H}from"path";import{readFileSync as re,writeFileSync as K,existsSync as F,mkdirSync as qe,chmodSync as Jo}from"fs";function Go(i){return Ho.includes(i)}var Ko,J,Q,ie,Ho,h,x=q(()=>{"use strict";Ko="runhuman",J=H(qo(),".config","runhuman"),Q=H(J,"config.json"),ie=H(J,"credentials.json"),Ho=["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&&Go(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 Fo(Ko).search(this.cwd))?.config||null}catch{return null}}loadGlobalConfig(){try{if(!F(Q))return null;let t=re(Q,"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){F(J)||qe(J,{recursive:!0});let n={};if(F(Q)){let o=re(Q,"utf-8");n=JSON.parse(o)}n[t]=e,K(Q,JSON.stringify(n,null,2))}async setProjectConfig(t,e){let n=H(this.cwd,".runhumanrc"),o={};if(F(n)){let r=re(n,"utf-8");o=JSON.parse(r)}o[t]=e,K(n,JSON.stringify(o,null,2))}async saveProjectConfig(t){let e=H(this.cwd,".runhumanrc"),n={};if(F(e)){let r=re(e,"utf-8");n=JSON.parse(r)}let o={...n,...t};K(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")&&F(Q)&&K(Q,"{}"),t==="project"||t==="all"){let e=H(this.cwd,".runhumanrc");F(e)&&K(e,"{}")}}saveCredentials(t){F(J)||qe(J,{recursive:!0}),K(ie,JSON.stringify(t,null,2));try{process.platform!=="win32"&&Jo(ie,384)}catch{}}loadCredentials(){try{if(!F(ie))return null;let t=re(ie,"utf-8");return JSON.parse(t)}catch{return null}}clearCredentials(){F(ie)&&K(ie,"{}")}saveUserInfo(t){let e=H(J,"user.json");F(J)||qe(J,{recursive:!0}),K(e,JSON.stringify(t,null,2))}loadUserInfo(){try{let t=H(J,"user.json");if(!F(t))return null;let e=re(t,"utf-8");return JSON.parse(e)}catch{return null}}clearUserInfo(){let t=H(J,"user.json");F(t)&&K(t,"{}")}}});import Z from"chalk";import Vo from"cli-table3";var Re,Je,d,v=q(()=>{"use strict";Re=24,Je=60,d=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
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}`))}deprecationWarnings(t){if(t.length!==0&&!(this.options.json||this.options.quiet))for(let e of t)process.stderr.write(this.color("yellow",`\u26A0 deprecation: ${e}`)+`
7
- `)}separator(){this.options.json||this.options.quiet||console.log("=".repeat(Pe))}sectionHeader(t){this.options.json||this.options.quiet||(console.log(`
8
- `+"=".repeat(Pe)),console.log(t),console.log("=".repeat(Pe)+`
9
- `))}detail(t,e){this.options.json||this.options.quiet||console.log(` ${t}:`.padEnd(Ie)+String(e))}heading(t){this.options.json||this.options.quiet||console.log(`
7
+ `)}warnings(t){if(t.length!==0&&!(this.options.json||this.options.quiet))for(let e of t)process.stderr.write(this.color("yellow",`\u26A0 warning: ${e}`)+`
8
+ `)}separator(){this.options.json||this.options.quiet||console.log("=".repeat(Je))}sectionHeader(t){this.options.json||this.options.quiet||(console.log(`
9
+ `+"=".repeat(Je)),console.log(t),console.log("=".repeat(Je)+`
10
+ `))}detail(t,e){this.options.json||this.options.quiet||console.log(` ${t}:`.padEnd(Re)+String(e))}heading(t){this.options.json||this.options.quiet||console.log(`
10
11
  ${t}
11
12
  `)}resultSummary(t){if(!(this.options.json||this.options.quiet))for(let[e,n]of Object.entries(t))if(typeof n=="object"&&n!==null){let r=JSON.stringify(n,null,2).split(`
12
13
  `).join(`
13
- `+" ".repeat(Ie));console.log(` ${e}:`.padEnd(Ie)+r)}else console.log(` ${e}:`.padEnd(Ie)+String(n))}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(`
14
- `);let e=new vo({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:"gray",preparing:"gray",queued:"gray",waiting:"gray",working:"blue",completed:"green",incomplete:"yellow",rejected:"yellow",abandoned:"yellow",error:"red",untestable:"red",failed:"red"},o=({preparing:"pending",waiting:"queued",abandoned:"expired",untestable:"error"}[t]??t).replaceAll("_"," ");return this.color(e[t]||"white",o)}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),u=Math.floor(c/60),l=Math.floor(u/24);return r<60?`${r}s ago`:c<60?`${c}m ago`:u<24?`${u}h ago`:l<30?`${l}d ago`:e.toLocaleDateString()}truncate(t,e){return t.length<=e?t:t.substring(0,e-3)+"..."}color(t,e){if(this.options.color===!1)return e;let o={red: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 W(i){return i.replace(/-/g,"").toLowerCase()}function Ne(i){let t=i.replace(/-/g,"");return No.test(t)}async function I(i,t,e,n){if(n?.requireFullId&&!Ne(e)){let l=tt[t],s=await l(i),p=W(e),m=s.filter(g=>W(g.id).startsWith(p));throw new Ue(e,t,n.requireFullId.reason,m)}if(Oe.test(e))return e;let o=tt[t],r=await o(i),c=W(e),u=r.filter(l=>W(l.id).startsWith(c));if(u.length===0)throw new ie(e,t);if(u.length===1)return u[0].id;throw new re(e,t,u)}var Oe,No,re,ie,Ue,tt,k=z(()=>{"use strict";S();Oe=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,No=/^[0-9a-f]{32}$/i,re=class extends U{constructor(e,n,o){let r=o.map(c=>` ${c.id} ${c.label}`).join(`
14
+ `+" ".repeat(Re));console.log(` ${e}:`.padEnd(Re)+r)}else console.log(` ${e}:`.padEnd(Re)+String(n))}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(`
15
+ `);let e=new Vo({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:"gray",preparing:"gray",queued:"gray",waiting:"gray",working:"blue",completed:"green",incomplete:"yellow",rejected:"yellow",abandoned:"yellow",error:"red",untestable:"red",failed:"red"},o=({preparing:"pending",waiting:"queued",abandoned:"expired",untestable:"error"}[t]??t).replaceAll("_"," ");return this.color(e[t]||"white",o)}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),l=Math.floor(c/60),u=Math.floor(l/24);return r<60?`${r}s ago`:c<60?`${c}m ago`:l<24?`${l}h ago`:u<30?`${u}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:Z.red,green:Z.green,blue:Z.blue,yellow:Z.yellow,cyan:Z.cyan,gray:Z.gray,white:Z.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 ee(i){return i.replace(/-/g,"").toLowerCase()}function Xe(i){let t=i.replace(/-/g,"");return Kr.test(t)}async function b(i,t,e,n){if(n?.requireFullId&&!Xe(e)){let u=It[t],a=await u(i),p=ee(e),m=a.filter(f=>ee(f.id).startsWith(p));throw new Be(e,t,n.requireFullId.reason,m)}if(Ye.test(e))return e;let o=It[t],r=await o(i),c=ee(e),l=r.filter(u=>ee(u.id).startsWith(c));if(l.length===0)throw new be(e,t);if(l.length===1)return l[0].id;throw new he(e,t,l)}var Ye,Kr,he,be,Be,It,N=q(()=>{"use strict";T();Ye=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,Kr=/^[0-9a-f]{32}$/i,he=class extends _{constructor(e,n,o){let r=o.map(c=>` ${c.id} ${c.label}`).join(`
15
16
  `);super(`"${e}" matches multiple ${n}s:
16
17
  ${r}
17
- Please provide more characters.`,1);this.prefix=e;this.resourceType=n;this.matches=o;this.name="AmbiguousIdError"}},ie=class extends U{constructor(e,n){super(`No ${n} found matching "${e}".`,3);this.prefix=e;this.resourceType=n;this.name="NoMatchError"}},Ue=class extends U{constructor(e,n,o,r){let c=`Full ${n} ID required for ${o} \u2014 short prefixes are not allowed for this operation.`;if(c+=`
18
+ Please provide more characters.`,1);this.prefix=e;this.resourceType=n;this.matches=o;this.name="AmbiguousIdError"}},be=class extends _{constructor(e,n){super(`No ${n} found matching "${e}".`,3);this.prefix=e;this.resourceType=n;this.name="NoMatchError"}},Be=class extends _{constructor(e,n,o,r){let c=`Full ${n} ID required for ${o} \u2014 short prefixes are not allowed for this operation.`;if(c+=`
18
19
 
19
20
  Provided: "${e}"`,r.length===1)c+=`
20
21
 
21
22
  This prefix matches: ${r[0].id} ${r[0].label}`,c+=`
22
- If this is correct, re-run the command with the full ID.`;else if(r.length>1){let u=r.map(l=>` ${l.id} ${l.label}`).join(`
23
+ If this is correct, re-run the command with the full ID.`;else if(r.length>1){let l=r.map(u=>` ${u.id} ${u.label}`).join(`
23
24
  `);c+=`
24
25
 
25
26
  This prefix matches multiple ${n}s:
26
- ${u}`,c+=`
27
+ ${l}`,c+=`
27
28
  Use the full ID of the intended ${n}.`}else c+=`
28
29
 
29
- No ${n} found matching this prefix.`;super(c,1);this.prefix=e;this.resourceType=n;this.reason=o;this.matches=r;this.name="FullIdRequiredError"}};tt={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});return(await Promise.all(t.map(n=>i.listApiKeys(n.id)))).flatMap(({items:n})=>n.map(o=>({id:o.id,label:o.name})))},async template(i){let{items:t}=await i.listProjects({limit:200});return(await Promise.all(t.map(n=>i.listTemplates(n.id)))).flatMap(({items:n})=>n.map(o=>({id:o.id,label:o.name})))},async schedule(i){let{items:t}=await i.listProjects({limit:200});return(await Promise.all(t.map(n=>i.listSchedules(n.id,{limit:200})))).flatMap(({items:n})=>n.map(o=>({id:o.id,label:o.name})))},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 ze(i){return{id:i.id,label:`${i.status.padEnd(10)} ${i.description??""}`.trim()}}function pt(i){return{id:i.id,label:i.name}}async function mt(i,t,e,n){let o=W(i);for(let r of e){let u=(await r.fetch()).filter(l=>W(l.id).startsWith(o));if(u.length===1)return n.info(`Resolved ${t} in ${r.label}`),u[0].id;if(u.length>1)throw new re(i,t,u)}throw new ie(i,t)}function gt(i){return Oe.test(i)||Ne(i)}async function K(i,t,e,n,o){if(gt(t))return t;let r=br(i,e,n);return mt(t,"job",r,o)}function br(i,t,e){if(t.projectId)return[{label:`project ${t.projectId}`,fetch:()=>lt(i,t.projectId)}];if(t.organizationId)return[{label:`organization ${t.organizationId}`,fetch:()=>ut(i,t.organizationId)}];let n=[];return e.project&&n.push({label:"default project",fetch:()=>lt(i,e.project)}),e.organization&&n.push({label:"default organization",fetch:()=>ut(i,e.organization)}),n.push({label:"all organizations",fetch:()=>yr(i)}),n}async function lt(i,t){let{items:e}=await i.listJobs({projectId:t,limit:200});return e.map(ze)}async function ut(i,t){let{items:e}=await i.listJobs({organizationId:t,limit:200});return e.map(ze)}async function yr(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(ze))}return e}async function le(i,t,e,n,o){if(gt(t))return t;let r=Ir(i,e,n);return mt(t,"project",r,o)}function Ir(i,t,e){if(t.organizationId)return[{label:`organization ${t.organizationId}`,fetch:()=>dt(i,t.organizationId)}];let n=[];return e.organization&&n.push({label:"default organization",fetch:()=>dt(i,e.organization)}),n.push({label:"all organizations",fetch:()=>wr(i)}),n}async function dt(i,t){let{items:e}=await i.listOrganizationProjects(t,{limit:200});return e.map(pt)}async function wr(i){let{items:t}=await i.listProjects({limit:200});return t.map(pt)}var te=z(()=>{"use strict";k()});var Le={};ho(Le,{waitCommand:()=>Ce,waitForJob:()=>ft});import{Command as jr}from"commander";import Cr from"ora";async function ft(i,t,e,n=600){let o=Date.now(),r=n*1e3,c=1e4,u=null;for(e.options.json||(u=Cr("Waiting for job completion...").start());;){let l=Date.now()-o;if(l>=r)throw u&&u.fail("Timeout waiting for job completion"),new ge(`Job ${i} did not complete within ${n} seconds \u2014 check ${t.dashboardUrl(i)} to continue tracking`);let s=await t.getJob(i);if(u){let p=e.formatDuration(Math.floor(l/1e3));u.text=`Waiting for job completion... (${p} elapsed, status: ${s.status})`}if(s.status==="completed"){u&&u.succeed("Test Completed!"),e.options.json||(console.log(),e.detail("Duration",s.testDurationSeconds?e.formatDuration(s.testDurationSeconds):"N/A"),e.detail("Cost","$"+(s.costUsd||0).toFixed(3)),s.testerAlias&&e.detail("Tester",s.testerAlias),s.result&&(console.log(`
30
- Results Summary:`),e.resultSummary(s.result)),console.log(),e.hints([["Full results",`runhuman results ${i}`]]));return}if(s.status==="error"||s.status==="incomplete"||s.status==="abandoned"||s.status==="rejected")throw u&&u.fail(`Test ${s.status}`),new he(i,s.status,t.dashboardUrl(i),s.testerResponse);await new Promise(p=>setTimeout(p,c))}}function Ce(){let i=new jr("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 d({json:e.json,color:o.color}),c=new y(o),u={projectId:e.project?await I(c,"project",e.project):void 0,organizationId:e.organization?await I(c,"organization",e.organization):void 0},l=await K(c,t,u,o,r);if(await ft(l,c,r,e.timeout||600),e.json){let s=await c.getJob(l),p=d.result(s);r.output(p)}}catch(n){let o=f(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}var Se=z(()=>{"use strict";v();E();T();S();te();k()});import{Command as ao}from"commander";v();E();T();S();import{Command as Sr}from"commander";import Ar from"ora";S();function et(i,t){try{return JSON.parse(i)}catch(e){throw e instanceof SyntaxError?new R(Ro(t,i,e)):e}}function Ro(i,t,e){let n=xo(e.message,t),o=n===null?"":` at position ${n}`;return`Invalid JSON for ${i}${o}: ${Po(e.message)}`}function xo(i,t){let e=i.match(/position (\d+)/);if(e)return Number(e[1]);let n=i.match(/line (\d+) column (\d+)/);return n?ko(t,Number(n[1]),Number(n[2])):null}function ko(i,t,e){let n=i.split(`
31
- `),o=0;for(let r=0;r<t-1&&r<n.length;r++)o+=n[r].length+1;return o+e-1}function Po(i){return i.replace(/\s+/g," ").trim()}S();var Uo={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 Oo(i,t,e){return i?"flag":process.env[e.envVar]?"env":"global config"}function C(i,t,e,n){let o=Uo[i],r=t||e[o.configKey];if(!r)throw new R(`${o.name} is required. You can:
30
+ No ${n} found matching this prefix.`;super(c,1);this.prefix=e;this.resourceType=n;this.reason=o;this.matches=r;this.name="FullIdRequiredError"}};It={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});return(await Promise.all(t.map(n=>i.listApiKeys(n.id)))).flatMap(({items:n})=>n.map(o=>({id:o.id,label:o.name})))},async template(i){let{items:t}=await i.listProjects({limit:200});return(await Promise.all(t.map(n=>i.listTemplates(n.id)))).flatMap(({items:n})=>n.map(o=>({id:o.id,label:o.name})))},async schedule(i){let{items:t}=await i.listProjects({limit:200});return(await Promise.all(t.map(n=>i.listSchedules(n.id,{limit:200})))).flatMap(({items:n})=>n.map(o=>({id:o.id,label:o.name})))},async note(i){let{items:t}=await i.listTesterNotes({limit:200});return t.map(e=>({id:e.id,label:e.title}))},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}}});import jt from"chalk";function ae(i,t){i.stopAndPersist({symbol:Hr,text:t})}function ce(i,t){i.stopAndPersist({symbol:Gr,text:t})}var Hr,Gr,Ue=q(()=>{"use strict";Hr=`${jt.green("\u2714")} `,Gr=`${jt.red("\u2716")} `});function Qe(i){return{id:i.id,label:`${i.status.padEnd(10)} ${i.description??""}`.trim()}}function At(i){return{id:i.id,label:i.name}}async function Et(i,t,e,n){let o=ee(i);for(let r of e){let l=(await r.fetch()).filter(u=>ee(u.id).startsWith(o));if(l.length===1)return n.info(`Resolved ${t} in ${r.label}`),l[0].id;if(l.length>1)throw new he(i,t,l)}throw new be(i,t)}function vt(i){return Ye.test(i)||Xe(i)}async function le(i,t,e,n,o){if(vt(t))return t;let r=Vr(i,e,n);return Et(t,"job",r,o)}function Vr(i,t,e){if(t.projectId)return[{label:`project ${t.projectId}`,fetch:()=>Ct(i,t.projectId)}];if(t.organizationId)return[{label:`organization ${t.organizationId}`,fetch:()=>St(i,t.organizationId)}];let n=[];return e.project&&n.push({label:"default project",fetch:()=>Ct(i,e.project)}),e.organization&&n.push({label:"default organization",fetch:()=>St(i,e.organization)}),n.push({label:"all organizations",fetch:()=>Wr(i)}),n}async function Ct(i,t){let{items:e}=await i.listJobs({projectId:t,limit:200});return e.map(Qe)}async function St(i,t){let{items:e}=await i.listJobs({organizationId:t,limit:200});return e.map(Qe)}async function Wr(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(Qe))}return e}async function ye(i,t,e,n,o){if(vt(t))return t;let r=Br(i,e,n);return Et(t,"project",r,o)}function Br(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:()=>Yr(i)}),n}async function Tt(i,t){let{items:e}=await i.listOrganizationProjects(t,{limit:200});return e.map(At)}async function Yr(i){let{items:t}=await i.listProjects({limit:200});return t.map(At)}var we=q(()=>{"use strict";N()});var Ze={};_o(Ze,{waitCommand:()=>_e,waitForJob:()=>kt});import{Command as Xr}from"commander";import Qr from"ora";async function kt(i,t,e,n=600){let o=Date.now(),r=n*1e3,c=1e4,l=null;for(e.options.json||(l=Qr("Waiting for job completion...").start());;){let u=Date.now()-o;if(u>=r)throw l&&ce(l,"Timeout waiting for job completion"),new X(`Job ${i} did not complete within ${n} seconds \u2014 check ${t.dashboardUrl(i)} to continue tracking`);let a=await t.getJob(i);if(l){let p=e.formatDuration(Math.floor(u/1e3));l.text=`Waiting for job completion... (${p} elapsed, status: ${a.status})`}if(a.status==="completed"){l&&ae(l,"Test Completed!"),e.options.json||(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(`
31
+ Results Summary:`),e.resultSummary(a.result)),console.log(),e.hints([["Full results",`runhuman results ${i}`]]));return}if(a.status==="error"||a.status==="incomplete"||a.status==="abandoned"||a.status==="rejected")throw l&&ce(l,`Test ${a.status}`),new ve(i,a.status,t.dashboardUrl(i),a.testerResponse);await new Promise(p=>setTimeout(p,c))}}function _e(){let i=new Xr("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 d({json:e.json,color:o.color}),c=new y(o),l={projectId:e.project?await b(c,"project",e.project):void 0,organizationId:e.organization?await b(c,"organization",e.organization):void 0},u=await le(c,t,l,o,r);if(await kt(u,c,r,e.timeout||600),e.json){let a=await c.getJob(u),p=d.result(a);r.output(p)}}catch(n){let o=g(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}var De=q(()=>{"use strict";R();x();v();T();we();N();Ue()});import{Command as Eo}from"commander";R();x();v();T();import{Command as Zr}from"commander";import ei from"ora";T();function Ke(i,t){try{return JSON.parse(i)}catch(e){throw e instanceof SyntaxError?new A(Wo(t,i,e)):e}}function Wo(i,t,e){let n=Bo(e.message,t),o=n===null?"":` at position ${n}`,r=n===null?Xo(e.message):Qo(e.message);return`Invalid JSON for ${i}${o}: ${r}`}function Bo(i,t){let e=i.match(/position (\d+)/);if(e)return Number(e[1]);let n=i.match(/line (\d+) column (\d+)/);return n?Yo(t,Number(n[1]),Number(n[2])):null}function Yo(i,t,e){let n=i.split(`
32
+ `),o=0;for(let r=0;r<t-1&&r<n.length;r++)o+=n[r].length+1;return o+e-1}function Xo(i){return i.replace(/\s+/g," ").trim()}function Qo(i){return i.replace(/\s*at position \d+/g,"").replace(/\s*\(line \d+ column \d+\)/g,"").replace(/\s+/g," ").trim()}var se={PENDING:"pending",PREPARING:"preparing",QUEUED:"queued",WAITING:"waiting",WORKING:"working",COMPLETED:"completed",INCOMPLETE:"incomplete",ABANDONED:"abandoned",REJECTED:"rejected",ERROR:"error",UNTESTABLE:"untestable"};function Ne(i){let t=[];function e(n,o){if(typeof n!="object"||n===null)return;let r=n;if(r.type==="object"&&(r.additionalProperties!==!1&&t.push(`${o}: Object schemas must have "additionalProperties": false for OpenAI structured outputs`),r.properties&&typeof r.properties=="object")){let c=Object.keys(r.properties),l=Array.isArray(r.required)?r.required:[],u=c.filter(a=>!l.includes(a));u.length>0&&t.push(`${o}: All properties must be in "required" array for OpenAI structured outputs. Missing: ${u.join(", ")}`);for(let[a,p]of Object.entries(r.properties))e(p,`${o}.properties.${a}`)}r.type==="array"&&(r.items===void 0?t.push(`${o}: Array schemas must declare "items" for OpenAI structured outputs (e.g. { "type": "array", "items": { "type": "string" } })`):e(r.items,`${o}.items`))}for(let[n,o]of Object.entries(i))e(o,n);return{valid:t.length===0,errors:t}}var pt={high:1800*1e3,medium:7200*1e3,low:1440*60*1e3};Fe();var He=["ios","android","pc","mac"],Ge=["english","spanish"];var me={type:"multi-select",matchMode:"any",values:He,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"},fe={type:"multi-select",matchMode:"all",values:Ge,labels:{english:"English",spanish:"Spanish"},slackLabels:{english:"English speaker",spanish:"Spanish speaker"},categoryName:"language",sectionLabel:"Language",tooltip:"Tester must speak all selected languages"};var ht=["tester_not_found","tester_not_active","tester_has_active_job","job_not_found","job_already_claimed","job_not_available","pool_requirements_not_met","mobile_only","desktop_only","version_conflict"],_a=new Set(ht);var B=["daily","weekly","biweekly","monthly","once"],Oe={daily:"Daily",weekly:"Weekly",biweekly:"Biweekly",monthly:"Monthly",once:"One-time"};var Pe={0:"Sun",1:"Mon",2:"Tue",3:"Wed",4:"Thu",5:"Fri",6:"Sat"};var ge=["starter","pro","max","enterprise","enterprise_pro"],bt=new Set(ge),Ve=["autonomous"],yt=new Set(Ve);var wt=Object.fromEntries(ge.map((i,t)=>[i,t]));T();function We(i,t){let e;try{e=JSON.parse(i)}catch(r){let c=r instanceof SyntaxError?r.message:String(r);throw new A(`Invalid JSON in ${t}: ${c}`)}if(typeof e!="object"||e===null||Array.isArray(e))throw new A(`${t} must be a JSON object mapping field names to JSON Schema definitions`);let n=e,o=Ne(n);if(!o.valid)throw new A(`Invalid output schema in ${t}:
33
+ - ${o.errors.join(`
34
+ - `)}`);return n}T();var qr={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 Jr(i,t,e){return i?"flag":process.env[e.envVar]?"env":"global config"}function C(i,t,e,n){let o=qr[i],r=t||e[o.configKey];if(!r)throw new A(`${o.name} is required. You can:
32
35
  \u2022 Pass ${o.flag} <id> for this command
33
36
  \u2022 Set a default: ${o.switchCommand}
34
- \u2022 Set via env: export ${o.envVar}=<id>`);let c=Oo(t,e,o);return c!=="flag"&&n.info(`Using default ${i}: ${r} [from ${c}]`),r}k();var ot={high:1800*1e3,medium:7200*1e3,low:1440*60*1e3};xe();var De=["ios","android","pc","mac"],Me=["english","spanish"];var ae={type:"multi-select",matchMode:"any",values:De,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"},se={type:"multi-select",matchMode:"all",values:Me,labels:{english:"English",spanish:"Spanish"},slackLabels:{english:"English speaker",spanish:"Spanish speaker"},categoryName:"language",sectionLabel:"Language",tooltip:"Tester must speak all selected languages"};var q=["daily","weekly","biweekly","monthly","once"],we={daily:"Daily",weekly:"Weekly",biweekly:"Biweekly",monthly:"Monthly",once:"One-time"};var je={0:"Sun",1:"Mon",2:"Tue",3:"Wed",4:"Thu",5:"Fri",6:"Sat"};var ce=["starter","pro","max","enterprise","enterprise_pro"],at=new Set(ce),_e=["autonomous"],st=new Set(_e);var ct=Object.fromEntries(ce.map((i,t)=>[i,t]));function $e(){let i=new Sr("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 (single repo)").option("--github-repos <repos>","GitHub repos for context (comma-separated owner/repo)").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-repos)").option("--auto-create-github-issues-repo <owner/repo>","Target repo for auto-created issues (must be in --github-repos)").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 d({json:e.json,quiet:e.quiet,color:o.color}),c=new y(o);if(e.template&&e.templateFile)throw new R("Cannot use both --template and --template-file. Choose one.");let u=e.template||e.templateFile;if(!t&&!u&&!o.defaultUrl)throw new R("URL is required (provide as argument, via --template/--template-file, or set defaultUrl in config)");if(!e.description&&!u)throw new R("Description is required (use -d flag or --template/--template-file)");let l=C("project",e.project,o,r),p={projectId:await I(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 w=await(await import("fs/promises")).readFile(e.schema,"utf-8");p.outputSchema=JSON.parse(w)}else e.schemaInline&&(p.outputSchema=JSON.parse(e.schemaInline));if(e.metadata&&(p.metadata=et(e.metadata,"--metadata")),e.githubRepos?p.githubRepos=e.githubRepos.split(",").map(b=>b.trim()):e.githubRepo&&(p.githubRepos=[e.githubRepo]),e.autoCreateGithubIssuesRepo&&(p.autoCreateGithubIssuesRepo=e.autoCreateGithubIssuesRepo),e.template&&(p.template=e.template),e.templateFile){let b=await import("fs/promises");p.templateContent=await b.readFile(e.templateFile,"utf-8")}if(e.requiredDevices){let b=e.requiredDevices.split(",");for(let w of b)if(!ae.values.includes(w))throw new R(`Invalid device "${w}". Valid values: ${ae.values.join(", ")}`);p.requiredDevices=b}if(e.requiredLanguages){let b=e.requiredLanguages.split(",");for(let w of b)if(!se.values.includes(w))throw new R(`Invalid language "${w}". Valid values: ${se.values.join(", ")}`);p.requiredLanguages=b}e.requireSocialVideos&&(p.requireSocialVideos=!0),e.requireApkInstall&&(p.requireApkInstall=!0),e.autoCreateGithubIssues&&(p.autoCreateGithubIssues=!0),e.maxExtensionMinutes!==void 0&&(p.maxExtensionMinutes=e.maxExtensionMinutes),e.maxExtensionCount!==void 0&&(p.maxExtensionCount=e.maxExtensionCount);let m=e.json?null:Ar("Creating QA test job...").start(),g=await c.createJob(p);if(m&&m.succeed("Job created successfully!"),g.deprecationWarnings&&r.deprecationWarnings(g.deprecationWarnings),e.json){let b=d.result({jobId:g.jobId,message:g.message,dashboardUrl:c.dashboardUrl(g.jobId),...g.deprecationWarnings&&{deprecationWarnings:g.deprecationWarnings}});r.output(b)}else e.quiet?console.log(g.jobId):(r.detail("Job ID",g.jobId),p.url&&r.detail("URL",p.url),r.detail("Dashboard",c.dashboardUrl(g.jobId)),console.log(),r.hints([["Track progress",`runhuman status ${g.jobId}`],["Wait for result",`runhuman wait ${g.jobId}`]]));if(e.sync){let{waitForJob:b}=await Promise.resolve().then(()=>(Se(),Le));await b(g.jobId,c,r,e.wait||300)}}catch(n){let o=f(n);new d({json:e.json,quiet:!1}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}v();E();T();S();te();k();import{Command as Tr}from"commander";function Fe(){let i=new Tr("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 d({json:e.json,color:o.color}),c=new y(o),u={projectId:e.project?await I(c,"project",e.project):void 0,organizationId:e.organization?await I(c,"organization",e.organization):void 0},l=await K(c,t,u,o,r),s=await c.getJob(l);if(e.json){let p=d.result(s);r.output(p)}else r.heading(`Job Status: ${l}`),r.detail("Status",r.formatStatus(s.status)),s.testerAlias&&r.detail("Tester",s.testerAlias),s.url&&r.detail("URL",s.url),s.description&&r.detail("Description",s.description),s.createdAt&&r.detail("Created",r.formatTimestamp(s.createdAt)),s.completedAt&&r.detail("Completed",r.formatTimestamp(s.completedAt)),s.testDurationSeconds&&r.detail("Duration",r.formatDuration(s.testDurationSeconds)),s.costUsd&&r.detail("Cost",`$${s.costUsd.toFixed(3)}`),s.deviceClass&&r.detail("Device Class",s.deviceClass),s.requiredDevices?.length&&r.detail("Required Devices",s.requiredDevices.join(", ")),s.requiredLanguages?.length&&r.detail("Required Langs",s.requiredLanguages.join(", ")),s.requireSocialVideos&&r.detail("Social Videos","required"),s.requireApkInstall&&r.detail("APK Install","required"),s.maxExtensionMinutes!==void 0&&s.maxExtensionCount!==void 0&&s.extensionsUsed!==void 0&&r.detail("Extensions",`${s.extensionsUsed}/${s.maxExtensionCount} used (${s.maxExtensionMinutes} min total)`),r.detail("Dashboard",c.dashboardUrl(l)),console.log(),s.status==="pending"||s.status==="queued"||s.status==="waiting"||s.status==="working"?r.hints([["Wait for completion",`runhuman wait ${l}`]]):s.status==="completed"&&r.hints([["View results",`runhuman results ${l}`]])}catch(n){let o=new d({json:e.json}),r=f(n);o.outputError(r.message,r.details),process.exit(r.exitCode)}}),i}Se();v();E();T();S();te();k();import{Command as vr}from"commander";var ht="=".repeat(60);function N(i){return`
35
- ${ht}
37
+ \u2022 Set via env: export ${o.envVar}=<id>`);let c=Jr(t,e,o);return c!=="flag"&&n.info(`Using default ${i}: ${r} [from ${c}]`),r}N();Ue();function xt(i,t){return i.split(",").map(e=>e.trim()).filter(e=>e.length>0).map(e=>{let n=Number(e);if(!Number.isInteger(n)||n<=0)throw new A(`Invalid value "${e}" in ${t}: expected positive integers (e.g. 42,43)`);return n})}function et(){let i=new Zr("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 (single repo)").option("--github-repos <repos>","GitHub repos for context (comma-separated owner/repo)").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 at least one GitHub repo via --github-repos, --github-repo, template, or project default)").option("--auto-create-github-issues-repo <owner/repo>","Target repo for auto-created issues (must be in the resolved github repos list)").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("--pr-numbers <numbers>","PR numbers to test (comma-separated, triggers AI test-plan consolidation)").option("--issue-numbers <numbers>","Issue numbers to test (comma-separated, triggers AI test-plan consolidation)").option("--no-check-testability","Disable the AI testability gate (on by default when PRs/issues are provided)").option("--results-template <file>","Path to an MDForm file. AI fills it to produce the structured results body.").option("--results-template-inline <markdown>","Inline MDForm content (alternative to --results-template)").option("--additional-validation-instructions <text>","Extra instructions for the LLM when validating tester results").option("--github-token <token>","GitHub token for PR/issue context (falls back to GITHUB_TOKEN env var)").option("--slack-notification-json <json>","Slack notification config override for this job (JSON object)").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 d({json:e.json,quiet:e.quiet,color:o.color}),c=new y(o);if(e.template&&e.templateFile)throw new A("Cannot use both --template and --template-file. Choose one.");let l=e.template||e.templateFile;if(!e.description&&!l)throw new A("Description is required (use -d flag or --template/--template-file)");if(e.resultsTemplate&&e.resultsTemplateInline)throw new A("Cannot use both --results-template and --results-template-inline. Choose one.");let u=await import("fs/promises"),a;if(e.schema){let M=await u.readFile(e.schema,"utf-8");a=We(M,`--schema ${e.schema}`)}else e.schemaInline&&(a=We(e.schemaInline,"--schema-inline"));let p;e.metadata&&(p=Ke(e.metadata,"--metadata"));let m;e.slackNotificationJson&&(m=Ke(e.slackNotificationJson,"--slack-notification-json"));let f;e.prNumbers&&(f=xt(e.prNumbers,"--pr-numbers"));let w;e.issueNumbers&&(w=xt(e.issueNumbers,"--issue-numbers"));let j;if(e.requiredDevices){let M=e.requiredDevices.split(",");for(let pe of M)if(!me.values.includes(pe))throw new A(`Invalid device "${pe}". Valid values: ${me.values.join(", ")}`);j=M}let E;if(e.requiredLanguages){let M=e.requiredLanguages.split(",");for(let pe of M)if(!fe.values.includes(pe))throw new A(`Invalid language "${pe}". Valid values: ${fe.values.join(", ")}`);E=M}let S;e.templateFile&&(S=await u.readFile(e.templateFile,"utf-8"));let U;e.resultsTemplate?U=await u.readFile(e.resultsTemplate,"utf-8"):e.resultsTemplateInline&&(U=e.resultsTemplateInline);let D;e.githubRepos?D=e.githubRepos.split(",").map(M=>M.trim()):e.githubRepo&&(D=[e.githubRepo]);let V=C("project",e.project,o,r),te=await b(c,"project",V),L=t;if(!L&&!l){let M=await c.getProject(te);if(M.defaultUrl)L=M.defaultUrl;else if(o.defaultUrl)L=o.defaultUrl,r.warn(`Project '${M.name}' has no default URL set; falling back to local CLI default (${o.defaultUrl}). Set a default URL on the project with \`runhuman projects update ${te} --default-url <url>\`.`);else throw new A(`URL is required. Provide one of:
38
+ \u2022 positional URL argument
39
+ \u2022 --template / --template-file
40
+ \u2022 a default URL on the project (set it via \`runhuman projects update <projectId> --default-url <url>\` or the dashboard)`)}let k={projectId:te,url:L,description:e.description||void 0,targetDurationMinutes:e.duration||o.defaultDuration,deviceClass:e.deviceClass||o.defaultDeviceClass};a&&(k.outputSchema=a),p&&(k.metadata=p),m&&(k.slackNotification=m),f&&(k.prNumbers=f),w&&(k.issueNumbers=w),j&&(k.requiredDevices=j),E&&(k.requiredLanguages=E),S&&(k.templateContent=S),U&&(k.resultsTemplate=U),D&&(k.githubRepos=D),e.template&&(k.template=e.template),e.autoCreateGithubIssuesRepo&&(k.autoCreateGithubIssuesRepo=e.autoCreateGithubIssuesRepo),e.requireSocialVideos&&(k.requireSocialVideos=!0),e.requireApkInstall&&(k.requireApkInstall=!0),e.autoCreateGithubIssues&&(k.autoCreateGithubIssues=!0),e.maxExtensionMinutes!==void 0&&(k.maxExtensionMinutes=e.maxExtensionMinutes),e.maxExtensionCount!==void 0&&(k.maxExtensionCount=e.maxExtensionCount),e.additionalValidationInstructions&&(k.additionalValidationInstructions=e.additionalValidationInstructions),e.checkTestability===!1&&(k.checkTestability=!1);let oe=e.githubToken||process.env.GITHUB_TOKEN;oe&&(k.githubToken=oe);let W=e.json?null:ei("Creating QA test job...").start(),P=await c.createJob(k);if(W&&ae(W,"Job created successfully!"),P.deprecationWarnings&&r.deprecationWarnings(P.deprecationWarnings),P.warnings&&r.warnings(P.warnings),e.json){let M=d.result({jobId:P.jobId,message:P.message,dashboardUrl:c.dashboardUrl(P.jobId),...P.deprecationWarnings&&{deprecationWarnings:P.deprecationWarnings},...P.warnings&&{warnings:P.warnings}});r.output(M)}else e.quiet?console.log(P.jobId):(r.detail("Job ID",P.jobId),k.url&&r.detail("URL",k.url),r.detail("Dashboard",c.dashboardUrl(P.jobId)),console.log(),r.hints([["Track progress",`runhuman status ${P.jobId}`],["Wait for result",`runhuman wait ${P.jobId}`]]));if(e.sync){let{waitForJob:M}=await Promise.resolve().then(()=>(De(),Ze));await M(P.jobId,c,r,e.wait||300)}}catch(n){let o=g(n);new d({json:e.json,quiet:!1}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}R();x();v();T();we();N();import{Command as ti}from"commander";function tt(){let i=new ti("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 d({json:e.json,color:o.color}),c=new y(o),l={projectId:e.project?await b(c,"project",e.project):void 0,organizationId:e.organization?await b(c,"organization",e.organization):void 0},u=await le(c,t,l,o,r),a=await c.getJob(u);if(e.json){let p=d.result(a);r.output(p)}else r.heading(`Job Status: ${u}`),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",c.dashboardUrl(u)),console.log(),a.status==="pending"||a.status==="queued"||a.status==="waiting"||a.status==="working"?r.hints([["Wait for completion",`runhuman wait ${u}`]]):a.status==="completed"&&r.hints([["View results",`runhuman results ${u}`]])}catch(n){let o=new d({json:e.json}),r=g(n);o.outputError(r.message,r.details),process.exit(r.exitCode)}}),i}De();R();x();v();T();we();N();import{Command as ni}from"commander";var Rt="=".repeat(60);function $(i){return`
41
+ ${Rt}
36
42
  ${i}
37
- ${ht}
38
- `}function ue(i){return new Date(i).toLocaleTimeString()}function Er(i){let t=Math.floor(i/60),e=Math.floor(i%60);return`${String(t).padStart(2,"0")}:${String(e).padStart(2,"0")}`}function bt(i){let t=i.transcription?.sessions.flatMap(n=>n.segments)??[];if(t.length===0)return N("Transcript")+` No transcript available
39
- `;let e=t.map(n=>{let o=n.words[0];return` [${o?Er(o.start):"00:00"}] ${n.text}`});return N("Transcript")+e.join(`
43
+ ${Rt}
44
+ `}function Ie(i){return new Date(i).toLocaleTimeString()}function oi(i){let t=Math.floor(i/60),e=Math.floor(i%60);return`${String(t).padStart(2,"0")}:${String(e).padStart(2,"0")}`}function Nt(i){let t=i.transcription?.sessions.flatMap(n=>n.segments)??[];if(t.length===0)return $("Transcript")+` No transcript available
45
+ `;let e=t.map(n=>{let o=n.words[0];return` [${o?oi(o.start):"00:00"}] ${n.text}`});return $("Transcript")+e.join(`
40
46
  `)+`
41
- `}function yt(i){let t=i.testerData?.consoleMessages;if(!t?.length)return N("Console Logs")+` No console logs recorded
42
- `;let e=t.map(o=>{let r=ue(o.timestamp);return` [${o.type}] ${r} ${o.message}`}),n=N("Console Logs")+e.join(`
47
+ `}function Ot(i){let t=i.testerData?.consoleMessages;if(!t?.length)return $("Console Logs")+` No console logs recorded
48
+ `;let e=t.map(o=>{let r=Ie(o.timestamp);return` [${o.type}] ${r} ${o.message}`}),n=$("Console Logs")+e.join(`
43
49
  `)+`
44
50
  `;if(i.testerData?.truncationInfo){let o=i.testerData.truncationInfo;n+=`
45
51
  (Truncated: ${JSON.stringify(o)})
46
- `}return n}function It(i){let t=i.testerData?.networkRequests;if(!t?.length)return N("Network Requests")+` No network requests recorded
47
- `;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(`
52
+ `}return n}function Pt(i){let t=i.testerData?.networkRequests;if(!t?.length)return $("Network Requests")+` No network requests recorded
53
+ `;let e=t.map(n=>{let o=Ie(n.timestamp),r=n.status??"---";return` ${n.method} ${r} ${n.url} (${o})`});return $("Network Requests")+e.join(`
48
54
  `)+`
49
- `}function wt(i){let t=i.testerData?.events;if(!t?.length)return N("User Events")+` No events recorded
50
- `;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(`
55
+ `}function Ut(i){let t=i.testerData?.events;if(!t?.length)return $("User Events")+` No events recorded
56
+ `;let e=t.map(n=>{let o=Ie(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 $("User Events")+e.join(`
51
57
  `)+`
52
- `}function jt(i){if(!i.keyMoments?.length)return N("Key Moments")+` No key moments available
53
- `;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(`
58
+ `}function _t(i){if(!i.keyMoments?.length)return $("Key Moments")+` No key moments available
59
+ `;let t=i.keyMoments.map(e=>{let n=Ie(e.timestamp);return` [${e.type}] ${n} ${e.description} (significance: ${e.significance})`});return $("Key Moments")+t.join(`
54
60
  `)+`
55
- `}function Ct(i){if(!i.conversationHistory?.length)return N("Conversation History")+` No conversation messages
56
- `;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(`
61
+ `}function Dt(i){if(!i.conversationHistory?.length)return $("Conversation History")+` No conversation messages
62
+ `;let t=i.conversationHistory.map(e=>{let n=Ie(e.timestamp),o=e.role.charAt(0).toUpperCase()+e.role.slice(1);return` [${n}] ${o}: ${e.content}`});return $("Conversation History")+t.join(`
57
63
  `)+`
58
- `}function St(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 u=typeof r=="string"?r:r.text;o.push(` ${c+1}. ${u}`)})),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(`
59
- `)});return N(`Extracted Issues (${i.extractedIssues.length})`)+t.join(`
64
+ `}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 l=typeof r=="string"?r:r.text;o.push(` ${c+1}. ${l}`)})),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(`
65
+ `)});return $(`Extracted Issues (${i.extractedIssues.length})`)+t.join(`
60
66
 
61
67
  `)+`
62
- `}function At(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})
63
- ${e.issueUrl}`});return N(`Created GitHub Issues (${i.createdIssues.length})`)+t.join(`
68
+ `}function zt(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})
69
+ ${e.issueUrl}`});return $(`Created GitHub Issues (${i.createdIssues.length})`)+t.join(`
64
70
 
65
71
  `)+`
66
- `}function Je(){let i=new vr("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 d({json:e.json,color:o.color}),c=new y(o),u={projectId:e.project?await I(c,"project",e.project):void 0,organizationId:e.organization?await I(c,"organization",e.organization):void 0},l=await K(c,t,u,o,r),s=await c.getJob(l);if(e.json){let b=d.result(s);r.output(b);return}r.heading(`Test Results: ${l}`),r.sectionHeader("Job Information"),r.detail("Job ID",s.id),r.detail("Status",s.status),s.testerAlias&&r.detail("Tester",s.testerAlias),s.url&&r.detail("URL",s.url),s.description&&r.detail("Description",s.description),s.createdAt&&r.detail("Created",r.formatTimestamp(s.createdAt)),s.completedAt&&r.detail("Completed",r.formatTimestamp(s.completedAt)),s.testDurationSeconds&&r.detail("Duration",r.formatDuration(s.testDurationSeconds)),s.costUsd&&r.detail("Cost",`$${s.costUsd.toFixed(3)}`),s.deviceClass&&r.detail("Device Class",s.deviceClass),s.requiredDevices?.length&&r.detail("Required Devices",s.requiredDevices.join(", ")),s.requiredLanguages?.length&&r.detail("Required Langs",s.requiredLanguages.join(", ")),s.requireSocialVideos&&r.detail("Social Videos","required"),s.requireApkInstall&&r.detail("APK Install","required"),s.result&&Object.keys(s.result).length>0&&!e.raw&&(r.sectionHeader("Structured Results (Extracted)"),r.resultSummary(s.result)),s.testerResponse&&!e.schemaOnly&&(r.sectionHeader("Tester Feedback"),console.log(" "+s.testerResponse.split(`
72
+ `}function ot(){let i=new ni("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 d({json:e.json,color:o.color}),c=new y(o),l={projectId:e.project?await b(c,"project",e.project):void 0,organizationId:e.organization?await b(c,"organization",e.organization):void 0},u=await le(c,t,l,o,r),a=await c.getJob(u);if(e.json){let w=d.result(a);r.output(w);return}r.heading(`Test Results: ${u}`),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)"),r.resultSummary(a.result)),a.testerResponse&&!e.schemaOnly&&(r.sectionHeader("Tester Feedback"),console.log(" "+a.testerResponse.split(`
67
73
  `).join(`
68
- `)));let p=St(s);p&&console.log(p);let m=At(s);m&&console.log(m);let g=e.all;(g||e.transcript)&&console.log(bt(s)),(g||e.consoleLogs)&&console.log(yt(s)),(g||e.network)&&console.log(It(s)),(g||e.events)&&console.log(wt(s)),(g||e.keyMoments)&&console.log(jt(s)),(g||e.conversation)&&console.log(Ct(s)),r.separator(),console.log(),s.richResultsGated&&(console.log(" Rich test data (transcript, logs, network) requires a paid plan."),console.log(` Upgrade at https://runhuman.com/pricing
69
- `))}catch(n){let o=f(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}v();E();T();S();import{Command as Rr}from"commander";k();function qe(){let i=new Rr("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 d({json:e.json,format:e.format,color:o.color}),c=new y(o),u=C("project",e.project,o,r),l=await I(c,"project",u),s={limit:e.limit||20,offset:e.offset||0,projectId:l};t&&t!=="all"&&(s.status=t);let{items:p,pagination:m}=await c.listJobs(s);if(e.json){let g=d.result({jobs:p,pagination:m});r.output(g)}else{if(m.total===0){r.heading("Recent Jobs (0)"),console.log(`No jobs found.
70
- `),r.hints([["Create a job",'runhuman create <url> -d "Test description"']]);return}r.heading(`Recent Jobs (${p.length} of ${m.total})`),console.log(r.formatJobList(p)),console.log(),r.hints([["View details","runhuman status <jobId>"],["View results","runhuman results <jobId>"]])}}catch(n){let o=new d({json:e.json}),r=f(n);o.outputError(r.message,r.details),process.exit(r.exitCode)}}),i}v();E();T();S();te();k();import{Command as xr}from"commander";import kr from"inquirer";function Ke(){let i=new xr("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 d({json:e.json,color:o.color}),c=new y(o),u={projectId:e.project?await I(c,"project",e.project):void 0,organizationId:e.organization?await I(c,"organization",e.organization):void 0},l=await K(c,t,u,o,r);if(!e.json&&!e.force){let{confirmed:s}=await kr.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete job ${l}? This cannot be undone.`,default:!1}]);if(!s)return}if(await c.deleteJob(l),e.json){let s=d.result({success:!0,message:"Job deleted successfully",jobId:l});r.output(s)}else r.success("Job deleted successfully"),r.detail("Job ID",l),console.log()}catch(n){let o=f(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}v();E();T();import{Command as Pr}from"commander";import Ur from"chokidar";S();k();import{existsSync as Ae,mkdirSync as Or,readFileSync as He,writeFileSync as Nr,unlinkSync as Dr}from"fs";import{join as Tt}from"path";import{homedir as Et}from"os";var H=Tt(Et(),".config","runhuman","watch.pid");function Ge(){let i=new Pr("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 _r();return}if(e.status){zr();return}vt()&&(console.log(`
74
+ `)));let p=Mt(a);p&&console.log(p);let m=zt(a);m&&console.log(m);let f=e.all;(f||e.transcript)&&console.log(Nt(a)),(f||e.consoleLogs)&&console.log(Ot(a)),(f||e.network)&&console.log(Pt(a)),(f||e.events)&&console.log(Ut(a)),(f||e.keyMoments)&&console.log(_t(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
75
+ `))}catch(n){let o=g(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}import{Command as ri}from"commander";R();x();v();T();N();T();var Lt=Object.values(se);function $t(i){if(!(!i||i==="all")){if(!Lt.includes(i))throw new A(`Invalid status filter "${i}". Valid values: all, ${Lt.join(", ")}`);return i}}var ii=`Status filter: all|${Object.values(se).join("|")}`;function nt(){let i=new ri("list");return i.description("List all jobs with optional filtering").argument("[filter]",ii).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 d({json:e.json,format:e.format,color:o.color}),c=new y(o),l=C("project",e.project,o,r),u=await b(c,"project",l),a={limit:e.limit||20,offset:e.offset||0,projectId:u},p=$t(t);p&&(a.status=p);let{items:m,pagination:f}=await c.listJobs(a);if(e.json){let w=d.result({jobs:m,pagination:f});r.output(w)}else{if(f.total===0){r.heading("Recent Jobs (0)"),console.log(`No jobs found.
76
+ `),r.hints([["Create a job",'runhuman create <url> -d "Test description"']]);return}r.heading(`Recent Jobs (${m.length} of ${f.total})`),console.log(r.formatJobList(m)),console.log(),r.hints([["View details","runhuman status <jobId>"],["View results","runhuman results <jobId>"]])}}catch(n){let o=new d({json:e.json}),r=g(n);o.outputError(r.message,r.details),process.exit(r.exitCode)}}),i}R();x();v();import{Command as si}from"commander";import ai from"chokidar";T();N();import{existsSync as Me,mkdirSync as ci,readFileSync as rt,writeFileSync as li,unlinkSync as ui}from"fs";import{join as Ft}from"path";import{homedir as qt}from"os";var Y=Ft(qt(),".config","runhuman","watch.pid");function it(){let i=new si("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 pi();return}if(e.status){mi();return}Jt()&&(console.log(`
71
77
  Watch mode is already running`),console.log(` Use --stop to stop it first
72
- `),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/**/*"],u=e.ignore||r.ignore||["**/node_modules/**","**/dist/**","**/build/**","**/.git/**"],l=e.debounce||r.debounce||2e3,s=e.url||r.url||o.defaultUrl,p=e.description||r.description||"Auto-test from watch mode",m=e.template||r.template;s||(console.log(`
78
+ `),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/**/*"],l=e.ignore||r.ignore||["**/node_modules/**","**/dist/**","**/build/**","**/.git/**"],u=e.debounce||r.debounce||2e3,a=e.description||r.description||"Auto-test from watch mode",p=e.template||r.template,m=new d({color:o.color}),f=new y(o),w=C("project",void 0,o,m),j=await b(f,"project",w),E=e.url||r.url;if(!E){let L=await f.getProject(j);L.defaultUrl?E=L.defaultUrl:o.defaultUrl?(E=o.defaultUrl,m.warn(`Project '${L.name}' has no default URL set; falling back to local CLI default (${o.defaultUrl}). Set a default URL on the project with \`runhuman projects update ${j} --default-url <url>\`.`)):(console.log(`
73
79
  \u274C Error: URL required
74
- `),console.log(`Provide via --url flag or set in .runhumanrc:
75
- `),console.log(' runhuman watch --url "https://myapp.com"'),console.log(` OR add to .runhumanrc: { "watch": { "url": "..." } }
76
- `),process.exit(1));let g=new d({color:o.color}),b=new y(o),w=C("project",void 0,o,g),x=await I(b,"project",w),A=m;g.heading("Starting watch mode"),g.detail("Watching",c.join(", ")),g.detail("Ignoring",u.join(", ")),g.detail("Debounce",`${l}ms`),g.detail("URL",s),m&&g.detail("Template",m),console.log(`
80
+ `),console.log("Provide one of:"),console.log(" \u2022 --url <url>"),console.log(" \u2022 `watch.url` in `.runhumanrc`"),console.log(" \u2022 a default URL on the project (`runhuman projects update "+j+" --default-url <url>`)\n"),process.exit(1))}let S=p;m.heading("Starting watch mode"),m.detail("Watching",c.join(", ")),m.detail("Ignoring",l.join(", ")),m.detail("Debounce",`${u}ms`),m.detail("URL",E),p&&m.detail("Template",p),console.log(`
77
81
  Press Ctrl+C to stop
78
- `),Mr(process.pid);let D=null,_=new Set,Y=Ur.watch(c,{ignored:u,persistent:!0,ignoreInitial:!0});Y.on("change",X=>{_.add(X),console.log(` Changed: ${X}`),D&&clearTimeout(D),D=setTimeout(async()=>{let Xe=Array.from(_);_.clear(),console.log(`
79
- Creating test job for ${Xe.length} file(s)...
80
- `);try{let Re={projectId:x,url:s,description:`${p}
82
+ `),di(process.pid);let U=null,D=new Set,V=ai.watch(c,{ignored:l,persistent:!0,ignoreInitial:!0});V.on("change",L=>{D.add(L),console.log(` Changed: ${L}`),U&&clearTimeout(U),U=setTimeout(async()=>{let k=Array.from(D);D.clear(),console.log(`
83
+ Creating test job for ${k.length} file(s)...
84
+ `);try{let oe={projectId:j,url:E,description:`${a}
81
85
 
82
86
  Changed files:
83
- ${Xe.map(go=>`- ${go}`).join(`
84
- `)}`,template:A},ne=await b.createJob(Re);console.log(` Job created: ${ne.jobId}`),console.log(` Dashboard: ${b.dashboardUrl(ne.jobId)}
87
+ ${k.map(P=>`- ${P}`).join(`
88
+ `)}`,template:S},W=await f.createJob(oe);console.log(` Job created: ${W.jobId}`),console.log(` Dashboard: ${f.dashboardUrl(W.jobId)}
85
89
  `),console.log(` Watching for more changes...
86
- `)}catch(Re){let ne=f(Re);g.outputError(ne.message,ne.details),console.log(`
90
+ `)}catch(oe){let W=g(oe);m.outputError(W.message,W.details),console.log(`
87
91
  Watching for more changes...
88
- `)}},l)}),Y.on("error",X=>{console.error(`
89
- \u274C Watch error: ${X instanceof Error?X.message:String(X)}
90
- `)});let Ye=()=>{console.log(`
92
+ `)}},u)}),V.on("error",L=>{console.error(`
93
+ \u274C Watch error: ${L instanceof Error?L.message:String(L)}
94
+ `)});let te=()=>{console.log(`
91
95
 
92
96
  Stopping watch mode...
93
- `),Y.close(),de(),process.exit(0)};process.on("SIGINT",Ye),process.on("SIGTERM",Ye)}catch(n){let o=f(n);new d({}).outputError(o.message,o.details),de(),process.exit(o.exitCode)}}),i}function Mr(i){let t=Tt(Et(),".config","runhuman");Ae(t)||Or(t,{recursive:!0}),Nr(H,i.toString())}function de(){Ae(H)&&Dr(H)}function vt(){if(!Ae(H))return!1;try{let i=parseInt(He(H,"utf-8"));return process.kill(i,0),!0}catch{return de(),!1}}async function _r(){if(!Ae(H)){console.log(`
97
+ `),V.close(),je(),process.exit(0)};process.on("SIGINT",te),process.on("SIGTERM",te)}catch(n){let o=g(n);new d({}).outputError(o.message,o.details),je(),process.exit(o.exitCode)}}),i}function di(i){let t=Ft(qt(),".config","runhuman");Me(t)||ci(t,{recursive:!0}),li(Y,i.toString())}function je(){Me(Y)&&ui(Y)}function Jt(){if(!Me(Y))return!1;try{let i=parseInt(rt(Y,"utf-8"));return process.kill(i,0),!0}catch{return je(),!1}}async function pi(){if(!Me(Y)){console.log(`
94
98
  Watch mode is not running
95
- `);return}try{let i=parseInt(He(H,"utf-8"));process.kill(i,"SIGTERM"),de(),console.log(`
99
+ `);return}try{let i=parseInt(rt(Y,"utf-8"));process.kill(i,"SIGTERM"),je(),console.log(`
96
100
  Watch mode stopped
97
- `)}catch{de(),console.log(`
101
+ `)}catch{je(),console.log(`
98
102
  Watch process not found (already stopped)
99
- `)}}function zr(){if(vt()){let i=parseInt(He(H,"utf-8"));console.log(`
103
+ `)}}function mi(){if(Jt()){let i=parseInt(rt(Y,"utf-8"));console.log(`
100
104
  Watch mode is running`),console.log(` PID: ${i}`),console.log(`
101
105
  Stop with: runhuman watch --stop
102
106
  `)}else console.log(`
103
107
  Watch mode is not running
104
- `)}v();E();S();T();import{Command as qr}from"commander";import Lr from"http";import{URL as $r}from"url";import Fr from"open";async function Te(i){let{apiUrl:t,autoOpenBrowser:e=!0}=i;return new Promise((n,o)=>{let r=Lr.createServer((u,l)=>{if(!u.url){l.writeHead(400),l.end("Bad request");return}let s=new $r(u.url,"http://localhost");if(s.pathname==="/callback"){let p=s.searchParams.get("token"),m=s.searchParams.get("email"),g=s.searchParams.get("projectId")||"",b=s.searchParams.get("error");if(b){l.writeHead(200,{"Content-Type":"text/html"}),l.end(Rt(b)),r.close(),o(new Error(b));return}if(p&&m){l.writeHead(200,{"Content-Type":"text/html"}),l.end(Jr(m)),r.close(),n({token:p,email:m,projectId:g});return}l.writeHead(400,{"Content-Type":"text/html"}),l.end(Rt("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 u=r.address();if(!u||typeof u=="string"){o(new Error("Failed to start local server"));return}let s=`http://127.0.0.1:${u.port}/callback`,p=`${t}/cli/auth?callback=${encodeURIComponent(s)}`;if(console.log(""),e){console.log("Opening browser for authentication...");try{await Fr(p)}catch{console.log("Could not open browser automatically."),console.log(`
105
- Please visit: ${p}`)}}else console.log(`Please visit: ${p}`);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",u=>{clearTimeout(c),o(new Error(`Failed to start local server: ${u.message}`))})})}function Jr(i){return`<!DOCTYPE html>
108
+ `)}R();x();T();v();import{Command as yi}from"commander";import fi from"http";import{URL as gi}from"url";import hi from"open";async function ze(i){let{apiUrl:t,autoOpenBrowser:e=!0}=i;return new Promise((n,o)=>{let r=fi.createServer((l,u)=>{if(!l.url){u.writeHead(400),u.end("Bad request");return}let a=new gi(l.url,"http://localhost");if(a.pathname==="/callback"){let p=a.searchParams.get("token"),m=a.searchParams.get("email"),f=a.searchParams.get("projectId")||"",w=a.searchParams.get("error");if(w){u.writeHead(200,{"Content-Type":"text/html"}),u.end(Kt(w)),r.close(),o(new Error(w));return}if(p&&m){u.writeHead(200,{"Content-Type":"text/html"}),u.end(bi(m)),r.close(),n({token:p,email:m,projectId:f});return}u.writeHead(400,{"Content-Type":"text/html"}),u.end(Kt("Missing token or email in callback")),r.close(),o(new Error("Missing token or email in callback"));return}u.writeHead(404),u.end("Not found")});r.listen(0,"127.0.0.1",async()=>{let l=r.address();if(!l||typeof l=="string"){o(new Error("Failed to start local server"));return}let a=`http://127.0.0.1:${l.port}/callback`,p=`${t}/cli/auth?callback=${encodeURIComponent(a)}`;if(console.log(""),e){console.log("Opening browser for authentication...");try{await hi(p)}catch{console.log("Could not open browser automatically."),console.log(`
109
+ Please visit: ${p}`)}}else console.log(`Please visit: ${p}`);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",l=>{clearTimeout(c),o(new Error(`Failed to start local server: ${l.message}`))})})}function bi(i){return`<!DOCTYPE html>
106
110
  <html>
107
111
  <head>
108
112
  <title>Runhuman CLI - Authenticated</title>
@@ -144,7 +148,7 @@ Please visit: ${p}`)}}else console.log(`Please visit: ${p}`);console.log(""),con
144
148
  <p class="close-note">You can close this tab and return to the terminal.</p>
145
149
  </div>
146
150
  </body>
147
- </html>`}function Rt(i){return`<!DOCTYPE html>
151
+ </html>`}function Kt(i){return`<!DOCTYPE html>
148
152
  <html>
149
153
  <head>
150
154
  <title>Runhuman CLI - Authentication Failed</title>
@@ -187,46 +191,49 @@ Please visit: ${p}`)}}else console.log(`Please visit: ${p}`);console.log(""),con
187
191
  <div class="error">${i}</div>
188
192
  </div>
189
193
  </body>
190
- </html>`}function xt(){let i=new qr("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 d({json:t.json,color:n.color});if(t.token)await Kr(e,o,t.token,t.json);else{let r=t.browser!==!1&&n.autoOpenBrowser!==!1;await Hr(e,o,n.apiUrl,r,t.json)}}catch(e){let n=f(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}async function Kr(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(d.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 Hr(i,t,e,n,o){o||console.log("Logging in to Runhuman...");let r=await Te({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(d.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)}E();S();T();import{Command as Gr}from"commander";function kt(){let i=new Gr("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 d({json:t.json});if(e.clearCredentials(),e.clearUserInfo(),t.json){let o=d.result({success:!0,message:"Logged out successfully"});n.output(o)}else n.success("Logged out successfully!"),console.log(` Credentials have been cleared.
191
- `)}catch(e){let n=f(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}v();E();S();T();import{Command as Vr}from"commander";function Pt(){let i=new Vr("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 d({json:t.json}),c=await new y(n).getCurrentUser();if(t.json){let u=d.result({user:c});o.output(u)}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=f(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}E();S();T();import{Command as Wr}from"commander";function Ut(){let i=new Wr("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 d({json:e.json});if(e.json){let c=d.result({[t]:o});r.output(c)}else console.log(o!==void 0?o:"(not set)")}catch(n){let o=f(n);new d({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 d({json:n.json});if(n.json){let c=d.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=f(o);new d({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 d({json:t.json});if(t.json){let r=d.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):
192
- `);for(let l of r){let s=n.effective[l],p=n.env?.[l],m=n.project?.[l],g=n.global?.[l],b=l==="apiKey",w=D=>b&&!t.showSecrets?Br(String(D)):String(D),x=p?"env":m?"project":g?"global":s?"default":null;console.log(` ${l}:`),console.log(s?` \u2713 ${w(s)} [from ${x}]`:" (not set)");let A=c[l];p&&console.log(` \u251C\u2500 env (${A}): ${w(p)}`),m&&console.log(` \u251C\u2500 project (.runhumanrc): ${w(m)}`),g&&console.log(` \u2514\u2500 global (~/.config/runhuman): ${w(g)}`),console.log()}let u=Object.keys(n.effective).filter(l=>!r.includes(l));if(u.length>0){console.log(`Other settings:
193
- `);for(let l of u){let s=n.effective[l];s!==void 0&&console.log(` ${l}:`.padEnd(24)+String(s))}console.log()}console.log("Environment Variables:"),console.log(" RUNHUMAN_ORGANIZATION Default organization ID"),console.log(" RUNHUMAN_PROJECT Default project ID"),console.log(" RUNHUMAN_API_KEY API key"),console.log(" RUNHUMAN_API_URL API URL"),console.log(" RUNHUMAN_NO_COLOR=1 Disable colors"),console.log(),o.hints([["Set value","runhuman config set <key> <value>"],["Get value","runhuman config get <key>"]])}}catch(e){let n=f(e);new d({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.
194
- `),process.exit(0)),await new h().reset(e);let o=new d({json:t.json});if(t.json){let r=d.result({success:!0,scope:e});o.output(r)}else o.success(`Reset ${e} configuration`)}catch(e){let n=f(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}function Br(i){return i.length<=8?"****":i.substring(0,4)+"*".repeat(i.length-8)+i.substring(i.length-4)}S();T();import{Command as Yr}from"commander";import Xr from"inquirer";import{writeFileSync as Qr}from"fs";import{join as Zr}from"path";function Ot(){let i=new Yr("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("--github-repos <repos>","GitHub repositories (comma-separated owner/repo)").option("-y, --yes","Skip all prompts (use defaults)").option("-j, --json","Output as JSON").action(async t=>{try{let e=new d({json:t.json});!t.json&&!t.yes&&(console.log(`Welcome to Runhuman!
194
+ </html>`}function Ht(){let i=new yi("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 d({json:t.json,color:n.color});if(t.token)await wi(e,o,t.token,t.json);else{let r=t.browser!==!1&&n.autoOpenBrowser!==!1;await Ii(e,o,n.apiUrl,r,t.json)}}catch(e){let n=g(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}async function wi(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(d.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 Ii(i,t,e,n,o){o||console.log("Logging in to Runhuman...");let r=await ze({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}),u=await new y(c).getCurrentUser();i.saveUserInfo(u),o?t.output(d.result({success:!0,user:u,projectId:r.projectId})):(console.log(""),t.success("Successfully logged in!"),t.detail("Email",u.email),t.detail("Account ID",u.accountId),t.detail("Default Project",r.projectId),console.log()),process.exit(0)}x();T();v();import{Command as ji}from"commander";function Gt(){let i=new ji("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 d({json:t.json});if(e.clearCredentials(),e.clearUserInfo(),t.json){let o=d.result({success:!0,message:"Logged out successfully"});n.output(o)}else n.success("Logged out successfully!"),console.log(` Credentials have been cleared.
195
+ `)}catch(e){let n=g(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}R();x();T();v();import{Command as Ci}from"commander";function Vt(){let i=new Ci("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 d({json:t.json}),c=await new y(n).getCurrentUser();if(t.json){let l=d.result({user:c});o.output(l)}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 d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}x();T();v();import{Command as Si}from"commander";function Wt(){let i=new Si("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 d({json:e.json});if(e.json){let c=d.result({[t]:o});r.output(c)}else console.log(o!==void 0?o:"(not set)")}catch(n){let o=g(n);new d({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 d({json:n.json});if(n.json){let c=d.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 d({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 d({json:t.json});if(t.json){let r=d.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):
196
+ `);for(let u of r){let a=n.effective[u],p=n.env?.[u],m=n.project?.[u],f=n.global?.[u],w=u==="apiKey",j=U=>w&&!t.showSecrets?Ti(String(U)):String(U),E=p?"env":m?"project":f?"global":a?"default":null;console.log(` ${u}:`),console.log(a?` \u2713 ${j(a)} [from ${E}]`:" (not set)");let S=c[u];p&&console.log(` \u251C\u2500 env (${S}): ${j(p)}`),m&&console.log(` \u251C\u2500 project (.runhumanrc): ${j(m)}`),f&&console.log(` \u2514\u2500 global (~/.config/runhuman): ${j(f)}`),console.log()}let l=Object.keys(n.effective).filter(u=>!r.includes(u));if(l.length>0){console.log(`Other settings:
197
+ `);for(let u of l){let a=n.effective[u];a!==void 0&&console.log(` ${u}:`.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 d({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.
198
+ `),process.exit(0)),await new h().reset(e);let o=new d({json:t.json});if(t.json){let r=d.result({success:!0,scope:e});o.output(r)}else o.success(`Reset ${e} configuration`)}catch(e){let n=g(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}function Ti(i){return i.length<=8?"****":i.substring(0,4)+"*".repeat(i.length-8)+i.substring(i.length-4)}T();v();import{Command as Ai}from"commander";import Ei from"inquirer";import{writeFileSync as vi}from"fs";import{join as ki}from"path";function Bt(){let i=new Ai("init");return i.description("Initialize a new Runhuman project with configuration").option("--name <text>","Project name").option("--github-repo <owner/repo>","GitHub repository").option("--github-repos <repos>","GitHub repositories (comma-separated owner/repo)").option("-y, --yes","Skip all prompts (use defaults)").option("-j, --json","Output as JSON").action(async t=>{try{let e=new d({json:t.json});!t.json&&!t.yes&&(console.log(`Welcome to Runhuman!
195
199
  `),console.log("Let's set up your project. This will create:"),console.log(" - A configuration file (.runhumanrc)"),console.log(` - Setup your project defaults
196
- `));let n=t.name,o=t.url,r=t.githubRepos?t.githubRepos.split(",").map(l=>l.trim()):t.githubRepo?[t.githubRepo]:void 0;if(!t.yes&&!t.json){let l=await Xr.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:"githubRepos",message:"Link to GitHub repositories (optional, comma-separated owner/repo):",when:!r}]);n=n||l.projectName,o=o||l.defaultUrl,!r&&l.githubRepos&&(r=l.githubRepos.split(",").map(s=>s.trim()).filter(Boolean))}let c={defaultUrl:o||void 0,githubRepos:r?.length?r:void 0,defaultDuration:5,defaultDeviceClass:"desktop"},u=Zr(process.cwd(),".runhumanrc");if(Qr(u,JSON.stringify(c,null,2)),t.json){let l=d.result({success:!0,configPath:u,config:c});e.output(l)}else e.success("Project initialized successfully!"),console.log(` Configuration saved to: .runhumanrc
200
+ `));let n=t.name,o=t.githubRepos?t.githubRepos.split(",").map(l=>l.trim()):t.githubRepo?[t.githubRepo]:void 0;if(!t.yes&&!t.json){let l=await Ei.prompt([{type:"input",name:"projectName",message:"Project name:",default:"My Project",when:!n},{type:"input",name:"githubRepos",message:"Link to GitHub repositories (optional, comma-separated owner/repo):",when:!o}]);n=n||l.projectName,!o&&l.githubRepos&&(o=l.githubRepos.split(",").map(u=>u.trim()).filter(Boolean))}let r={githubRepos:o?.length?o:void 0,defaultDuration:5,defaultDeviceClass:"desktop"},c=ki(process.cwd(),".runhumanrc");if(vi(c,JSON.stringify(r,null,2)),t.json){let l=d.result({success:!0,configPath:c,config:r});e.output(l)}else e.success("Project initialized successfully!"),console.log(` Configuration saved to: .runhumanrc
197
201
  `),console.log(` All set! Try creating your first test:
198
202
  `),console.log(` runhuman create https://example.com -d "Test homepage"
199
203
  `),console.log(` Learn more: https://runhuman.com/docs/cli
200
- `)}catch(e){let n=f(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}v();E();S();T();import{Command as ei}from"commander";import Nt from"inquirer";k();te();import ti from"cli-table3";function Dt(){let i=new ei("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 d({json:t.json,color:n.color}),r=new y(n),c=t.limit||20,u=t.offset||0,[{items:l,pagination:s},{items:p}]=await Promise.all([r.listProjects({limit:c,offset:u}),r.listOrganizations({})]),m=new Map(p.map(g=>[g.id,g.name]));if(t.json){let g=d.result({projects:l,pagination:s});o.output(g)}else{if(s.total===0){o.heading("Your Projects (0)"),console.log(`No projects found.
201
- `),o.hints([["Create a project","runhuman projects create <name> -o <org-id>"]]);return}o.heading(`Your Projects (${l.length} of ${s.total})`);let g=new ti({head:o.tableHead(["Name","Organization","Project ID","Created"]),colWidths:[25,25,25,15]});l.forEach(A=>{let D=m.get(A.organizationId)||"(unknown)";g.push([A.name,D,A.id,o.formatShortDate(A.createdAt)])}),console.log(g.toString());let b=u+1,w=u+l.length;console.log(`
202
- Showing ${b}-${w} of ${s.total} projects`);let x=[["View project","runhuman projects show <id>"],["Filter by org","runhuman orgs projects <org-id>"]];s.hasMore&&x.push(["Next page",`runhuman projects list --offset ${u+c}`]),console.log(),o.hints(x)}}catch(e){let n=f(e);new d({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 a GitHub repository").option("--github-repos <repos>","Link GitHub repositories (comma-separated owner/repo)").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 d({json:e.json,color:o.color}),c=new y(o),u=C("organization",e.organization,o,r),l=await I(c,"organization",u),s=await c.createProject({name:t,organizationId:l,defaultUrl:e.defaultUrl,githubRepos:e.githubRepos?e.githubRepos.split(",").map(p=>p.trim()):e.githubRepo?[e.githubRepo]:void 0});if(e.setDefault&&await n.set("project",s.id,!1),e.json){let p=d.result(s);r.output(p)}else r.success("Project created successfully!"),console.log(" Project ID: "+s.id),console.log(" Name: "+s.name),e.setDefault&&console.log(" Set as default project"),console.log()}catch(n){let o=f(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=await le(c,t,{},o,r),l=await c.getProject(u),[s,p]=await Promise.all([c.getOrganization(l.organizationId),c.getOrganizationBilling(l.organizationId)]);if(e.json){let m=d.result({project:l,organization:s,balance:{balance:p.balance,balanceUsd:p.balanceUsd}});r.output(m)}else r.heading("Project Details"),r.detail("Project ID",l.id),r.detail("Name",l.name),r.detail("Organization",s.name+" ("+s.id+")"),r.detail("Org Balance","$"+p.balanceUsd.toFixed(2)),l.defaultUrl&&r.detail("Default URL",l.defaultUrl),l.githubRepos?.length&&r.detail("GitHub Repos",l.githubRepos.map(m=>m.repo).join(", ")),r.detail("Created",r.formatTimestamp(l.createdAt)),r.detail("Last Updated",r.formatTimestamp(l.updatedAt)),console.log(),r.hints([["View organization","runhuman orgs show "+s.id],["List org projects","runhuman orgs projects "+s.id],["Check org balance","runhuman orgs balance "+s.id]])}catch(n){let o=f(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=await le(c,t,{},o,r),l=await c.getProject(u);if(await n.set("project",u,e.global!==!1),e.json){let s=d.result({success:!0,projectId:u,projectName:l.name,scope:e.global!==!1?"global":"local"});r.output(s)}else r.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(n){let o=f(n);new d({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("--add-repos <repos>","Add GitHub repositories (comma-separated owner/repo)").option("--remove-repos <repos>","Remove GitHub repositories (comma-separated owner/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 d({json:e.json,color:o.color}),c={name:e.name,defaultUrl:e.defaultUrl,addGithubRepos:e.addRepos?e.addRepos.split(",").map(p=>p.trim()):void 0,removeGithubRepos:e.removeRepos?e.removeRepos.split(",").map(p=>p.trim()):void 0},u=new y(o),l=await le(u,t,{},o,r),s=await u.updateProject(l,c);if(e.json){let p=d.result(s);r.output(p)}else r.success("Project updated successfully!")}catch(n){let o=f(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=await le(c,t,{},o,r),l=await I(c,"organization",e.toOrg,{requireFullId:{reason:"transfer target"}}),[s,p]=await Promise.all([c.getProject(u),c.getOrganization(l)]);if(!e.json&&!e.force){let{confirmed:g}=await Nt.prompt([{type:"confirm",name:"confirmed",message:`Transfer "${s.name}" to organization "${p.name}"? The current organization will lose access to this project.`,default:!1}]);if(!g)return}let m=await c.initiateTransfer(u,l);e.json?r.output(d.result(m)):m.type==="immediate"?(r.success(`Project transferred to ${p.name}!`),r.detail("Project",m.project.name),r.detail("New Organization",p.name),console.log(),r.hints([["View project","runhuman projects show "+u]])):(r.success("Transfer request sent (pending approval)."),r.detail("Transfer ID",m.transfer.id),r.detail("Project",s.name),r.detail("To Organization",p.name),console.log(),r.hints([["Check status","runhuman transfers list"],["Cancel transfer","runhuman transfers cancel "+m.transfer.id]]))}catch(n){let o=f(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=await I(c,"project",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.force){let{confirmed:l}=await Nt.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=d.result({success:!0});r.output(l)}else r.success("Project deleted successfully!")}catch(n){let o=f(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}v();E();S();T();k();import{Command as ii}from"commander";import Lt from"cli-table3";v();E();S();T();k();import{Command as Ve}from"commander";import oi from"cli-table3";import ni from"readline";function ri(i){let t=ni.createInterface({input:process.stdin,output:process.stdout});return new Promise(e=>{t.question(i,n=>{t.close(),e(n.toLowerCase()==="y")})})}function Mt(){let i=new Ve("members");return i.description("List members of an organization"),i.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 d({json:e.json,color:o.color}),c=new y(o),u=await I(c,"organization",t),[l,s]=await Promise.all([c.getOrganization(u),c.listOrganizationMembers(u)]);if(e.json){let p=d.result({organizationId:u,organizationName:l.name,members:s.items,total:s.total,memberLimit:s.memberLimit,activeMemberCount:s.activeMemberCount,suspendedMemberCount:s.suspendedMemberCount});r.output(p)}else{r.heading(`Members of ${l.name} (${s.activeMemberCount} active${s.suspendedMemberCount>0?`, ${s.suspendedMemberCount} suspended`:""})`);let p=new oi({head:r.tableHead(["Name","Email","Role","Status","Joined"]),colWidths:[22,28,10,12,14]});s.items.forEach(m=>{let g=[m.firstName,m.lastName].filter(Boolean).join(" ")||"-",b=m.isOwner?`${m.role} *`:m.role;p.push([g,m.email,b,m.status,r.formatShortDate(m.joinedAt)])}),console.log(p.toString()),console.log(),s.memberLimit>0&&(console.log(` Member limit: ${s.activeMemberCount}/${s.memberLimit}`),console.log()),r.hints([["Invite member",`runhuman orgs invite ${u} user@example.com`],["Remove member",`runhuman orgs remove ${u} <user-id>`]])}}catch(n){let o=f(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}function _t(){let i=new Ve("invite");return i.description("Invite a member to an organization"),i.argument("<organizationId>","Organization ID").argument("<email>","Email address to invite").option("-r, --role <role>","Role: admin, contributor, or viewer","contributor").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e,n)=>{try{let r=await new h().loadConfig({apiKey:n.apiKey,apiUrl:n.apiUrl}),c=new d({json:n.json,color:r.color}),u=new y(r),l=await I(u,"organization",t),s=await u.getOrganization(l),p={email:e,role:n.role},m=await u.inviteOrganizationMember(l,p);n.json?c.output(d.result({success:!0,organizationId:l,organizationName:s.name,email:e,role:n.role,invitationId:m.invitationId,message:m.message})):(c.success("Invitation sent"),console.log(),c.detail("Organization",s.name),c.detail("Email",e),c.detail("Role",n.role),c.detail("Invitation ID",m.invitationId),console.log(),console.log(` ${m.message}`),console.log(),c.hints([["View members",`runhuman orgs members ${l}`]]))}catch(o){let r=f(o);new d({json:n.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i}function zt(){let i=new Ve("remove");return i.description("Remove a member from an organization"),i.argument("<organizationId>","Organization ID").argument("<userId>","User ID to remove").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,n)=>{try{let r=await new h().loadConfig({apiKey:n.apiKey,apiUrl:n.apiUrl}),c=new d({json:n.json,color:r.color}),u=new y(r),l=await I(u,"organization",t),[s,p]=await Promise.all([u.getOrganization(l),u.listOrganizationMembers(l)]),m=p.items.find(b=>b.userId===e),g=m?[m.firstName,m.lastName].filter(Boolean).join(" ")||m.email:e;if(!n.force&&!n.json&&(console.log(` Remove ${g} from ${s.name}?`),console.log(" This will revoke their access to all projects in this organization."),console.log(),!await ri(" Continue? [y/N]: "))){console.log(" Cancelled.");return}await u.removeOrganizationMember(l,e),n.json?c.output(d.result({success:!0,organizationId:l,organizationName:s.name,removedUserId:e,removedMemberName:g})):(c.success(`Member '${g}' removed from organization '${s.name}'`),console.log(),c.hints([["View members",`runhuman orgs members ${l}`]]))}catch(o){let r=f(o);new d({json:n.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i}function $t(){let i=new ii("orgs");return i.description("Manage organizations"),i.addCommand(Mt()),i.addCommand(_t()),i.addCommand(zt()),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 d({json:t.json,color:n.color}),r=new y(n),{items:c,total:u}=await r.listOrganizations({limit:t.limit||20});if(t.json){let l=d.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 Lt({head:o.tableHead(["Organization ID","Name","Projects","Created"]),colWidths:[25,30,12,15]});c.forEach(s=>{l.push([s.id,s.name,s.projectCount?.toString()||"0",o.formatShortDate(s.createdAt)])}),console.log(l.toString()),console.log(),o.hints([["View org details","runhuman orgs show <id>"],["Check org balance","runhuman orgs balance <id>"]])}}catch(e){let n=f(e);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=await I(c,"organization",t),l=await c.getOrganization(u);if(e.json){let s=d.result(l);r.output(s)}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=f(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=await I(c,"organization",t),[l,s]=await Promise.all([c.getOrganization(u),c.getOrganizationBilling(u)]);if(e.json){let p=d.result({balance:s});r.output(p)}else r.heading(`Organization Balance: ${l.name}`),r.detail("Current Balance","$"+s.balanceUsd.toFixed(2)),r.detail("Billing Active",s.hasBilling?"Yes":"No"),r.detail("Estimated Tests","~"+Math.floor(s.balance)+" tests at $1.00 per test"),console.log(),s.hasBilling?r.hints([["Manage billing","https://runhuman.com/dashboard/organizations/"+u+"/billing"]]):r.hints([["Set up billing","https://runhuman.com/dashboard/organizations/"+u+"/billing"]])}catch(n){let o=f(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=await I(c,"organization",t),l=await c.getOrganization(u);if(await n.set("organization",u,e.global!==!1),e.json){let s=d.result({success:!0,organizationId:u,organizationName:l.name,scope:e.global!==!1?"global":"local"});r.output(s)}else r.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(n){let o=f(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=await I(c,"organization",t),[l,{items:s,total:p}]=await Promise.all([c.getOrganization(u),c.listOrganizationProjects(u,{limit:e.limit||50})]);if(e.json){let m=d.result({organizationId:u,projects:s,total:p});r.output(m)}else{if(p===0){r.heading(`Projects in ${l.name} (0 projects)`),console.log(`No projects found in this organization.
203
- `),r.hints([["Create a project","runhuman projects create <name> --organization "+u],["View organization","runhuman orgs show "+u]]);return}r.heading(`Projects in ${l.name} (${s.length} of ${p})`);let m=new Lt({head:r.tableHead(["Name","Project ID","Default URL","Created"]),colWidths:[25,25,30,15]});s.forEach(g=>{let b=g.defaultUrl?g.defaultUrl.length>27?g.defaultUrl.substring(0,24)+"...":g.defaultUrl:"-";m.push([g.name,g.id,b,r.formatShortDate(g.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 "+u],["View organization","runhuman orgs show "+u]])}}catch(n){let o=f(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}v();E();S();T();import{Command as ai}from"commander";import si from"inquirer";k();import ci from"cli-table3";function Jt(){let i=new ai("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 d({json:t.json,color:n.color}),r=new y(n),c=C("organization",t.organization,n,o),u=await I(r,"organization",c),{items:l,total:s}=await r.listApiKeys(u);if(t.json){let p=d.result({keys:l,total:s});o.output(p)}else{if(s===0){o.heading("API Keys (0)"),console.log(`No API keys found for this organization.
204
- `),o.hints([["Create a key",'runhuman keys create "Key Name" --organization '+u]]);return}o.heading(`API Keys (${l.length} of ${s})`);let p=new ci({head:o.tableHead(["Key ID","Name","Key","Last Used","Created"]),colWidths:[20,25,20,20,20]});l.forEach(m=>{let g=t.showKeys?m.key:Ft(m.key),b=m.lastUsedAt?o.formatShortDate(m.lastUsedAt):"Never";p.push([m.id,m.name,g,b,o.formatShortDate(m.createdAt)])}),console.log(p.toString()),console.log(),o.hints([["Create new key",'runhuman keys create "Key Name" --organization '+u],["Show full key","runhuman keys show <keyId>"]])}}catch(e){let n=f(e);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=C("organization",e.organization,o,r),l=await I(c,"organization",u),s=await c.createApiKey(l,t);if(e.json){let p=d.result(s);r.output(p)}else r.success("API Key created successfully!"),r.detail("Key ID",s.id),r.detail("Name",s.name),console.log(),r.detail("API Key",s.key),console.log(" "+"^".repeat(s.key.length)),console.log(` \u26A0\uFE0F Save this key! It won't be shown again.
205
- `),console.log(" Use this key:"),console.log(" export RUNHUMAN_API_KEY="+s.key),console.log(` runhuman create https://myapp.com -d "Test"
204
+ `)}catch(e){let n=g(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}R();x();T();v();import{Command as xi}from"commander";import Yt from"inquirer";N();we();import Ri from"cli-table3";function Xt(){let i=new xi("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 d({json:t.json,color:n.color}),r=new y(n),c=t.limit||20,l=t.offset||0,[{items:u,pagination:a},{items:p}]=await Promise.all([r.listProjects({limit:c,offset:l}),r.listOrganizations({})]),m=new Map(p.map(f=>[f.id,f.name]));if(t.json){let f=d.result({projects:u,pagination:a});o.output(f)}else{if(a.total===0){o.heading("Your Projects (0)"),console.log(`No projects found.
205
+ `),o.hints([["Create a project","runhuman projects create <name> -o <org-id>"]]);return}o.heading(`Your Projects (${u.length} of ${a.total})`);let f=new Ri({head:o.tableHead(["Name","Organization","Project ID","Created"]),colWidths:[25,25,25,15]});u.forEach(S=>{let U=m.get(S.organizationId)||"(unknown)";f.push([S.name,U,S.id,o.formatShortDate(S.createdAt)])}),console.log(f.toString());let w=l+1,j=l+u.length;console.log(`
206
+ Showing ${w}-${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 ${l+c}`]),console.log(),o.hints(E)}}catch(e){let n=g(e);new d({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 a GitHub repository").option("--github-repos <repos>","Link GitHub repositories (comma-separated owner/repo)").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 d({json:e.json,color:o.color}),c=new y(o),l=C("organization",e.organization,o,r),u=await b(c,"organization",l),a=await c.createProject({name:t,organizationId:u,defaultUrl:e.defaultUrl,githubRepos:e.githubRepos?e.githubRepos.split(",").map(p=>p.trim()):e.githubRepo?[e.githubRepo]:void 0});if(e.setDefault&&await n.set("project",a.id,!1),e.json){let p=d.result(a);r.output(p)}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 d({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 d({json:e.json,color:o.color}),c=new y(o),l=await ye(c,t,{},o,r),u=await c.getProject(l),[a,p]=await Promise.all([c.getOrganization(u.organizationId),c.getOrganizationBilling(u.organizationId)]);if(e.json){let m=d.result({project:u,organization:a,balance:{balance:p.balance,balanceUsd:p.balanceUsd}});r.output(m)}else r.heading("Project Details"),r.detail("Project ID",u.id),r.detail("Name",u.name),r.detail("Organization",a.name+" ("+a.id+")"),r.detail("Org Balance","$"+p.balanceUsd.toFixed(2)),u.defaultUrl&&r.detail("Default URL",u.defaultUrl),u.githubRepos?.length&&r.detail("GitHub Repos",u.githubRepos.map(m=>m.repo).join(", ")),r.detail("Created",r.formatTimestamp(u.createdAt)),r.detail("Last Updated",r.formatTimestamp(u.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 d({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 d({json:e.json,color:o.color}),c=new y(o),l=await ye(c,t,{},o,r),u=await c.getProject(l);if(await n.set("project",l,e.global!==!1),e.json){let a=d.result({success:!0,projectId:l,projectName:u.name,scope:e.global!==!1?"global":"local"});r.output(a)}else r.success(`Default project set to: ${u.name} (${l})`),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 d({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("--add-repos <repos>","Add GitHub repositories (comma-separated owner/repo)").option("--remove-repos <repos>","Remove GitHub repositories (comma-separated owner/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 d({json:e.json,color:o.color}),c={name:e.name,defaultUrl:e.defaultUrl,addGithubRepos:e.addRepos?e.addRepos.split(",").map(p=>p.trim()):void 0,removeGithubRepos:e.removeRepos?e.removeRepos.split(",").map(p=>p.trim()):void 0},l=new y(o),u=await ye(l,t,{},o,r),a=await l.updateProject(u,c);if(e.json){let p=d.result(a);r.output(p)}else r.success("Project updated successfully!")}catch(n){let o=g(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),l=await ye(c,t,{},o,r),u=await b(c,"organization",e.toOrg,{requireFullId:{reason:"transfer target"}}),[a,p]=await Promise.all([c.getProject(l),c.getOrganization(u)]);if(!e.json&&!e.force){let{confirmed:f}=await Yt.prompt([{type:"confirm",name:"confirmed",message:`Transfer "${a.name}" to organization "${p.name}"? The current organization will lose access to this project.`,default:!1}]);if(!f)return}let m=await c.initiateTransfer(l,u);e.json?r.output(d.result(m)):m.type==="immediate"?(r.success(`Project transferred to ${p.name}!`),r.detail("Project",m.project.name),r.detail("New Organization",p.name),console.log(),r.hints([["View project","runhuman projects show "+l]])):(r.success("Transfer request sent (pending approval)."),r.detail("Transfer ID",m.transfer.id),r.detail("Project",a.name),r.detail("To Organization",p.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 d({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 d({json:e.json,color:o.color}),c=new y(o),l=await b(c,"project",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.force){let{confirmed:u}=await Yt.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete project ${l} and all its data? This cannot be undone.`,default:!1}]);if(!u)return}if(await c.deleteProject(l),e.json){let u=d.result({success:!0});r.output(u)}else r.success("Project deleted successfully!")}catch(n){let o=g(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}R();x();T();v();N();import{Command as Ui}from"commander";import to from"cli-table3";R();x();T();v();N();import{Command as st}from"commander";import Ni from"cli-table3";import Oi from"readline";function Pi(i){let t=Oi.createInterface({input:process.stdin,output:process.stdout});return new Promise(e=>{t.question(i,n=>{t.close(),e(n.toLowerCase()==="y")})})}function Qt(){let i=new st("members");return i.description("List members of an organization"),i.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 d({json:e.json,color:o.color}),c=new y(o),l=await b(c,"organization",t),[u,a]=await Promise.all([c.getOrganization(l),c.listOrganizationMembers(l)]);if(e.json){let p=d.result({organizationId:l,organizationName:u.name,members:a.items,total:a.total,memberLimit:a.memberLimit,activeMemberCount:a.activeMemberCount,suspendedMemberCount:a.suspendedMemberCount});r.output(p)}else{r.heading(`Members of ${u.name} (${a.activeMemberCount} active${a.suspendedMemberCount>0?`, ${a.suspendedMemberCount} suspended`:""})`);let p=new Ni({head:r.tableHead(["Name","Email","Role","Status","Joined"]),colWidths:[22,28,10,12,14]});a.items.forEach(m=>{let f=[m.firstName,m.lastName].filter(Boolean).join(" ")||"-",w=m.isOwner?`${m.role} *`:m.role;p.push([f,m.email,w,m.status,r.formatShortDate(m.joinedAt)])}),console.log(p.toString()),console.log(),a.memberLimit>0&&(console.log(` Member limit: ${a.activeMemberCount}/${a.memberLimit}`),console.log()),r.hints([["Invite member",`runhuman orgs invite ${l} user@example.com`],["Remove member",`runhuman orgs remove ${l} <user-id>`]])}}catch(n){let o=g(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}function Zt(){let i=new st("invite");return i.description("Invite a member to an organization"),i.argument("<organizationId>","Organization ID").argument("<email>","Email address to invite").option("--role <role>","Role: admin, contributor, or viewer","contributor").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e,n)=>{try{let r=await new h().loadConfig({apiKey:n.apiKey,apiUrl:n.apiUrl}),c=new d({json:n.json,color:r.color}),l=new y(r),u=await b(l,"organization",t),a=await l.getOrganization(u),p={email:e,role:n.role},m=await l.inviteOrganizationMember(u,p);n.json?c.output(d.result({success:!0,organizationId:u,organizationName:a.name,email:e,role:n.role,invitationId:m.invitationId,message:m.message})):(c.success("Invitation sent"),console.log(),c.detail("Organization",a.name),c.detail("Email",e),c.detail("Role",n.role),c.detail("Invitation ID",m.invitationId),console.log(),console.log(` ${m.message}`),console.log(),c.hints([["View members",`runhuman orgs members ${u}`]]))}catch(o){let r=g(o);new d({json:n.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i}function eo(){let i=new st("remove");return i.description("Remove a member from an organization"),i.argument("<organizationId>","Organization ID").argument("<userId>","User ID to remove").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,n)=>{try{let r=await new h().loadConfig({apiKey:n.apiKey,apiUrl:n.apiUrl}),c=new d({json:n.json,color:r.color}),l=new y(r),u=await b(l,"organization",t),[a,p]=await Promise.all([l.getOrganization(u),l.listOrganizationMembers(u)]),m=p.items.find(w=>w.userId===e),f=m?[m.firstName,m.lastName].filter(Boolean).join(" ")||m.email:e;if(!n.force&&!n.json&&(console.log(` Remove ${f} from ${a.name}?`),console.log(" This will revoke their access to all projects in this organization."),console.log(),!await Pi(" Continue? [y/N]: "))){console.log(" Cancelled.");return}await l.removeOrganizationMember(u,e),n.json?c.output(d.result({success:!0,organizationId:u,organizationName:a.name,removedUserId:e,removedMemberName:f})):(c.success(`Member '${f}' removed from organization '${a.name}'`),console.log(),c.hints([["View members",`runhuman orgs members ${u}`]]))}catch(o){let r=g(o);new d({json:n.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),i}function oo(){let i=new Ui("orgs");return i.description("Manage organizations"),i.addCommand(Qt()),i.addCommand(Zt()),i.addCommand(eo()),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 d({json:t.json,color:n.color}),r=new y(n),{items:c,total:l}=await r.listOrganizations({limit:t.limit||20});if(t.json){let u=d.result({organizations:c,pagination:{total:l,limit:t.limit||20,offset:0,hasMore:c.length<l}});o.output(u)}else{o.heading(`Your Organizations (${c.length} of ${l})`);let u=new to({head:o.tableHead(["Organization ID","Name","Projects","Created"]),colWidths:[25,30,12,15]});c.forEach(a=>{u.push([a.id,a.name,a.projectCount?.toString()||"0",o.formatShortDate(a.createdAt)])}),console.log(u.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 d({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 d({json:e.json,color:o.color}),c=new y(o),l=await b(c,"organization",t),u=await c.getOrganization(l);if(e.json){let a=d.result(u);r.output(a)}else r.heading("Organization Details"),r.detail("Organization ID",u.id),r.detail("Name",u.name),r.detail("Created",r.formatTimestamp(u.createdAt)),console.log(),r.hints([["List projects","runhuman orgs projects "+u.id],["Check balance","runhuman orgs balance "+u.id]])}catch(n){let o=g(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),l=await b(c,"organization",t),[u,a]=await Promise.all([c.getOrganization(l),c.getOrganizationBilling(l)]);if(e.json){let p=d.result({balance:a});r.output(p)}else r.heading(`Organization Balance: ${u.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/"+l+"/billing"]]):r.hints([["Set up billing","https://runhuman.com/dashboard/organizations/"+l+"/billing"]])}catch(n){let o=g(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),l=await b(c,"organization",t),u=await c.getOrganization(l);if(await n.set("organization",l,e.global!==!1),e.json){let a=d.result({success:!0,organizationId:l,organizationName:u.name,scope:e.global!==!1?"global":"local"});r.output(a)}else r.success(`Default organization set to: ${u.name} (${l})`),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 d({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 d({json:e.json,color:o.color}),c=new y(o),l=await b(c,"organization",t),[u,{items:a,total:p}]=await Promise.all([c.getOrganization(l),c.listOrganizationProjects(l,{limit:e.limit||50})]);if(e.json){let m=d.result({organizationId:l,projects:a,total:p});r.output(m)}else{if(p===0){r.heading(`Projects in ${u.name} (0 projects)`),console.log(`No projects found in this organization.
207
+ `),r.hints([["Create a project","runhuman projects create <name> --organization "+l],["View organization","runhuman orgs show "+l]]);return}r.heading(`Projects in ${u.name} (${a.length} of ${p})`);let m=new to({head:r.tableHead(["Name","Project ID","Default URL","Created"]),colWidths:[25,25,30,15]});a.forEach(f=>{let w=f.defaultUrl?f.defaultUrl.length>27?f.defaultUrl.substring(0,24)+"...":f.defaultUrl:"-";m.push([f.name,f.id,w,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 "+l],["View organization","runhuman orgs show "+l]])}}catch(n){let o=g(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}R();x();T();v();import{Command as _i}from"commander";import Di from"inquirer";N();import Mi from"cli-table3";function ro(){let i=new _i("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 d({json:t.json,color:n.color}),r=new y(n),c=C("organization",t.organization,n,o),l=await b(r,"organization",c),{items:u,total:a}=await r.listApiKeys(l);if(t.json){let p=d.result({keys:u,total:a});o.output(p)}else{if(a===0){o.heading("API Keys (0)"),console.log(`No API keys found for this organization.
208
+ `),o.hints([["Create a key",'runhuman keys create "Key Name" --organization '+l]]);return}o.heading(`API Keys (${u.length} of ${a})`);let p=new Mi({head:o.tableHead(["Key ID","Name","Key","Last Used","Created"]),colWidths:[20,25,20,20,20]});u.forEach(m=>{let f=t.showKeys?m.key:no(m.key),w=m.lastUsedAt?o.formatShortDate(m.lastUsedAt):"Never";p.push([m.id,m.name,f,w,o.formatShortDate(m.createdAt)])}),console.log(p.toString()),console.log(),o.hints([["Create new key",'runhuman keys create "Key Name" --organization '+l],["Show full key","runhuman keys show <keyId>"]])}}catch(e){let n=g(e);new d({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 d({json:e.json,color:o.color}),c=new y(o),l=C("organization",e.organization,o,r),u=await b(c,"organization",l),a=await c.createApiKey(u,t);if(e.json){let p=d.result(a);r.output(p)}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.
209
+ `),console.log(" Use this key:"),console.log(" export RUNHUMAN_API_KEY="+a.key),console.log(` runhuman create https://myapp.com -d "Test"
206
210
  `),console.log(" Store securely:"),console.log(" - Use environment variables (recommended)"),console.log(" - Use secret management tools"),console.log(` - Never commit to git!
207
- `)}catch(n){let o=f(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=await I(c,"key",t),l=await c.getApiKey(u);if(e.json){let s=d.result(l);r.output(s)}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:Ft(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=f(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=await I(c,"key",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.force){let{confirmed:l}=await si.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete API key ${u}? This cannot be undone.`,default:!1}]);if(!l)return}if(await c.deleteApiKey(u),e.json){let l=d.result({success:!0});r.output(l)}else r.success("API key deleted successfully!")}catch(n){let o=f(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}function Ft(i){return i.length<=12?"****":i.substring(0,8)+"*".repeat(i.length-12)+i.substring(i.length-4)}v();E();S();T();import{Command as li}from"commander";import qt from"inquirer";k();import ui from"cli-table3";async function di(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 qt.prompt([{type:"confirm",name:"overwrite",message:`A template named "${i}" already exists. Overwrite it?`,default:!1}]);return n}function Kt(){let i=new li("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 d({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),u=await I(r,"project",c),{items:l,pagination:s}=await r.listTemplates(u);if(t.json){let p=d.result({templates:l,pagination:s});o.output(p)}else{if(s.total===0){o.heading("Test Templates (0)"),console.log(`No templates found for this project.
208
- `),o.hints([["Create a template",'runhuman templates create "Template Name" --project '+u]]);return}o.heading(`Test Templates (${l.length} of ${s.total})`);let p=new ui({head:o.tableHead(["ID","Name","Description","Created"]),colWidths:[30,25,35,20]});l.forEach(m=>{let g=m.testDescription&&m.testDescription.length>30?m.testDescription.substring(0,27)+"...":m.testDescription||"-";p.push([m.id,m.name,g,o.formatShortDate(m.createdAt)])}),console.log(p.toString()),console.log(),o.hints([["Create new template",'runhuman templates create "Template Name" --project '+u],["View template","runhuman templates show <templateId>"]])}}catch(e){let n=f(e);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=C("project",e.project,o,r),l=await I(c,"project",u),s;if(e.schema){let{readFileSync:g}=await import("fs"),b=g(e.schema,"utf-8");s=JSON.parse(b)}else e.schemaInline&&(s=JSON.parse(e.schemaInline));let p={name:t,testDescription:e.description,targetDurationMinutes:e.duration,deviceClass:e.deviceClass,outputSchema:s},m;try{m=await c.createTemplate(l,p)}catch(g){if(!(g instanceof Q))throw g;if(!await di(t,e,r)){console.log("Cancelled.");return}let{items:w}=await c.listTemplates(l),x=w.find(A=>A.name.toLowerCase()===t.toLowerCase());if(!x)throw new Error(`Template "${t}" not found despite 409 conflict`);m=await c.updateTemplate(l,x.id,p),e.json?r.output(d.result(m)):(r.success("Template overwritten successfully!"),r.detail("Template ID",m.id),r.detail("Name",m.name));return}e.json?r.output(d.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=f(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=C("project",e.project,o,r),l=await I(c,"project",u),s=await I(c,"template",t),p=await c.getTemplate(l,s);if(e.json){let m=d.result(p);r.output(m)}else r.heading("Template Details"),r.detail("Template ID",p.id),r.detail("Name",p.name),r.detail("Description",p.testDescription||"-"),r.detail("Project",p.projectId),p.targetDurationMinutes&&r.detail("Duration",p.targetDurationMinutes+" minutes"),p.deviceClass&&r.detail("Device Class",p.deviceClass),r.detail("Created",r.formatTimestamp(p.createdAt)),p.outputSchema&&(console.log(`
209
- Output Schema:`),console.log(JSON.stringify(p.outputSchema,null,2))),console.log(),r.hints([["Use this template",'runhuman create https://myapp.com --template "'+p.name+'"']])}catch(n){let o=f(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=C("project",e.project,o,r),l=await I(c,"project",u),s=await I(c,"template",t),p;if(e.schema){let{readFileSync:w}=await import("fs"),x=w(e.schema,"utf-8");p=JSON.parse(x)}else e.schemaInline&&(p=JSON.parse(e.schemaInline));let m={name:e.name,testDescription:e.description,targetDurationMinutes:e.duration,deviceClass:e.deviceClass,outputSchema:p},g=Object.fromEntries(Object.entries(m).filter(([,w])=>w!==void 0));if(Object.keys(g).length===0)throw new Error("No updates provided. Use --name, --description, --duration, --device-class, or --schema");let b=await c.updateTemplate(l,s,m);if(e.json){let w=d.result(b);r.output(w)}else r.success("Template updated successfully!"),r.detail("Template ID",b.id),r.detail("Name",b.name),console.log()}catch(n){let o=f(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=C("project",e.project,o,r),l=await I(c,"project",u),s=await I(c,"template",t);if(!e.json&&!e.force){let{confirmed:p}=await qt.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete template ${s}? This cannot be undone.`,default:!1}]);if(!p)return}if(await c.deleteTemplate(l,s),e.json){let p=d.result({success:!0});r.output(p)}else r.success("Template deleted successfully!")}catch(n){let o=f(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}v();E();S();T();import{Command as Si}from"commander";k();function We(i,t){return`${String(i).padStart(2,"0")}:${String(t).padStart(2,"0")}`}function Ee(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 ve(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 pi(i){return i.length===0?"-":i.map(t=>je[t]).join(", ")}function Be(i,t,e,n){let o=we[i];switch(i){case"weekly":case"biweekly":return t.length>0?`${o} on ${pi(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 oe(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",Be(t.frequency,t.weekdays,t.dayOfMonth,t.scheduledDate)),i.detail("Time",`${We(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))}v();E();S();T();import{Command as mi}from"commander";k();function Ht(){return new mi("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 d({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),u=await I(r,"project",c),l=await I(r,"template",t.template),s=gi(t.frequency),{hour:p,minute:m}=t.time?Ee(t.time):{hour:9,minute:0},g=t.timezone||"America/New_York",b={name:i,templateId:l,frequency:s,scheduledHour:p,scheduledMinute:m,timezone:g};t.weekdays&&(b.weekdays=ve(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),fi(b);let w=await r.createSchedule(u,b);t.json?o.output(d.result(w)):(o.success("Schedule created successfully!"),oe(o,w),console.log(),o.hints([["View schedule",`runhuman schedules show ${w.id}`],["List schedules",`runhuman schedules list --project ${u}`]]))}catch(e){let n=f(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}function gi(i){if(!i)return"weekly";if(!q.includes(i))throw new R(`Invalid frequency "${i}". Must be one of: ${q.join(", ")}`);return i}function fi(i){if(i.frequency==="monthly"&&i.dayOfMonth===void 0)throw new R("Monthly schedules require --day-of-month (1-28, or 0 for last day).");if(i.frequency==="once"&&!i.scheduledDate)throw new R("One-time schedules require --date (YYYY-MM-DD).");if((i.frequency==="weekly"||i.frequency==="biweekly")&&(!i.weekdays||i.weekdays.length===0))throw new R(`${i.frequency} schedules require --weekdays (e.g., mon,wed,fri).`)}v();E();S();T();import{Command as hi}from"commander";k();function Gt(){return new hi("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 d({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),u=await I(r,"project",c),l=await I(r,"schedule",i),s=await r.getSchedule(u,l);t.json?o.output(d.result(s)):(o.heading("Schedule Details"),oe(o,s),console.log(),o.hints([["View history",`runhuman schedules history ${s.id}`],["Pause schedule",`runhuman schedules pause ${s.id}`],["Update schedule",`runhuman schedules update ${s.id} --frequency daily`]]))}catch(e){let n=f(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}v();E();S();T();import{Command as bi}from"commander";k();function Vt(){return new bi("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 d({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),u=await I(r,"project",c),l=await I(r,"schedule",i),s=yi(t);if(s.templateId&&(s.templateId=await I(r,"template",s.templateId)),Object.keys(s).length===0)throw new R("No updates provided. Use --name, --template, --frequency, --time, --timezone, --weekdays, --day-of-month, --date, --url, --status, or --auto-github-issues.");let p=await r.updateSchedule(u,l,s);t.json?o.output(d.result(p)):(o.success("Schedule updated successfully!"),oe(o,p),console.log())}catch(e){let n=f(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}function yi(i){let t={};if(i.name&&(t.name=i.name),i.template&&(t.templateId=i.template),i.frequency){let e=i.frequency;if(!q.includes(e))throw new R(`Invalid frequency "${e}". Must be one of: ${q.join(", ")}`);t.frequency=e}if(i.time){let{hour:e,minute:n}=Ee(i.time);t.scheduledHour=e,t.scheduledMinute=n}if(i.timezone&&(t.timezone=i.timezone),i.weekdays&&(t.weekdays=ve(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 R('Status must be "active" or "paused".');t.status=e}return t}v();E();S();T();import{Command as Ii}from"commander";import wi from"inquirer";k();function Wt(){return new Ii("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 d({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),u=await I(r,"project",c),l=await I(r,"schedule",i,{requireFullId:{reason:"deletion"}});if(!t.json&&!t.force){let{confirmed:s}=await wi.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete schedule ${l}? This cannot be undone.`,default:!1}]);if(!s)return}await r.deleteSchedule(u,l),t.json?o.output(d.result({success:!0})):o.success("Schedule deleted successfully!")}catch(e){let n=f(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}v();E();S();T();import{Command as ji}from"commander";k();import Ci from"cli-table3";function Bt(){return new ji("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 d({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),u=await I(r,"project",c),l=await I(r,"schedule",i),s=t.limit||20,p=t.offset||0,{items:m,pagination:g}=await r.listScheduleExecutions(u,l,{limit:s,offset:p});if(t.json){o.output(d.result({executions:m,pagination:g}));return}if(g.total===0){o.heading("Execution History (0)"),console.log(`No executions recorded for this schedule yet.
210
- `);return}o.heading(`Execution History (${m.length} of ${g.total})`);let b=new Ci({head:o.tableHead(["Execution ID","Status","Job ID","Scheduled For","Executed At"]),colWidths:[12,28,12,22,22]});for(let w of m)b.push([w.id.substring(0,8)+"\u2026",o.formatStatus(w.status),w.jobId?w.jobId.substring(0,8)+"\u2026":"-",o.formatTimestamp(w.scheduledAt),o.formatTimestamp(w.executedAt)]);console.log(b.toString()),g.hasMore?(console.log(`
211
- Showing ${p+1}-${p+m.length} of ${g.total}`),o.hints([["Next page",`runhuman schedules history ${l} --offset ${p+s}`]])):console.log()}catch(e){let n=f(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}import Ai from"cli-table3";function Xt(){let i=new Si("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 d({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),u=await I(r,"project",c),l=t.limit||20,s=t.offset||0,{items:p,pagination:m}=await r.listSchedules(u,{limit:l,offset:s});if(t.json){o.output(d.result({schedules:p,pagination:m}));return}if(m.total===0){o.heading("Schedules (0)"),console.log(`No schedules found for this project.
212
- `),o.hints([["Create a schedule",'runhuman schedules create "My Schedule" --template <id> --project '+u]]);return}o.heading(`Schedules (${p.length} of ${m.total})`);let g=new Ai({head:o.tableHead(["ID","Name","Template","Frequency","Time","Status","Next Run"]),colWidths:[12,18,18,22,8,10,20]});for(let b of p){let w=Be(b.frequency,b.weekdays,b.dayOfMonth,b.scheduledDate);g.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,w.length>20?w.substring(0,17)+"\u2026":w,We(b.scheduledHour,b.scheduledMinute),o.formatStatus(b.status),o.formatShortDate(b.nextExecutionAt)])}console.log(g.toString()),m.hasMore?(console.log(`
213
- Showing ${s+1}-${s+p.length} of ${m.total}`),o.hints([["Next page",`runhuman schedules list --offset ${s+l} --project ${u}`],["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 '+u]]))}catch(e){let n=f(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i.addCommand(Ht()),i.addCommand(Gt()),i.addCommand(Vt()),i.addCommand(Wt()),i.addCommand(Bt()),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 Yt(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 Yt(t,"active",e)}),i}async function Yt(i,t,e){try{let o=await new h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new d({json:e.json,color:o.color}),c=new y(o),u=C("project",e.project,o,r),l=await I(c,"project",u),s=await I(c,"schedule",i),p=await c.updateSchedule(l,s,{status:t});if(e.json)r.output(d.result(p));else{let m=t==="paused"?"paused":"resumed";r.success(`Schedule ${m} successfully!`),r.detail("Schedule ID",p.id),r.detail("Name",p.name),r.detail("Status",p.status),console.log()}}catch(n){let o=f(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}v();E();S();T();import{Command as Ti}from"commander";k();import Qt from"cli-table3";function Zt(){let i=new Ti("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 d({json:e.json,color:o.color}),c=new y(o),u=C("project",e.project,o,r),l=await I(c,"project",u);if(!t.match(/^([^/]+)\/([^/]+)$/))throw new Error("Invalid repository format. Use: owner/repo");if(await c.updateProject(l,{addGithubRepos:[t]}),e.json){let p=d.result({success:!0,repository:t});r.output(p)}else r.success("GitHub repository linked successfully!"),r.detail("Repository",t),r.detail("Project",l),console.log(),await n.saveProjectConfig({githubRepos:[t]}),console.log(` Repository saved to project config (.runhumanrc)
214
- `),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=f(n);new d({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 d({json:t.json,color:n.color}),r=new y(n),c=C("organization",t.organization,n,o),u=await I(r,"organization",c),l=await r.listGithubRepos(u,{search:t.search});if(t.json)o.output(d.result(l));else{o.heading(`GitHub Repositories (${l.items.length})`);let s=new Qt({head:o.tableHead(["Repository","Added"]),colWidths:[45,20]});l.items.forEach(p=>{s.push([p.fullName,o.formatShortDate(p.createdAt)])}),console.log(s.toString()),console.log(),o.hints([["Test an issue","runhuman github test <issueNumber> --repo owner/repo"]])}}catch(e){let n=f(e);new d({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 d({json:e.json,color:o.color}),c=t.match(/^([^/]+)\/([^/]+)$/);if(!c)throw new Error("Invalid repository format. Use: owner/repo");let[,u,l]=c,p=await new y(o).listGithubIssues(u,l,{state:e.state,labels:e.labels?.split(",")});if(e.json){let m=d.result({issues:p});r.output(m)}else{r.heading(`GitHub Issues for ${t} (${p.length})`);let m=new Qt({head:r.tableHead(["#","Title","State","Labels","Created"]),colWidths:[8,40,10,20,15]});p.forEach(g=>{let b=g.labels?.join(", ")||"-",w=b.length>18?b.substring(0,15)+"...":b;m.push(["#"+g.number,g.title.length>38?g.title.substring(0,35)+"...":g.title,g.state,w,r.formatShortDate(g.createdAt)])}),console.log(m.toString()),console.log(),r.hints([["Test an issue","runhuman github test <issueNumber> --repo "+t]])}}catch(n){let o=f(n);new d({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.githubRepos?.[0];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 d({json:e.json,color:o.color}),u=r.match(/^([^/]+)\/([^/]+)$/);if(!u)throw new Error("Invalid repository format. Use: owner/repo");let[,l,s]=u,p=new y(o),m=await p.getGithubIssue(l,s,parseInt(t)),g=C("project",void 0,o,c),w={projectId:await I(p,"project",g),url:e.url,description:`Test GitHub issue #${t}: ${m.title}
211
+ `)}catch(n){let o=g(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),l=await b(c,"key",t),u=await c.getApiKey(l);if(e.json){let a=d.result(u);r.output(a)}else r.heading("API Key Details"),r.detail("Key ID",u.id),r.detail("Name",u.name),r.detail("API Key",e.showKey?u.key:no(u.key)),r.detail("Created",r.formatTimestamp(u.createdAt)),u.lastUsedAt&&r.detail("Last Used",r.formatTimestamp(u.lastUsedAt)),console.log()}catch(n){let o=g(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),l=await b(c,"key",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.force){let{confirmed:u}=await Di.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete API key ${l}? This cannot be undone.`,default:!1}]);if(!u)return}if(await c.deleteApiKey(l),e.json){let u=d.result({success:!0});r.output(u)}else r.success("API key deleted successfully!")}catch(n){let o=g(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}function no(i){return i.length<=12?"****":i.substring(0,8)+"*".repeat(i.length-12)+i.substring(i.length-4)}R();x();T();v();import{Command as zi}from"commander";import io from"inquirer";N();import Li from"cli-table3";async function $i(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 io.prompt([{type:"confirm",name:"overwrite",message:`A template named "${i}" already exists. Overwrite it?`,default:!1}]);return n}function so(){let i=new zi("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 d({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),l=await b(r,"project",c),{items:u,pagination:a}=await r.listTemplates(l);if(t.json){let p=d.result({templates:u,pagination:a});o.output(p)}else{if(a.total===0){o.heading("Test Templates (0)"),console.log(`No templates found for this project.
212
+ `),o.hints([["Create a template",'runhuman templates create "Template Name" --project '+l]]);return}o.heading(`Test Templates (${u.length} of ${a.total})`);let p=new Li({head:o.tableHead(["ID","Name","Description","Created"]),colWidths:[30,25,35,20]});u.forEach(m=>{let f=m.testDescription&&m.testDescription.length>30?m.testDescription.substring(0,27)+"...":m.testDescription||"-";p.push([m.id,m.name,f,o.formatShortDate(m.createdAt)])}),console.log(p.toString()),console.log(),o.hints([["Create new template",'runhuman templates create "Template Name" --project '+l],["View template","runhuman templates show <templateId>"]])}}catch(e){let n=g(e);new d({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 d({json:e.json,color:o.color}),c=new y(o),l=C("project",e.project,o,r),u=await b(c,"project",l),a;if(e.schema){let{readFileSync:f}=await import("fs"),w=f(e.schema,"utf-8");a=JSON.parse(w)}else e.schemaInline&&(a=JSON.parse(e.schemaInline));let p={name:t,testDescription:e.description,targetDurationMinutes:e.duration,deviceClass:e.deviceClass,outputSchema:a},m;try{m=await c.createTemplate(u,p)}catch(f){if(!(f instanceof ne))throw f;if(!await $i(t,e,r)){console.log("Cancelled.");return}let{items:j}=await c.listTemplates(u),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(u,E.id,p),e.json?r.output(d.result(m)):(r.success("Template overwritten successfully!"),r.detail("Template ID",m.id),r.detail("Name",m.name));return}e.json?r.output(d.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 d({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 d({json:e.json,color:o.color}),c=new y(o),l=C("project",e.project,o,r),u=await b(c,"project",l),a=await b(c,"template",t),p=await c.getTemplate(u,a);if(e.json){let m=d.result(p);r.output(m)}else r.heading("Template Details"),r.detail("Template ID",p.id),r.detail("Name",p.name),r.detail("Description",p.testDescription||"-"),r.detail("Project",p.projectId),p.targetDurationMinutes&&r.detail("Duration",p.targetDurationMinutes+" minutes"),p.deviceClass&&r.detail("Device Class",p.deviceClass),r.detail("Created",r.formatTimestamp(p.createdAt)),p.outputSchema&&(console.log(`
213
+ Output Schema:`),console.log(JSON.stringify(p.outputSchema,null,2))),console.log(),r.hints([["Use this template",'runhuman create https://myapp.com --template "'+p.name+'"']])}catch(n){let o=g(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),l=C("project",e.project,o,r),u=await b(c,"project",l),a=await b(c,"template",t),p;if(e.schema){let{readFileSync:j}=await import("fs"),E=j(e.schema,"utf-8");p=JSON.parse(E)}else e.schemaInline&&(p=JSON.parse(e.schemaInline));let m={name:e.name,testDescription:e.description,targetDurationMinutes:e.duration,deviceClass:e.deviceClass,outputSchema:p},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 w=await c.updateTemplate(u,a,m);if(e.json){let j=d.result(w);r.output(j)}else r.success("Template updated successfully!"),r.detail("Template ID",w.id),r.detail("Name",w.name),console.log()}catch(n){let o=g(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),l=C("project",e.project,o,r),u=await b(c,"project",l),a=await b(c,"template",t);if(!e.json&&!e.force){let{confirmed:p}=await io.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete template ${a}? This cannot be undone.`,default:!1}]);if(!p)return}if(await c.deleteTemplate(u,a),e.json){let p=d.result({success:!0});r.output(p)}else r.success("Template deleted successfully!")}catch(n){let o=g(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}R();x();T();v();import{Command as Qi}from"commander";N();function at(i,t){return`${String(i).padStart(2,"0")}:${String(t).padStart(2,"0")}`}function Le(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 $e(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 Fi(i){return i.length===0?"-":i.map(t=>Pe[t]).join(", ")}function ct(i,t,e,n){let o=Oe[i];switch(i){case"weekly":case"biweekly":return t.length>0?`${o} on ${Fi(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 ue(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",ct(t.frequency,t.weekdays,t.dayOfMonth,t.scheduledDate)),i.detail("Time",`${at(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))}R();x();T();v();import{Command as qi}from"commander";N();function ao(){return new qi("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 d({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),l=await b(r,"project",c),u=await b(r,"template",t.template),a=Ji(t.frequency),{hour:p,minute:m}=t.time?Le(t.time):{hour:9,minute:0},f=t.timezone||"America/New_York",w={name:i,templateId:u,frequency:a,scheduledHour:p,scheduledMinute:m,timezone:f};t.weekdays&&(w.weekdays=$e(t.weekdays)),t.dayOfMonth!==void 0&&(w.dayOfMonth=t.dayOfMonth),t.date&&(w.scheduledDate=t.date),t.url&&(w.urlOverride=t.url),t.autoGithubIssues&&(w.autoCreateGithubIssuesOverride=!0),Ki(w);let j=await r.createSchedule(l,w);t.json?o.output(d.result(j)):(o.success("Schedule created successfully!"),ue(o,j),console.log(),o.hints([["View schedule",`runhuman schedules show ${j.id}`],["List schedules",`runhuman schedules list --project ${l}`]]))}catch(e){let n=g(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}function Ji(i){if(!i)return"weekly";if(!B.includes(i))throw new A(`Invalid frequency "${i}". Must be one of: ${B.join(", ")}`);return i}function Ki(i){if(i.frequency==="monthly"&&i.dayOfMonth===void 0)throw new A("Monthly schedules require --day-of-month (1-28, or 0 for last day).");if(i.frequency==="once"&&!i.scheduledDate)throw new A("One-time schedules require --date (YYYY-MM-DD).");if((i.frequency==="weekly"||i.frequency==="biweekly")&&(!i.weekdays||i.weekdays.length===0))throw new A(`${i.frequency} schedules require --weekdays (e.g., mon,wed,fri).`)}R();x();T();v();import{Command as Hi}from"commander";N();function co(){return new Hi("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 d({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),l=await b(r,"project",c),u=await b(r,"schedule",i),a=await r.getSchedule(l,u);t.json?o.output(d.result(a)):(o.heading("Schedule Details"),ue(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 d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}R();x();T();v();import{Command as Gi}from"commander";N();function lo(){return new Gi("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 d({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),l=await b(r,"project",c),u=await b(r,"schedule",i),a=Vi(t);if(a.templateId&&(a.templateId=await b(r,"template",a.templateId)),Object.keys(a).length===0)throw new A("No updates provided. Use --name, --template, --frequency, --time, --timezone, --weekdays, --day-of-month, --date, --url, --status, or --auto-github-issues.");let p=await r.updateSchedule(l,u,a);t.json?o.output(d.result(p)):(o.success("Schedule updated successfully!"),ue(o,p),console.log())}catch(e){let n=g(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}function Vi(i){let t={};if(i.name&&(t.name=i.name),i.template&&(t.templateId=i.template),i.frequency){let e=i.frequency;if(!B.includes(e))throw new A(`Invalid frequency "${e}". Must be one of: ${B.join(", ")}`);t.frequency=e}if(i.time){let{hour:e,minute:n}=Le(i.time);t.scheduledHour=e,t.scheduledMinute=n}if(i.timezone&&(t.timezone=i.timezone),i.weekdays&&(t.weekdays=$e(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 A('Status must be "active" or "paused".');t.status=e}return t}R();x();T();v();import{Command as Wi}from"commander";import Bi from"inquirer";N();function uo(){return new Wi("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 d({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),l=await b(r,"project",c),u=await b(r,"schedule",i,{requireFullId:{reason:"deletion"}});if(!t.json&&!t.force){let{confirmed:a}=await Bi.prompt([{type:"confirm",name:"confirmed",message:`Permanently delete schedule ${u}? This cannot be undone.`,default:!1}]);if(!a)return}await r.deleteSchedule(l,u),t.json?o.output(d.result({success:!0})):o.success("Schedule deleted successfully!")}catch(e){let n=g(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}R();x();T();v();import{Command as Yi}from"commander";N();import Xi from"cli-table3";function po(){return new Yi("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 d({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),l=await b(r,"project",c),u=await b(r,"schedule",i),a=t.limit||20,p=t.offset||0,{items:m,pagination:f}=await r.listScheduleExecutions(l,u,{limit:a,offset:p});if(t.json){o.output(d.result({executions:m,pagination:f}));return}if(f.total===0){o.heading("Execution History (0)"),console.log(`No executions recorded for this schedule yet.
214
+ `);return}o.heading(`Execution History (${m.length} of ${f.total})`);let w=new Xi({head:o.tableHead(["Execution ID","Status","Job ID","Scheduled For","Executed At"]),colWidths:[12,28,12,22,22]});for(let j of m)w.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(w.toString()),f.hasMore?(console.log(`
215
+ Showing ${p+1}-${p+m.length} of ${f.total}`),o.hints([["Next page",`runhuman schedules history ${u} --offset ${p+a}`]])):console.log()}catch(e){let n=g(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}})}import Zi from"cli-table3";function fo(){let i=new Qi("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 d({json:t.json,color:n.color}),r=new y(n),c=C("project",t.project,n,o),l=await b(r,"project",c),u=t.limit||20,a=t.offset||0,{items:p,pagination:m}=await r.listSchedules(l,{limit:u,offset:a});if(t.json){o.output(d.result({schedules:p,pagination:m}));return}if(m.total===0){o.heading("Schedules (0)"),console.log(`No schedules found for this project.
216
+ `),o.hints([["Create a schedule",'runhuman schedules create "My Schedule" --template <id> --project '+l]]);return}o.heading(`Schedules (${p.length} of ${m.total})`);let f=new Zi({head:o.tableHead(["ID","Name","Template","Frequency","Time","Status","Next Run"]),colWidths:[12,18,18,22,8,10,20]});for(let w of p){let j=ct(w.frequency,w.weekdays,w.dayOfMonth,w.scheduledDate);f.push([w.id.substring(0,8)+"\u2026",w.name.length>16?w.name.substring(0,13)+"\u2026":w.name,w.templateName.length>16?w.templateName.substring(0,13)+"\u2026":w.templateName,j.length>20?j.substring(0,17)+"\u2026":j,at(w.scheduledHour,w.scheduledMinute),o.formatStatus(w.status),o.formatShortDate(w.nextExecutionAt)])}console.log(f.toString()),m.hasMore?(console.log(`
217
+ Showing ${a+1}-${a+p.length} of ${m.total}`),o.hints([["Next page",`runhuman schedules list --offset ${a+u} --project ${l}`],["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 '+l]]))}catch(e){let n=g(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i.addCommand(ao()),i.addCommand(co()),i.addCommand(lo()),i.addCommand(uo()),i.addCommand(po()),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 mo(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 mo(t,"active",e)}),i}async function mo(i,t,e){try{let o=await new h().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),r=new d({json:e.json,color:o.color}),c=new y(o),l=C("project",e.project,o,r),u=await b(c,"project",l),a=await b(c,"schedule",i),p=await c.updateSchedule(u,a,{status:t});if(e.json)r.output(d.result(p));else{let m=t==="paused"?"paused":"resumed";r.success(`Schedule ${m} successfully!`),r.detail("Schedule ID",p.id),r.detail("Name",p.name),r.detail("Status",p.status),console.log()}}catch(n){let o=g(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}R();x();T();v();import{Command as es}from"commander";N();import go from"cli-table3";function ho(){let i=new es("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 d({json:e.json,color:o.color}),c=new y(o),l=C("project",e.project,o,r),u=await b(c,"project",l);if(!t.match(/^([^/]+)\/([^/]+)$/))throw new Error("Invalid repository format. Use: owner/repo");if(await c.updateProject(u,{addGithubRepos:[t]}),e.json){let p=d.result({success:!0,repository:t});r.output(p)}else r.success("GitHub repository linked successfully!"),r.detail("Repository",t),r.detail("Project",u),console.log(),await n.saveProjectConfig({githubRepos:[t]}),console.log(` Repository saved to project config (.runhumanrc)
218
+ `),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 d({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 d({json:t.json,color:n.color}),r=new y(n),c=C("organization",t.organization,n,o),l=await b(r,"organization",c),u=await r.listGithubRepos(l,{search:t.search});if(t.json)o.output(d.result(u));else{o.heading(`GitHub Repositories (${u.items.length})`);let a=new go({head:o.tableHead(["Repository","Added"]),colWidths:[45,20]});u.items.forEach(p=>{a.push([p.fullName,o.formatShortDate(p.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 d({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 d({json:e.json,color:o.color}),c=t.match(/^([^/]+)\/([^/]+)$/);if(!c)throw new Error("Invalid repository format. Use: owner/repo");let[,l,u]=c,p=await new y(o).listGithubIssues(l,u,{state:e.state,labels:e.labels?.split(",")});if(e.json){let m=d.result({issues:p});r.output(m)}else{r.heading(`GitHub Issues for ${t} (${p.length})`);let m=new go({head:r.tableHead(["#","Title","State","Labels","Created"]),colWidths:[8,40,10,20,15]});p.forEach(f=>{let w=f.labels?.join(", ")||"-",j=w.length>18?w.substring(0,15)+"...":w;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 d({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.githubRepos?.[0];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 d({json:e.json,color:o.color}),l=r.match(/^([^/]+)\/([^/]+)$/);if(!l)throw new Error("Invalid repository format. Use: owner/repo");let[,u,a]=l,p=new y(o),m=await p.getGithubIssue(u,a,parseInt(t)),f=C("project",void 0,o,c),j={projectId:await b(p,"project",f),url:e.url,description:`Test GitHub issue #${t}: ${m.title}
215
219
 
216
- ${m.body}`,metadata:{githubIssue:{owner:l,repo:s,number:parseInt(t),url:m.url}},template:e.template},x=await p.createJob(w);if(e.json){let A=d.result(x);c.output(A)}else if(c.success("QA test job created for issue #"+t),c.detail("Job ID",x.jobId),c.detail("Issue","#"+t+" - "+m.title),c.detail("URL",e.url),console.log(),c.hints([["Check status","runhuman status "+x.jobId]]),e.sync){let{waitForJob:A}=await Promise.resolve().then(()=>(Se(),Le));await A(x.jobId,p,c,600)}}catch(n){let o=f(n);new d({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.githubRepos?.[0];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 d({json:t.json,color:n.color}),c=o.match(/^([^/]+)\/([^/]+)$/);if(!c)throw new Error("Invalid repository format. Use: owner/repo");let[,u,l]=c,s=new y(n),p=await s.listGithubIssues(u,l,{state:t.state,labels:t.labels?.split(",")}),m=parseInt(t.limit),g=p.slice(0,m);if(g.length===0){console.log(`No issues found matching the criteria.
217
- `);return}let b=C("project",void 0,n,r),w=await I(s,"project",b);console.log(`
218
- Creating ${g.length} test jobs...
219
- `);let x=[];for(let A of g){let D={projectId:w,url:t.url,description:`Test GitHub issue #${A.number}: ${A.title}
220
+ ${m.body}`,metadata:{githubIssue:{repo:`${u}/${a}`,issueNumber:parseInt(t),issueUrl:m.url}},template:e.template},E=await p.createJob(j);if(e.json){let S=d.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("URL",e.url),console.log(),c.hints([["Check status","runhuman status "+E.jobId]]),e.sync){let{waitForJob:S}=await Promise.resolve().then(()=>(De(),Ze));await S(E.jobId,p,c,600)}}catch(n){let o=g(n);new d({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.githubRepos?.[0];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 d({json:t.json,color:n.color}),c=o.match(/^([^/]+)\/([^/]+)$/);if(!c)throw new Error("Invalid repository format. Use: owner/repo");let[,l,u]=c,a=new y(n),p=await a.listGithubIssues(l,u,{state:t.state,labels:t.labels?.split(",")}),m=parseInt(t.limit),f=p.slice(0,m);if(f.length===0){console.log(`No issues found matching the criteria.
221
+ `);return}let w=C("project",void 0,n,r),j=await b(a,"project",w);console.log(`
222
+ Creating ${f.length} test jobs...
223
+ `);let E=[];for(let S of f){let U={projectId:j,url:t.url,description:`Test GitHub issue #${S.number}: ${S.title}
220
224
 
221
- ${A.body}`,metadata:{githubIssue:{owner:u,repo:l,number:A.number,url:A.url}},template:t.template};try{let _=await s.createJob(D);x.push({issue:A.number,jobId:_.jobId,status:"created"}),console.log(` ok Issue #${A.number} \u2192 Job ${_.jobId}`)}catch(_){let Y=f(_);x.push({issue:A.number,error:Y.message,status:"failed"}),console.log(` FAIL Issue #${A.number} \u2192 Failed: ${Y.message}`)}}if(t.json){let A=d.result({jobs:x});r.output(A)}else{let A=x.filter(_=>_.status==="created").length,D=x.filter(_=>_.status==="failed").length;console.log(`
222
- Created ${A} jobs`),D>0&&console.log(`Failed ${D} jobs`),console.log(),r.hints([["Check all jobs","runhuman list"]])}}catch(e){let n=f(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}v();E();S();T();k();import{Command as Ei}from"commander";import eo from"cli-table3";function to(){let i=new Ei("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 d({json:t.json,color:n.color}),r=new y(n),[c,u]=await Promise.all([r.listPendingTransfers(),r.listOutgoingTransfers()]);if(t.json){let l=d.result({incoming:c.items,outgoing:u.items});o.output(l);return}if(o.heading(`Incoming Transfers (${c.total})`),c.items.length===0)console.log(`No pending incoming transfers.
223
- `);else{let l=new eo({head:o.tableHead(["Transfer ID","Project","From","To Org","Created"]),colWidths:[20,25,25,25,15]});c.items.forEach(s=>{l.push([s.id.substring(0,18),s.projectName,s.initiatedByEmail??"Unknown",s.toOrganizationName??"-",o.formatShortDate(s.createdAt)])}),console.log(l.toString()),console.log()}if(o.heading(`Outgoing Transfers (${u.total})`),u.items.length===0)console.log(`No pending outgoing transfers.
224
- `);else{let l=new eo({head:o.tableHead(["Transfer ID","Project","To Org","Status","Created"]),colWidths:[20,25,25,15,15]});u.items.forEach(s=>{l.push([s.id.substring(0,18),s.projectName,s.toOrganizationName??"-",s.status,o.formatShortDate(s.createdAt)])}),console.log(l.toString()),console.log()}o.hints([["Accept transfer","runhuman transfers accept <transferId>"],["Reject transfer","runhuman transfers reject <transferId>"],["Cancel transfer","runhuman transfers cancel <transferId>"]])}catch(e){let n=f(e);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=await I(c,"transfer",t);await c.acceptTransfer(u),e.json?r.output(d.result({success:!0,transferId:u})):(r.success("Transfer accepted!"),r.detail("Transfer ID",u),console.log())}catch(n){let o=f(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=await I(c,"transfer",t);await c.rejectTransfer(u),e.json?r.output(d.result({success:!0,transferId:u})):(r.success("Transfer rejected."),r.detail("Transfer ID",u),console.log())}catch(n){let o=f(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),u=await I(c,"transfer",t);await c.cancelTransfer(u),e.json?r.output(d.result({success:!0,transferId:u})):(r.success("Transfer cancelled."),r.detail("Transfer ID",u),console.log())}catch(n){let o=f(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}E();v();import{existsSync as vi}from"fs";import{join as Ri}from"path";import{execSync as xi}from"child_process";import B from"inquirer";import O from"chalk";import no from"ora";T();async function ro(){let i=new h,t=new d({color:!0});console.log(""),console.log(O.bold("Runhuman")+" - Human QA testing for your apps"),console.log("");let e=await oo(i);if(!e.isAuthenticated){let{shouldLogin:n}=await B.prompt([{type:"confirm",name:"shouldLogin",message:"You're not logged in. Would you like to sign in?",default:!0}]);if(!n){console.log(`
225
- Run `+O.cyan("runhuman login")+` when you're ready to sign in.
226
- `);return}await ki(i,t),Object.assign(e,await oo(i))}console.log(O.green("Logged in as "+e.userEmail)+`
227
- `),e.isRunhumanProject?await Pi(i,e):await Ui(i,e)}async function oo(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{xi("git rev-parse --is-inside-work-tree",{stdio:"ignore"}),t.isGitRepo=!0}catch{t.isGitRepo=!1}let n=Ri(process.cwd(),".runhumanrc");if(vi(n)){t.isRunhumanProject=!0;try{let o=await i.loadConfig();t.projectConfig={defaultUrl:o.defaultUrl,githubRepos:o.githubRepos}}catch{}}return t}async function ki(i,t){let e=await i.loadConfig(),n=e.apiUrl||"https://runhuman.com";console.log("");let o=no("Opening browser for authentication...").start();try{let r=await Te({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 Pi(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 B.prompt([{type:"list",name:"action",message:"What would you like to do?",choices:[{name:"Quick test a URL",value:"quick-test"},{name:"Run a template",value:"run-template"},{name:"View recent jobs",value:"list-jobs"},new B.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await io(i,t);break;case"run-template":console.log(`
228
- Run `+O.cyan("runhuman templates")+" to see available templates."),console.log("Then run "+O.cyan("runhuman create --template <name>")+` to use one.
225
+ ${S.body}`,metadata:{githubIssue:{repo:`${l}/${u}`,issueNumber:S.number,issueUrl:S.url}},template:t.template};try{let D=await a.createJob(U);E.push({issue:S.number,jobId:D.jobId,status:"created"}),console.log(` ok Issue #${S.number} \u2192 Job ${D.jobId}`)}catch(D){let V=g(D);E.push({issue:S.number,error:V.message,status:"failed"}),console.log(` FAIL Issue #${S.number} \u2192 Failed: ${V.message}`)}}if(t.json){let S=d.result({jobs:E});r.output(S)}else{let S=E.filter(D=>D.status==="created").length,U=E.filter(D=>D.status==="failed").length;console.log(`
226
+ Created ${S} jobs`),U>0&&console.log(`Failed ${U} jobs`),console.log(),r.hints([["Check all jobs","runhuman list"]])}}catch(e){let n=g(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i}R();x();T();v();N();import{Command as ts}from"commander";import bo from"cli-table3";function yo(){let i=new ts("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 d({json:t.json,color:n.color}),r=new y(n),[c,l]=await Promise.all([r.listPendingTransfers(),r.listOutgoingTransfers()]);if(t.json){let u=d.result({incoming:c.items,outgoing:l.items});o.output(u);return}if(o.heading(`Incoming Transfers (${c.total})`),c.items.length===0)console.log(`No pending incoming transfers.
227
+ `);else{let u=new bo({head:o.tableHead(["Transfer ID","Project","From","To Org","Created"]),colWidths:[20,25,25,25,15]});c.items.forEach(a=>{u.push([a.id.substring(0,18),a.projectName,a.initiatedByEmail??"Unknown",a.toOrganizationName??"-",o.formatShortDate(a.createdAt)])}),console.log(u.toString()),console.log()}if(o.heading(`Outgoing Transfers (${l.total})`),l.items.length===0)console.log(`No pending outgoing transfers.
228
+ `);else{let u=new bo({head:o.tableHead(["Transfer ID","Project","To Org","Status","Created"]),colWidths:[20,25,25,15,15]});l.items.forEach(a=>{u.push([a.id.substring(0,18),a.projectName,a.toOrganizationName??"-",a.status,o.formatShortDate(a.createdAt)])}),console.log(u.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 d({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 d({json:e.json,color:o.color}),c=new y(o),l=await b(c,"transfer",t);await c.acceptTransfer(l),e.json?r.output(d.result({success:!0,transferId:l})):(r.success("Transfer accepted!"),r.detail("Transfer ID",l),console.log())}catch(n){let o=g(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),l=await b(c,"transfer",t);await c.rejectTransfer(l),e.json?r.output(d.result({success:!0,transferId:l})):(r.success("Transfer rejected."),r.detail("Transfer ID",l),console.log())}catch(n){let o=g(n);new d({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 d({json:e.json,color:o.color}),c=new y(o),l=await b(c,"transfer",t);await c.cancelTransfer(l),e.json?r.output(d.result({success:!0,transferId:l})):(r.success("Transfer cancelled."),r.detail("Transfer ID",l),console.log())}catch(n){let o=g(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}R();x();T();v();N();import{Command as os}from"commander";import ns from"inquirer";import{readFileSync as wo}from"fs";import rs from"cli-table3";var Se=["credentials","environment","setup","procedures","known-issues","contacts","general"];function lt(i){if(!i)return;let t=i.split(",").map(n=>n.trim()).filter(Boolean),e=t.filter(n=>!Se.includes(n));if(e.length>0)throw new Error(`Unknown tag(s): ${e.join(", ")}. Allowed: ${Se.join(", ")}`);return t}function Io(i,t){if(i!==void 0&&t!==void 0)throw new Error("Provide either --body or --body-file, not both");if(i!==void 0)return i;if(t!==void 0)return t==="-"?wo(0,"utf-8"):wo(t,"utf-8")}async function Ce(i){let e=await new h().loadConfig({apiKey:i.apiKey,apiUrl:i.apiUrl}),n=new d({json:i.json,color:e.color});return{apiClient:new y(e),output:n,config:e}}function is(i,t){i.heading("Tester Note"),i.detail("ID",t.id),i.detail("Title",t.title),i.detail("Organization",`${t.organizationName} (${t.organizationId})`),t.projectId?i.detail("Project",`${t.projectName??"-"} (${t.projectId})`):i.detail("Project","Org-wide (no project)"),i.detail("Tags",t.tags.length?t.tags.join(", "):"-"),i.detail("Visibility",t.isInternal?"internal":"customer-visible"),i.detail("Created",`${i.formatTimestamp(t.createdAt)} by ${t.createdByEmail}`),i.detail("Edited",`${i.formatTimestamp(t.editedAt)} by ${t.editedByEmail}`),console.log(`
229
+ `+t.body+`
230
+ `)}function jo(){let i=new os("notes");return i.description("Manage tester knowledge-base notes"),i.command("list").alias("ls").description("List tester notes visible to you").option("-s, --search <text>","Full-text search across title and body").option("-t, --tags <list>",`Comma-separated tag filter (${Se.join(", ")})`).option("-o, --organization <id>","Filter by organization id (full or short prefix)").option("-p, --project <id>","Filter by project id (full or short prefix)").option("--limit <n>","Max rows to return",t=>parseInt(t,10)).option("--offset <n>","Skip this many rows",t=>parseInt(t,10)).option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let{apiClient:e,output:n}=await Ce(t),o=lt(t.tags),r=t.organization?await b(e,"organization",t.organization):void 0,c=t.project?await b(e,"project",t.project):void 0,l=await e.listTesterNotes({search:t.search,tags:o,organizationId:r,projectId:c,limit:t.limit,offset:t.offset});if(t.json){n.output(d.result(l));return}if(l.total===0){n.heading("Tester Notes (0)"),console.log(`No notes matched your filters.
231
+ `);return}n.heading(`Tester Notes (${l.items.length} of ${l.total})`);let u=new rs({head:n.tableHead(["ID","Title","Org","Project","Tags","Edited"]),colWidths:[14,28,20,18,22,14]});for(let a of l.items)u.push([a.id.slice(0,8),a.title.length>26?a.title.slice(0,23)+"...":a.title,a.organizationName,a.projectName??"-",a.tags.join(", ")||"-",n.formatShortDate(a.editedAt)]);console.log(u.toString()),console.log(),n.hints([["Show a note","runhuman notes show <noteId>"],["Create a note",'runhuman notes create "Title" --organization <orgId>']])}catch(e){let n=g(e);new d({json:t.json}).outputError(n.message,n.details),process.exit(n.exitCode)}}),i.command("show").aliases(["get","info"]).description("Show a single note").argument("<noteId>","Note id (full or short prefix)").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let{apiClient:n,output:o}=await Ce(e),r=await b(n,"note",t),c=await n.getTesterNote(r);e.json?o.output(d.result(c)):is(o,c)}catch(n){let o=g(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("create").alias("new").description("Create a new tester note").argument("<title>","Note title").option("-o, --organization <id>","Organization id the note belongs to (defaults to current org; derived from --project when set)").option("-p, --project <id>","Project id to scope the note to (omit for org-wide)").option("-b, --body <text>","Note body text").option("--body-file <path>",'Read body from file (use "-" for stdin)').option("-t, --tags <list>",`Comma-separated tags (${Se.join(", ")})`).option("--public","Make this note customer-visible (default: internal-only)").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let{apiClient:n,output:o,config:r}=await Ce(e),c=Io(e.body,e.bodyFile);if(c===void 0)throw new Error("Provide a body via --body or --body-file");let l=e.project?await b(n,"project",e.project):void 0,u;if(e.organization)u=await b(n,"organization",e.organization);else if(l)u=(await n.getProject(l)).organizationId;else{let m=C("organization",void 0,r,o);u=await b(n,"organization",m)}let a={title:t,body:c,organizationId:u,projectId:l??null,tags:lt(e.tags),isInternal:!e.public},p=await n.createTesterNote(a);e.json?o.output(d.result(p)):(o.success("Note created"),o.detail("ID",p.id),o.detail("Title",p.title))}catch(n){let o=g(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("edit").aliases(["update"]).description("Edit an existing note").argument("<noteId>","Note id (full or short prefix)").option("--title <text>","New title").option("-b, --body <text>","New body").option("--body-file <path>",'Read new body from file (use "-" for stdin)').option("-o, --organization <id>","Move the note to a different organization").option("-p, --project <id>",'Re-scope to a project (use --project "" to clear)').option("-t, --tags <list>",`Replace tags (${Se.join(", ")})`).option("--internal","Mark internal-only").option("--public","Mark customer-visible").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let{apiClient:n,output:o}=await Ce(e),r=await b(n,"note",t);if(e.internal&&e.public)throw new Error("--internal and --public are mutually exclusive");let c=Io(e.body,e.bodyFile),l={};if(e.title!==void 0&&(l.title=e.title),c!==void 0&&(l.body=c),e.organization!==void 0&&(l.organizationId=await b(n,"organization",e.organization)),e.project!==void 0&&(l.projectId=e.project===""?null:await b(n,"project",e.project)),e.tags!==void 0&&(l.tags=lt(e.tags)),e.internal&&(l.isInternal=!0),e.public&&(l.isInternal=!1),Object.keys(l).length===0)throw new Error("Nothing to update. Pass at least one of --title, --body, --body-file, --organization, --project, --tags, --internal, --public.");let u=await n.updateTesterNote(r,l);e.json?o.output(d.result(u)):(o.success("Note updated"),o.detail("ID",u.id),o.detail("Title",u.title))}catch(n){let o=g(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i.command("archive").aliases(["delete","rm"]).description("Archive a note (soft-delete; reversible by a staff member in the dashboard)").argument("<noteId>","Note id (full or short prefix)").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{apiClient:n,output:o}=await Ce(e),r=await b(n,"note",t);if(!e.json&&!e.force){let{confirmed:c}=await ns.prompt([{type:"confirm",name:"confirmed",message:`Archive note ${r}?`,default:!1}]);if(!c)return}await n.archiveTesterNote(r),e.json?o.output(d.result({success:!0})):o.success("Note archived")}catch(n){let o=g(n);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),i}x();R();import{existsSync as ss}from"fs";import{join as as}from"path";import{execSync as cs}from"child_process";import de from"inquirer";import z from"chalk";import So from"ora";v();Ue();async function To(){let i=new h,t=new d({color:!0});console.log(""),console.log(z.bold("Runhuman")+" - Human QA testing for your apps"),console.log("");let e=await Co(i);if(!e.isAuthenticated){let{shouldLogin:n}=await de.prompt([{type:"confirm",name:"shouldLogin",message:"You're not logged in. Would you like to sign in?",default:!0}]);if(!n){console.log(`
232
+ Run `+z.cyan("runhuman login")+` when you're ready to sign in.
233
+ `);return}await ls(i,t),Object.assign(e,await Co(i))}console.log(z.green("Logged in as "+e.userEmail)+`
234
+ `),e.isRunhumanProject?await us(i,e):await ds(i,e)}async function Co(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{cs("git rev-parse --is-inside-work-tree",{stdio:"ignore"}),t.isGitRepo=!0}catch{t.isGitRepo=!1}let n=as(process.cwd(),".runhumanrc");if(ss(n)){t.isRunhumanProject=!0;try{let o=await i.loadConfig();t.projectConfig={githubRepos:o.githubRepos}}catch{}}return t}async function ls(i,t){let e=await i.loadConfig(),n=e.apiUrl||"https://runhuman.com";console.log("");let o=So("Opening browser for authentication...").start();try{let r=await ze({apiUrl:n,autoOpenBrowser:e.autoOpenBrowser!==!1});o.stop(),i.saveCredentials({accessToken:r.token});let c=await i.loadConfig({apiKey:r.token}),u=await new y(c).getCurrentUser();i.saveUserInfo(u),t.success("Successfully logged in!"),console.log("")}catch(r){throw o.stop(),r}}async function us(i,t){console.log(z.dim("Runhuman project detected")),console.log("");let{action:e}=await de.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 de.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await Ao(i,t);break;case"run-template":console.log(`
235
+ Run `+z.cyan("runhuman templates")+" to see available templates."),console.log("Then run "+z.cyan("runhuman create --template <name>")+` to use one.
229
236
  `);break;case"list-jobs":console.log(`
230
- Run `+O.cyan("runhuman list")+` to see your recent jobs.
231
- `);break;case"exit":break}}async function Ui(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 B.prompt([{type:"list",name:"action",message:"What would you like to do?",choices:[{name:"Quick test a URL",value:"quick-test"},{name:"Set up this repo with Runhuman",value:"setup-repo",disabled:!t.isGitRepo},{name:"Connect a GitHub repo",value:"connect-github"},new B.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await io(i,t);break;case"setup-repo":await Oi(i);break;case"connect-github":await Ni(i);break;case"exit":break}}async function io(i,t){let e=t.projectConfig?.defaultUrl||"",n=await B.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=no("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(`
232
- 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(""),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 B.prompt([{type:"input",name:"defaultUrl",message:"Default URL to test (optional):"}]);await i.saveProjectConfig({defaultUrl:t.defaultUrl||void 0,defaultDuration:5,defaultDeviceClass:"desktop"}),console.log(""),console.log(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 Di}from"fs";import{join as Mi,dirname as _i}from"path";import{fileURLToPath as zi}from"url";var Li=zi(import.meta.url),$i=_i(Li),Fi=Mi($i,"../package.json"),Ji=JSON.parse(Di(Fi,"utf-8")),qi=Ji.version,P=new ao;P.name("runhuman").description("CLI for Runhuman - AI-orchestrated human QA testing").version(qi);P.addCommand($e());P.addCommand(Fe());P.addCommand(Ce());P.addCommand(Je());P.addCommand(qe());P.addCommand(Ke());P.addCommand(Ge());var $=new ao("job");$.alias("jobs");$.description("Manage QA test jobs");$.addCommand($e());$.addCommand(Fe());$.addCommand(Ce());$.addCommand(Je());$.addCommand(qe());$.addCommand(Ke());$.addCommand(Ge());P.addCommand($);P.addCommand(xt());P.addCommand(kt());P.addCommand(Pt());P.addCommand(Ut());P.addCommand(Ot());var so=Dt();so.alias("project");P.addCommand(so);var co=$t();co.aliases(["org","organizations","organization"]);P.addCommand(co);var lo=Jt();lo.alias("key");P.addCommand(lo);var uo=Kt();uo.alias("template");P.addCommand(uo);var po=Xt();po.alias("schedule");P.addCommand(po);P.addCommand(Zt());var mo=to();mo.alias("transfer");P.addCommand(mo);process.argv.slice(2).length?P.parse(process.argv):ro().catch(i=>{console.error(i.message),process.exit(1)});
237
+ Run `+z.cyan("runhuman list")+` to see your recent jobs.
238
+ `);break;case"exit":break}}async function ds(i,t){t.isGitRepo?console.log(z.dim("Git repository detected (not yet set up with Runhuman)")):console.log(z.dim("Not in a git repository")),console.log("");let{action:e}=await de.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 de.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await Ao(i,t);break;case"setup-repo":await ps(i);break;case"connect-github":await ms(i);break;case"exit":break}}async function Ao(i,t){let e=await de.prompt([{type:"input",name:"url",message:"URL to test:",validate:o=>{if(!o.trim())return"URL is required";try{return new URL(o),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"description",message:"What should we test? (describe in plain English):",validate:o=>o.trim()?!0:"Description is required"}]),n=So("Creating test job...").start();try{let o=i.loadCredentials(),r=await i.loadConfig({apiKey:o?.accessToken});if(!r.project){ce(n,"No project selected"),console.log(`
239
+ Run: runhuman projects switch <project-id>`);return}let l=await new y(r).createJob({projectId:r.project,url:e.url,description:e.description});ae(n,"Test job created!"),console.log(""),console.log(" Job ID: "+z.cyan(l.jobId)),console.log(""),console.log("A human tester will test your app shortly."),console.log("Run "+z.cyan(`runhuman wait ${l.jobId}`)+" to wait for results."),console.log("")}catch(o){throw ce(n,"Failed to create test job"),o}}async function ps(i){console.log(""),console.log("Setting up Runhuman in the current repository..."),console.log(""),await i.saveProjectConfig({defaultDuration:5,defaultDeviceClass:"desktop"}),console.log(""),console.log(z.green("Created .runhumanrc")),console.log(""),console.log("Next steps:"),console.log(" 1. Install the GitHub App to enable @runhuman comments"),console.log(" "+z.cyan("runhuman github connect")),console.log(""),console.log(" 2. Set a default URL on your project (so you can run jobs without --url):"),console.log(" "+z.cyan("runhuman projects update <projectId> --default-url https://your-app.com")),console.log(""),console.log(" 3. Create your first test:"),console.log(" "+z.cyan('runhuman create https://your-app.com -d "Test login flow"')),console.log("")}async function ms(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(" "+z.cyan(n)),console.log(""),console.log("After installation, you can comment "+z.bold("@runhuman")+" on any issue"),console.log("to trigger a QA test."),console.log("")}import{readFileSync as fs}from"fs";import{join as gs,dirname as hs}from"path";import{fileURLToPath as bs}from"url";var ys=bs(import.meta.url),ws=hs(ys),Is=gs(ws,"../package.json"),js=JSON.parse(fs(Is,"utf-8")),Cs=js.version,O=new Eo;O.name("runhuman").description("CLI for Runhuman - AI-orchestrated human QA testing").version(Cs);O.addCommand(et());O.addCommand(tt());O.addCommand(_e());O.addCommand(ot());O.addCommand(nt());O.addCommand(it());var G=new Eo("job");G.alias("jobs");G.description("Manage QA test jobs");G.addCommand(et());G.addCommand(tt());G.addCommand(_e());G.addCommand(ot());G.addCommand(nt());G.addCommand(it());O.addCommand(G);O.addCommand(Ht());O.addCommand(Gt());O.addCommand(Vt());O.addCommand(Wt());O.addCommand(Bt());var vo=Xt();vo.alias("project");O.addCommand(vo);var ko=oo();ko.aliases(["org","organizations","organization"]);O.addCommand(ko);var xo=ro();xo.alias("key");O.addCommand(xo);var Ro=so();Ro.alias("template");O.addCommand(Ro);var No=fo();No.alias("schedule");O.addCommand(No);O.addCommand(ho());var Oo=yo();Oo.alias("transfer");O.addCommand(Oo);var Po=jo();Po.alias("note");O.addCommand(Po);process.argv.slice(2).length?O.parse(process.argv):To().catch(i=>{console.error(i.message),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runhuman",
3
- "version": "1.10.1",
3
+ "version": "1.12.0",
4
4
  "description": "CLI for Runhuman - AI-orchestrated human QA testing",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",