runhuman 1.12.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -15
- package/README.md +947 -731
- package/dist/index.js +108 -107
- package/package.json +80 -80
package/dist/index.js
CHANGED
|
@@ -1,112 +1,111 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
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",`
|
|
2
|
+
var Gn=Object.defineProperty;var B=(n,t)=>()=>(n&&(t=n(n=0)),t);var Wn=(n,t)=>{for(var e in t)Gn(n,e,{get:t[e],enumerable:!0})};function Yn(n){return n instanceof N}function f(n){return Yn(n)?{message:n.message,exitCode:n.exitCode,details:n.details}:n instanceof Error?{message:n.message,exitCode:1}:{message:String(n),exitCode:1}}var N,Ne,Me,j,Y,Le,pe,$e,A=B(()=>{"use strict";N=class extends Error{constructor(e,r=1,o){super(e);this.exitCode=r;this.details=o;this.name="CliError"}},Ne=class extends N{constructor(t="Authentication failed",e){super(t,2,e),this.name="AuthenticationError"}},Me=class extends N{constructor(t="Resource not found",e){super(t,3,e),this.name="NotFoundError"}},j=class extends N{constructor(t="Validation failed",e){super(t,4,e),this.name="ValidationError"}},Y=class extends N{constructor(t="Operation timed out",e){super(t,5,e),this.name="TimeoutError"}},Le=class extends N{constructor(t="Insufficient balance",e){super(t,6,e),this.name="InsufficientBalanceError"}},pe=class extends N{constructor(t="Resource conflict",e){super(t,7,e),this.name="ConflictError"}},$e=class extends N{jobId;status;dashboardUrl;constructor(t,e,r,o){let i=`Job ${t} ended in terminal state "${e}"`,s=o?`${i}: ${o}`:i;super(s,8,{jobId:t,status:e,dashboardUrl:r,detail:o}),this.name="JobTerminalError",this.jobId=t,this.status=e,this.dashboardUrl=r}}});function a(n){return{pattern:n,build:e=>{if(!e)return n;let r=n;for(let[o,i]of Object.entries(e))i!==void 0&&(r=r.replace(`:${o}?`,i),r=r.replace(`:${o}`,i));return r=r.replace(/\/:[^/]+\?/g,""),r=r.replace(/\/+/g,"/"),r.length>1&&r.endsWith("/")&&(r=r.slice(0,-1)),r}}}var ze=B(()=>{"use strict"});var Fe,Nt=B(()=>{"use strict";ze();Fe={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"),adminJobDetail:a("/admin/jobs/:jobId"),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"),adminOttoChannels:a("/admin/otto-channels"),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"),organizationJob:a("/dashboard/organizations/:organizationId/jobs/:jobId"),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"),organizationOttoGitHub:a("/dashboard/organizations/:organizationId/otto-github"),organizationJira:a("/dashboard/organizations/:organizationId/jira"),organizationPlayground:a("/dashboard/organizations/:organizationId/playground"),organizationTemplates:a("/dashboard/organizations/:organizationId/templates"),organizationSchedules:a("/dashboard/organizations/:organizationId/schedules"),organizationNotes:a("/dashboard/organizations/:organizationId/notes"),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"),testerIntroApplying:a("/tester-intro/applying"),testerMyJobs:a("/tester/my-jobs"),testerMyJobDetail:a("/tester/my-jobs/:jobId"),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"),issueJira:a("/dashboard/:projectId/issues/jira/:issueKey"),issueSessions:a("/dashboard/:projectId/issue-sessions"),issueSession:a("/dashboard/:projectId/issue-sessions/:sessionId"),projectSettings:a("/dashboard/:projectId/settings"),projectIntegrations:a("/dashboard/:projectId/integrations"),schedules:a("/dashboard/:projectId/schedules"),notes:a("/dashboard/:projectId/notes"),flowCharts:a("/dashboard/:projectId/flowcharts"),flowChart:a("/dashboard/:projectId/flowcharts/:flowchartId"),flowChartView:a("/view/flowchart/:projectId/:flowchartId"),invite:a("/invite/:token"),inviteAccepted:a("/invite-accepted"),try:a("/try"),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"),adminNotifications:a("/admin/notifications"),learn:a("/learn"),learnLesson:a("/learn/:lessonSlug")}});var S,Mt=B(()=>{"use strict";ze();S={jobs:a("/jobs"),job:a("/jobs/:jobId"),jobCancel:a("/jobs/:jobId/cancel"),jobCreateIssue:a("/jobs/:jobId/create-issue"),jobUndoIssueComment:a("/jobs/:jobId/extracted-issues/:extractedIssueIndex/undo-comment"),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"),testerJobEnd:a("/tester/jobs/:jobId/end"),testerJobRequestExtension:a("/tester/jobs/:jobId/request-extension"),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"),ottoGithubAppAuthorize:a("/otto/github/install/authorize"),ottoGithubAppCallback:a("/otto/github/install/callback"),ottoGithubAppWebhooks:a("/webhooks/otto-github-app"),jiraOAuthAuthorize:a("/jira/oauth/authorize"),jiraOAuthCallback:a("/jira/oauth/callback"),jiraInstallations:a("/organizations/:organizationId/jira/installations"),jiraInstallationDelete:a("/organizations/:organizationId/jira/installations/:installationId"),jiraProjects:a("/organizations/:organizationId/jira/projects"),jiraIssueCreateManual:a("/jobs/:jobId/issues/:extractedIssueId/jira"),jiraIssuesForJob:a("/jobs/:jobId/jira-issues"),jiraIssues:a("/jira/issues"),jiraIssue:a("/jira/issues/:issueKey"),jiraIssueComments:a("/jira/issues/:issueKey/comments"),jiraIssueTest:a("/jira/issues/test"),jiraIssuesBulkTest:a("/jira/issues/bulk-test"),jiraTestSessions:a("/jira/issues/test-sessions"),jiraTestSession:a("/jira/issues/test-sessions/:sessionId"),jiraTestSessionSeen:a("/jira/issues/test-sessions/:sessionId/seen"),jiraTestSessionsCounts:a("/jira/issues/test-sessions/counts"),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"),billingTopupCheckout:a("/billing/topup/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"),livekitWebhooks:a("/livekit/webhooks"),health:a("/health"),status:a("/status"),configFeatureFlags:a("/config/feature-flags"),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"),firstUseGate:a("/onboarding/first-use-gate"),search:a("/search"),testerApply:a("/tester/apply"),testerProfile:a("/tester/profile"),testerPublicProfile:a("/testers/:testerId/profile"),testerRecentJobs:a("/testers/:testerId/recent-jobs"),testerDownloadApp:a("/tester/download-app"),testerDownloadExtension:a("/tester/download-extension"),testerDownloadMobileApp:a("/tester/download-mobile-app"),testerMobileAppVersions:a("/tester/mobile-app-versions"),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"),testerActivity:a("/tester/activity"),testerSchedule:a("/tester/schedule"),testerAllSchedules:a("/tester/schedules/all"),testerPublicSchedule:a("/testers/:testerId/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"),adminTesterScheduleCsv:a("/admin/testers/schedule-csv"),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"),organizationInvitations:a("/organizations/:organizationId/invitations"),organizationInvitation:a("/organizations/:organizationId/invitations/:invitationId"),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"),organizationLowBalanceSettings:a("/organizations/:organizationId/low-balance-settings"),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"),organizationOttoGithubInstallations:a("/organizations/:organizationId/otto/github/installations"),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"),organizationRepoIndexes:a("/organizations/:organizationId/repo-indexes"),organizationRepoIndexCancel:a("/organizations/:organizationId/repo-indexes/cancel"),organizationRepoIndexSync:a("/organizations/:organizationId/repo-indexes/sync"),organizationRepoIndexSyncPreview:a("/organizations/:organizationId/repo-indexes/sync-preview"),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"),adminJobRunProcessingPipeline:a("/admin/jobs/:jobId/run-processing-pipeline"),adminJobTerminate:a("/admin/jobs/:jobId/terminate"),adminJobRelease:a("/admin/jobs/:jobId/release"),adminJobReassign:a("/admin/jobs/:jobId/reassign"),adminJobUnarchive:a("/admin/jobs/:jobId/unarchive"),adminOttoChannelSettings:a("/admin/otto-channel-settings"),adminOttoChannelSettingsLookup:a("/admin/otto-channel-settings/lookup"),adminOttoChannelSetting:a("/admin/otto-channel-settings/:instanceKey/:channelId"),adminOpsDigestSettings:a("/admin/ops-digest/settings"),adminOpsDigestTrigger:a("/admin/ops-digest/trigger"),adminOpsDigestPreview:a("/admin/ops-digest/preview"),notificationPreferences:a("/notifications/preferences"),notificationUnsubscribe:a("/notifications/unsubscribe"),notificationUnsubscribePreview:a("/notifications/unsubscribe/preview"),notificationUnsubscribeProject:a("/notifications/unsubscribe/project"),notificationUnsubscribeNuclear:a("/notifications/unsubscribe/nuclear"),notificationNuclearReset:a("/notifications/nuclear-reset"),projectEmailPreference:a("/projects/:projectId/email-preference"),adminNotificationSampleVariables:a("/admin/notifications/sample-variables"),adminNotificationPreviewSend:a("/admin/notifications/preview-send"),adminAnnouncementPreviewRender:a("/admin/announcements/preview-render"),adminAnnouncementPreviewSend:a("/admin/announcements/preview-send"),adminAnnouncementBroadcast:a("/admin/announcements/broadcast"),adminAnnouncementAudienceCount:a("/admin/announcements/audience-count")}});var ot=B(()=>{"use strict";ze();Nt();Mt()});import Zn from"axios";function tr(n){if(!n)return;let t={};return n.limit!==void 0&&(t.limit=n.limit),n.offset!==void 0&&(t.offset=n.offset),n.search!==void 0&&(t.search=n.search),n.deviceClass&&n.deviceClass.length>0&&(t.deviceClass=n.deviceClass),n.source&&n.source.length>0&&(t.source=n.source),n.hasSchema!==void 0&&(t.hasSchema=String(n.hasSchema)),n.includeDefaults!==void 0&&(t.includeDefaults=String(n.includeDefaults)),n.sort!==void 0&&(t.sort=n.sort),n.direction!==void 0&&(t.direction=n.direction),t}var er,h,x=B(()=>{"use strict";A();ot();er={default:3e4,jobCreate:12e4};h=class{client;config;timeouts;constructor(t,e=er){if(this.config=t,this.timeouts=e,!t.apiUrl)throw new Error("apiUrl is required in CLI config");this.client=Zn.create({baseURL:t.apiUrl+"/api",timeout:e.default,headers:{"Content-Type":"application/json"},paramsSerializer:{indexes:null}}),this.client.interceptors.request.use(r=>(this.config.apiKey&&(r.headers.Authorization=`Bearer ${this.config.apiKey}`),r.method==="delete"&&!r.data&&delete r.headers["Content-Type"],r)),this.client.interceptors.response.use(r=>r,r=>{throw this.handleError(r)})}handleError(t){if(t.response){let e=t.response.status,r=t.response.data,o=r?.error||r?.message||t.message;switch(e){case 401:case 403:return new Ne(o,r);case 402:return new Le(o,r);case 404:return new Me(o,r);case 409:return new pe(o,r);case 400:case 422:return new j(o,r);default:return new N(o,1,r)}}return t.code==="ECONNABORTED"?this.buildTimeoutError(t):t.code==="ENOTFOUND"||t.code==="ECONNREFUSED"?new N("Cannot connect to Runhuman API",1):new N(t.message,1)}buildTimeoutError(t){let e=t.config?.meta?.operation,r=t.config?.timeout??this.timeouts.default,o=Math.round(r/1e3);return e==="createJob"?new Y(`Job creation timed out after ${o}s. The job may have been created on the server \u2014 run \`runhuman job list\` to check before retrying, or you may produce a duplicate.`):new Y(`Request timed out after ${o}s`)}get apiUrl(){return this.config.apiUrl}dashboardUrl(t){return`${this.apiUrl}${Fe.jobSimple.build({jobId:t})}`}async createJob(t){return(await this.client.post(S.jobs.build(),t,{timeout:this.timeouts.jobCreate,meta:{operation:"createJob"}})).data}async getJob(t){return(await this.client.get(S.job.build({jobId:t}))).data}async listJobs(t){return(await this.client.get(S.jobs.build(),{params:t})).data}async getJobArtifact(t,e){return(await this.client.get(S.jobArtifact.build({jobId:t,artifactType:e}))).data}async getJobRecording(t){return(await this.client.get(S.livekitRecording.build({jobId:t}))).data}async shareJob(t){return(await this.client.post(S.jobShare.build({jobId:t}),{})).data}async triggerEnhancedVideoRender(t){return(await this.client.post(S.enhancedVideoRender.build({jobId:t}),{})).data}async getEnhancedVideoStatus(t){return(await this.client.get(S.enhancedVideoStatus.build({jobId:t}))).data}async unshareJob(t){await this.client.delete(S.jobShare.build({jobId:t}))}async createIssueFromFinding(t,e){return(await this.client.post(S.jobCreateIssue.build({jobId:t}),e)).data}async getBillingBalance(t){return(await this.client.get(S.billingBalance.build(),{params:t})).data}async getBillingSubscription(t){return(await this.client.get(S.billingSubscription.build(),{params:t})).data}async getBillingPortal(t){return(await this.client.get(S.billingPortal.build(),{params:t})).data}async listProjects(t){return(await this.client.get(S.projects.build(),{params:t})).data}async getProject(t){return(await this.client.get(S.project.build({projectId:t}))).data}async createProject(t){return(await this.client.post(S.projects.build(),t)).data}async updateProject(t,e){return(await this.client.patch(S.project.build({projectId:t}),e)).data}async deleteProject(t){await this.client.delete(S.project.build({projectId:t}))}async listApiKeys(t,e){return(await this.client.get(S.organizationApiKeys.build({organizationId:t}),{params:e})).data}async createApiKey(t,e){return(await this.client.post(S.organizationApiKeys.build({organizationId:t}),{name:e})).data}async getApiKey(t){return(await this.client.get(S.key.build({keyId:t}))).data}async deleteApiKey(t){await this.client.delete(S.key.build({keyId:t}))}async listOrganizations(t){return(await this.client.get(S.organizations.build(),{params:t})).data}async getOrganization(t){return(await this.client.get(S.organization.build({organizationId:t}))).data}async getOrganizationBilling(t){return(await this.client.get(S.organizationBilling.build({organizationId:t}))).data}async listOrganizationProjects(t,e){return(await this.client.get(S.organizationProjects.build({organizationId:t}),{params:e})).data}async listOrganizationMembers(t){return(await this.client.get(S.organizationMembers.build({organizationId:t}))).data}async inviteOrganizationMember(t,e){return(await this.client.post(S.organizationInvite.build({organizationId:t}),e)).data}async removeOrganizationMember(t,e){await this.client.delete(S.organizationMember.build({organizationId:t,userId:e}))}async listOrganizationInvitations(t){return(await this.client.get(S.organizationInvitations.build({organizationId:t}))).data}async revokeOrganizationInvitation(t,e){await this.client.delete(S.organizationInvitation.build({organizationId:t,invitationId:e}))}async getOrganizationUsage(t,e){return(await this.client.get(S.organizationUsage.build({organizationId:t}),{params:e})).data}async refreshGithubInstallation(t,e){return(await this.client.post(S.organizationGitHubInstallationRefresh.build({organizationId:t,installationId:e}),{})).data}async listTemplates(t,e){return(await this.client.get(S.projectTemplates.build({projectId:t}),{params:tr(e)})).data}async getTemplate(t,e){return(await this.client.get(S.projectTemplate.build({projectId:t,templateId:e}))).data}async createTemplate(t,e){return(await this.client.post(S.projectTemplates.build({projectId:t}),e)).data}async updateTemplate(t,e,r){return(await this.client.patch(S.projectTemplate.build({projectId:t,templateId:e}),r)).data}async deleteTemplate(t,e){await this.client.delete(S.projectTemplate.build({projectId:t,templateId:e}))}async listSchedules(t,e){return(await this.client.get(S.projectSchedules.build({projectId:t}),{params:e})).data}async createSchedule(t,e){return(await this.client.post(S.projectSchedules.build({projectId:t}),e)).data}async getSchedule(t,e){return(await this.client.get(S.projectSchedule.build({projectId:t,scheduleId:e}))).data}async updateSchedule(t,e,r){return(await this.client.patch(S.projectSchedule.build({projectId:t,scheduleId:e}),r)).data}async deleteSchedule(t,e){await this.client.delete(S.projectSchedule.build({projectId:t,scheduleId:e}))}async listScheduleExecutions(t,e,r){return(await this.client.get(S.projectScheduleExecutions.build({projectId:t,scheduleId:e}),{params:r})).data}async getCurrentUser(){return(await this.client.get(S.authMe.build())).data}async listGithubRepos(t,e){return(await this.client.get(S.organizationGitHubRepos.build({organizationId:t}),{params:e})).data}async listGithubIssues(t,e,r){return(await this.client.get(S.githubIssuesByRepo.build({owner:t,repo:e}),{params:r})).data}async getGithubIssue(t,e,r){return(await this.client.get(S.githubIssueByRepo.build({owner:t,repo:e,issueNumber:String(r)}))).data}async initiateTransfer(t,e){let r=await this.client.post(S.projectTransfer.build({projectId:t}),{toOrganizationId:e});return r.status===200?{type:"immediate",project:r.data}:{type:"pending",transfer:r.data}}async listPendingTransfers(t){return(await this.client.get(S.transfersPending.build(),{params:t})).data}async listOutgoingTransfers(t){return(await this.client.get(S.transfersOutgoing.build(),{params:t})).data}async acceptTransfer(t){return(await this.client.post(S.transferAccept.build({transferId:t}),{})).data}async rejectTransfer(t){return(await this.client.post(S.transferReject.build({transferId:t}),{})).data}async cancelTransfer(t){return(await this.client.post(S.transferCancel.build({transferId:t}),{})).data}async listTesterNotes(t){return(await this.client.get(S.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(S.testerNote.build({noteId:t}))).data}async createTesterNote(t){return(await this.client.post(S.testerNotes.build(),t)).data}async updateTesterNote(t,e){return(await this.client.patch(S.testerNote.build({noteId:t}),e)).data}async archiveTesterNote(t){return(await this.client.post(S.testerNoteArchive.build({noteId:t}),{})).data}}});import{cosmiconfig as or}from"cosmiconfig";import{homedir as nr}from"os";import{join as X}from"path";import{readFileSync as me,writeFileSync as Q,existsSync as V,mkdirSync as nt,chmodSync as rr}from"fs";var ir,W,se,fe,g,R=B(()=>{"use strict";ir="runhuman",W=X(nr(),".config","runhuman"),se=X(W,"config.json"),fe=X(W,"credentials.json"),g=class{constructor(t=process.cwd()){this.cwd=t}projectConfig=null;projectConfigFilepath=null;globalConfig=null;envConfig=null;getProjectConfigFilepath(){return this.projectConfigFilepath}async loadConfig(t={}){this.envConfig=this.loadEnvConfig(),this.projectConfig=await this.loadProjectConfig(),this.globalConfig=this.loadGlobalConfig();let e=this.loadCredentials(),r=e?.accessToken?{apiKey:e.accessToken}:{},o=Object.fromEntries(Object.entries(t).filter(([,s])=>s!==void 0));return{...this.getDefaults(),...this.globalConfig,...this.projectConfig,...r,...this.envConfig,...o}}getDefaults(){return{apiUrl:"https://runhuman.com",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_NO_COLOR==="1"&&(t.color=!1),t}async loadProjectConfig(){try{let e=await or(ir).search(this.cwd);return this.projectConfigFilepath=e?.filepath??null,e?.config||null}catch{return this.projectConfigFilepath=null,null}}loadGlobalConfig(){try{if(!V(se))return null;let t=me(se,"utf-8");return JSON.parse(t)}catch{return null}}async get(t){return(await this.loadConfig())[t]}async set(t,e,r=!1){r?await this.setGlobalConfig(t,e):await this.setProjectConfig(t,e)}async setGlobalConfig(t,e){V(W)||nt(W,{recursive:!0});let r={};if(V(se)){let o=me(se,"utf-8");r=JSON.parse(o)}r[t]=e,Q(se,JSON.stringify(r,null,2))}async setProjectConfig(t,e){let r=X(this.cwd,".runhumanrc"),o={};if(V(r)){let i=me(r,"utf-8");o=JSON.parse(i)}o[t]=e,Q(r,JSON.stringify(o,null,2))}async saveProjectConfig(t){let e=X(this.cwd,".runhumanrc"),r={};if(V(e)){let i=me(e,"utf-8");r=JSON.parse(i)}let o={...r,...t};Q(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")&&V(se)&&Q(se,"{}"),t==="project"||t==="all"){let e=X(this.cwd,".runhumanrc");V(e)&&Q(e,"{}")}}saveCredentials(t){V(W)||nt(W,{recursive:!0}),Q(fe,JSON.stringify(t,null,2));try{process.platform!=="win32"&&rr(fe,384)}catch{}}loadCredentials(){try{if(!V(fe))return null;let t=me(fe,"utf-8");return JSON.parse(t)}catch{return null}}clearCredentials(){V(fe)&&Q(fe,"{}")}saveUserInfo(t){let e=X(W,"user.json");V(W)||nt(W,{recursive:!0}),Q(e,JSON.stringify(t,null,2))}loadUserInfo(){try{let t=X(W,"user.json");if(!V(t))return null;let e=me(t,"utf-8");return JSON.parse(e)}catch{return null}}clearUserInfo(){let t=X(W,"user.json");V(t)&&Q(t,"{}")}}});import le from"chalk";import ar from"cli-table3";var Je,rt,d,v=B(()=>{"use strict";Je=24,rt=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)}outputPretty(t){typeof t=="string"?console.log(t):(Array.isArray(t),console.log(JSON.stringify(t,null,2)))}outputError(t,e){if(this.options.json){let r={success:!1,error:{message:t,details:e},timestamp:new Date().toISOString()};console.error(JSON.stringify(r,null,2))}else console.error(this.color("red",`
|
|
3
3
|
Error: ${t}
|
|
4
4
|
`)),e?.code==="INSUFFICIENT_TOKENS"&&e?.link?(console.error(this.color("yellow","Add funds to continue:")),console.error(this.color("cyan",e.link)),console.error("")):e&&console.error(this.color("gray",JSON.stringify(e,null,2)))}success(t){!this.options.json&&!this.options.quiet&&console.log(this.color("green",`
|
|
5
5
|
${t}
|
|
6
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
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(
|
|
9
|
-
`+"=".repeat(
|
|
10
|
-
`))}detail(t,e){this.options.json||this.options.quiet||console.log(` ${t}:`.padEnd(
|
|
8
|
+
`)}separator(){this.options.json||this.options.quiet||console.log("=".repeat(rt))}sectionHeader(t){this.options.json||this.options.quiet||(console.log(`
|
|
9
|
+
`+"=".repeat(rt)),console.log(t),console.log("=".repeat(rt)+`
|
|
10
|
+
`))}detail(t,e){this.options.json||this.options.quiet||console.log(` ${t}:`.padEnd(Je)+String(e))}heading(t){this.options.json||this.options.quiet||console.log(`
|
|
11
11
|
${t}
|
|
12
|
-
`)}resultSummary(t){if(!(this.options.json||this.options.quiet))for(let[e,
|
|
12
|
+
`)}resultSummary(t){if(!(this.options.json||this.options.quiet))for(let[e,r]of Object.entries(t))if(typeof r=="object"&&r!==null){let i=JSON.stringify(r,null,2).split(`
|
|
13
13
|
`).join(`
|
|
14
|
-
`+" ".repeat(
|
|
15
|
-
`);
|
|
16
|
-
|
|
17
|
-
${r}
|
|
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+=`
|
|
14
|
+
`+" ".repeat(Je));console.log(` ${e}:`.padEnd(Je)+i)}else console.log(` ${e}:`.padEnd(Je)+String(r))}hints(t){if(this.options.json||this.options.quiet)return;let r=Math.max(...t.map(([o])=>o.length))+4;for(let[o,i]of t)console.log(` ${o}:`.padEnd(r)+` ${i}`);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){let e=new ar({head:this.tableHead(["Job ID","Status","URL","Created","Duration","Cost"]),colWidths:[15,12,30,14,10,10]});return t.forEach(r=>{e.push([r.id,this.formatStatus(r.status),r.url?this.truncate(r.url,28):"-",r.createdAt?this.formatDate(r.createdAt):"-",r.testDurationSeconds?this.formatDuration(r.testDurationSeconds):"-",r.costUsd?`$${r.costUsd.toFixed(3)}`:"-"])}),e.toString()}formatStatus(t){let e={pending:"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),r=t%60;if(e<60)return`${e}m ${r}s`;let o=Math.floor(e/60),i=e%60;return`${o}h ${i}m ${r}s`}formatDate(t){let e=new Date(t),o=new Date().getTime()-e.getTime(),i=Math.floor(o/1e3),s=Math.floor(i/60),c=Math.floor(s/60),u=Math.floor(c/24);return i<60?`${i}s ago`:s<60?`${s}m ago`:c<24?`${c}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:le.red,green:le.green,blue:le.blue,yellow:le.yellow,cyan:le.cyan,gray:le.gray,white:le.white}[t];return o?o(e):e}static result(t,e={}){let r={success:!0,data:t,timestamp:new Date().toISOString()};return e.pagination&&(r.pagination=e.pagination),e.warnings&&e.warnings.length>0&&(r.warnings=[...e.warnings]),r}static error(t,e,r){let o={success:!1,error:{message:t},timestamp:new Date().toISOString()};return e!==void 0&&(o.error.code=e),r!==void 0&&(o.error.details=r),o}}});function de(n){return n.replace(/-/g,"").toLowerCase()}function wt(n){let t=n.replace(/-/g,"");return ma.test(t)}async function y(n,t,e,r){if(r?.requireFullId&&!wt(e)){let u=ro[t],l=await u(n),p=de(e),m=l.filter(b=>de(b.id).startsWith(p));throw new yt(e,t,r.requireFullId.reason,m)}if(It.test(e))return e;let o=ro[t],i=await o(n),s=de(e),c=i.filter(u=>de(u.id).startsWith(s));if(c.length===0)throw new ve(e,t);if(c.length===1)return c[0].id;throw new Te(e,t,c)}var It,ma,Te,ve,yt,ro,U=B(()=>{"use strict";A();It=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,ma=/^[0-9a-f]{32}$/i,Te=class extends N{constructor(e,r,o){let i=o.map(s=>` ${s.id} ${s.label}`).join(`
|
|
15
|
+
`);super(`"${e}" matches multiple ${r}s:
|
|
16
|
+
${i}
|
|
17
|
+
Please provide more characters.`,1);this.prefix=e;this.resourceType=r;this.matches=o;this.name="AmbiguousIdError"}},ve=class extends N{constructor(e,r){super(`No ${r} found matching "${e}".`,3);this.prefix=e;this.resourceType=r;this.name="NoMatchError"}},yt=class extends N{constructor(e,r,o,i){let s=`Full ${r} ID required for ${o} \u2014 short prefixes are not allowed for this operation.`;if(s+=`
|
|
19
18
|
|
|
20
|
-
Provided: "${e}"`,
|
|
19
|
+
Provided: "${e}"`,i.length===1)s+=`
|
|
21
20
|
|
|
22
|
-
This prefix matches: ${
|
|
23
|
-
If this is correct, re-run the command with the full ID.`;else if(
|
|
24
|
-
`);
|
|
21
|
+
This prefix matches: ${i[0].id} ${i[0].label}`,s+=`
|
|
22
|
+
If this is correct, re-run the command with the full ID.`;else if(i.length>1){let c=i.map(u=>` ${u.id} ${u.label}`).join(`
|
|
23
|
+
`);s+=`
|
|
25
24
|
|
|
26
|
-
This prefix matches multiple ${
|
|
27
|
-
${
|
|
28
|
-
Use the full ID of the intended ${
|
|
25
|
+
This prefix matches multiple ${r}s:
|
|
26
|
+
${c}`,s+=`
|
|
27
|
+
Use the full ID of the intended ${r}.`}else s+=`
|
|
29
28
|
|
|
30
|
-
No ${
|
|
31
|
-
Results Summary:`),e.resultSummary(
|
|
32
|
-
`),o=0;for(let
|
|
29
|
+
No ${r} found matching this prefix.`;super(s,1);this.prefix=e;this.resourceType=r;this.reason=o;this.matches=i;this.name="FullIdRequiredError"}};ro={async project(n){let{items:t}=await n.listProjects({limit:200});return t.map(e=>({id:e.id,label:e.name}))},async organization(n){let{items:t}=await n.listOrganizations({limit:200});return t.map(e=>({id:e.id,label:e.name}))},async key(n){let{items:t}=await n.listOrganizations({limit:200});return(await Promise.all(t.map(r=>n.listApiKeys(r.id)))).flatMap(({items:r})=>r.map(o=>({id:o.id,label:o.name})))},async template(n){let{items:t}=await n.listProjects({limit:200});return(await Promise.all(t.map(r=>n.listTemplates(r.id)))).flatMap(({items:r})=>r.map(o=>({id:o.id,label:o.name})))},async schedule(n){let{items:t}=await n.listProjects({limit:200});return(await Promise.all(t.map(r=>n.listSchedules(r.id,{limit:200})))).flatMap(({items:r})=>r.map(o=>({id:o.id,label:o.name})))},async note(n){let{items:t}=await n.listTesterNotes({limit:200});return t.map(e=>({id:e.id,label:e.title}))},async transfer(n){let[t,e]=await Promise.all([n.listPendingTransfers(),n.listOutgoingTransfers()]),r=[...t.items,...e.items],o=new Set,i=[];for(let s of r)o.has(s.id)||(o.add(s.id),i.push({id:s.id,label:s.projectName}));return i}}});import io from"chalk";function ne(n,t){n.stopAndPersist({symbol:fa,text:t})}function Z(n,t){n.stopAndPersist({symbol:ga,text:t})}var fa,ga,Re=B(()=>{"use strict";fa=`${io.green("\u2714")} `,ga=`${io.red("\u2716")} `});function ba(n,t){let e=o=>o.replace(/^--(no-)?/,""),r=e(t);return n.options.some(o=>o.long?e(o.long)===r:!1)}function J(n){for(let t of ha){if(ba(n,t.long))continue;let e=t.short?`${t.short}, ${t.long}`:t.long;n.option(e,t.description)}return n}function Ct(n){if(n.commands.length===0){J(n);return}for(let t of n.commands)Ct(t)}function ya(n,t){return{json:n.json,quiet:n.quiet,color:n.color===!1?!1:t?.color}}function I(n,t){return new d(ya(n,t))}var ha,O=B(()=>{"use strict";v();ha=[{short:"-j",long:"--json",description:"Output as JSON"},{short:"-q",long:"--quiet",description:"Minimize output (suppress success and info messages)"},{short:"-y",long:"--yes",description:"Skip confirmation prompts"},{long:"--no-color",description:"Disable colored output"}]});function St(n){return{id:n.id,label:`${n.status.padEnd(10)} ${n.description??""}`.trim()}}function co(n){return{id:n.id,label:n.name}}async function uo(n,t,e,r){let o=de(n);for(let i of e){let c=(await i.fetch()).filter(u=>de(u.id).startsWith(o));if(c.length===1)return r.info(`Resolved ${t} in ${i.label}`),c[0].id;if(c.length>1)throw new Te(n,t,c)}throw new ve(n,t)}function po(n){return It.test(n)||wt(n)}async function re(n,t,e,r,o){if(po(t))return t;let i=Ia(n,e,r);return uo(t,"job",i,o)}function Ia(n,t,e){if(t.projectId)return[{label:`project ${t.projectId}`,fetch:()=>ao(n,t.projectId)}];if(t.organizationId)return[{label:`organization ${t.organizationId}`,fetch:()=>so(n,t.organizationId)}];let r=[];return e.project&&r.push({label:"default project",fetch:()=>ao(n,e.project)}),e.organization&&r.push({label:"default organization",fetch:()=>so(n,e.organization)}),r.push({label:"all organizations",fetch:()=>wa(n)}),r}async function ao(n,t){let{items:e}=await n.listJobs({projectId:t,limit:200});return e.map(St)}async function so(n,t){let{items:e}=await n.listJobs({organizationId:t,limit:200});return e.map(St)}async function wa(n){let{items:t}=await n.listOrganizations({limit:200}),e=[];for(let r of t){let{items:o}=await n.listJobs({organizationId:r.id,limit:200});e.push(...o.map(St))}return e}async function xe(n,t,e,r,o){if(po(t))return t;let i=Ca(n,e,r);return uo(t,"project",i,o)}function Ca(n,t,e){if(t.organizationId)return[{label:`organization ${t.organizationId}`,fetch:()=>lo(n,t.organizationId)}];let r=[];return e.organization&&r.push({label:"default organization",fetch:()=>lo(n,e.organization)}),r.push({label:"all organizations",fetch:()=>Sa(n)}),r}async function lo(n,t){let{items:e}=await n.listOrganizationProjects(t,{limit:200});return e.map(co)}async function Sa(n){let{items:t}=await n.listProjects({limit:200});return t.map(co)}var ge=B(()=>{"use strict";U()});var jt={};Wn(jt,{waitCommand:()=>Ge,waitForJob:()=>mo});import{Command as ja}from"commander";import Ea from"ora";async function mo(n,t,e,r=600){let o=Date.now(),i=r*1e3,s=1e4,c=null;for(e.options.json||(c=Ea("Waiting for job completion...").start());;){let u=Date.now()-o;if(u>=i)throw c&&Z(c,"Timeout waiting for job completion"),new Y(`Job ${n} did not complete within ${r} seconds \u2014 check ${t.dashboardUrl(n)} to continue tracking`);let l=await t.getJob(n);if(c){let p=e.formatDuration(Math.floor(u/1e3));c.text=`Waiting for job completion... (${p} elapsed, status: ${l.status})`}if(l.status==="completed"){c&&ne(c,"Test Completed!"),e.options.json||(console.log(),e.detail("Duration",l.testDurationSeconds?e.formatDuration(l.testDurationSeconds):"N/A"),e.detail("Cost","$"+(l.costUsd||0).toFixed(3)),l.testerAlias&&e.detail("Tester",l.testerAlias),l.result&&(console.log(`
|
|
30
|
+
Results Summary:`),e.resultSummary(l.result)),console.log(),e.hints([["Full results",`runhuman job results ${n}`]]));return}if(l.status==="error"||l.status==="incomplete"||l.status==="abandoned"||l.status==="rejected")throw c&&Z(c,`Test ${l.status}`),new $e(n,l.status,t.dashboardUrl(n),l.testerResponse);await new Promise(p=>setTimeout(p,s))}}function Ge(){let n=new ja("wait");return n.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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c={projectId:e.project?await y(s,"project",e.project):void 0,organizationId:e.organization?await y(s,"organization",e.organization):void 0},u=await re(s,t,c,o,i);if(await mo(u,s,i,e.timeout||600),e.json){let l=await s.getJob(u),p=d.result(l);i.output(p)}}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}var We=B(()=>{"use strict";x();R();v();O();A();ge();U();Re()});import{Command as zn}from"commander";x();R();v();A();import{Command as Aa}from"commander";import Ta from"ora";A();function it(n,t){try{return JSON.parse(n)}catch(e){throw e instanceof SyntaxError?new j(sr(t,n,e)):e}}function sr(n,t,e){let r=lr(e.message,t),o=r===null?"":` at position ${r}`,i=r===null?ur(e.message):dr(e.message);return`Invalid JSON for ${n}${o}: ${i}`}function lr(n,t){let e=n.match(/position (\d+)/);if(e)return Number(e[1]);let r=n.match(/line (\d+) column (\d+)/);return r?cr(t,Number(r[1]),Number(r[2])):null}function cr(n,t,e){let r=n.split(`
|
|
31
|
+
`),o=0;for(let i=0;i<t-1&&i<r.length;i++)o+=r[i].length+1;return o+e-1}function ur(n){return n.replace(/\s+/g," ").trim()}function dr(n){return n.replace(/\s*at position \d+/g,"").replace(/\s*\(line \d+ column \d+\)/g,"").replace(/\s+/g," ").trim()}var _={PENDING:"pending",PREPARING:"preparing",QUEUED:"queued",WAITING:"waiting",WORKING:"working",COMPLETED:"completed",INCOMPLETE:"incomplete",ABANDONED:"abandoned",REJECTED:"rejected",ERROR:"error",UNTESTABLE:"untestable"};function qe(n){let t=[];function e(r,o){if(typeof r!="object"||r===null)return;let i=r;if(i.type==="object"&&(i.additionalProperties!==!1&&t.push(`${o}: Object schemas must have "additionalProperties": false for OpenAI structured outputs`),i.properties&&typeof i.properties=="object")){let s=Object.keys(i.properties),c=Array.isArray(i.required)?i.required:[],u=s.filter(l=>!c.includes(l));u.length>0&&t.push(`${o}: All properties must be in "required" array for OpenAI structured outputs. Missing: ${u.join(", ")}`);for(let[l,p]of Object.entries(i.properties))e(p,`${o}.properties.${l}`)}i.type==="array"&&(i.items===void 0?t.push(`${o}: Array schemas must declare "items" for OpenAI structured outputs (e.g. { "type": "array", "items": { "type": "string" } })`):e(i.items,`${o}.items`))}for(let[r,o]of Object.entries(n))e(o,r);return{valid:t.length===0,errors:t}}function He(n){if(!(n.include.length===0&&!n.other))return JSON.stringify(n)}var ce="Other",ue=[{label:"Pending",statuses:[_.PENDING,_.PREPARING]},{label:"Queued",statuses:[_.QUEUED,_.WAITING]},{label:"Working",statuses:[_.WORKING]},{label:"Completed",statuses:[_.COMPLETED]},{label:"Cancelled",statuses:[_.REJECTED]},{label:"Expired",statuses:[_.ABANDONED]},{label:"Failed",statuses:[_.INCOMPLETE,_.ERROR,_.UNTESTABLE]}],te=[{label:"Invalidated",statuses:[_.REJECTED],reasons:["tester_invalidated"]},{label:"Cancelled by tester",statuses:[_.REJECTED],reasons:["tester_cancelled"]},{label:"Admin terminated",statuses:[_.REJECTED],reasons:["admin_terminated"]},{label:"No capacity",statuses:[_.ABANDONED],reasons:["no_capacity"]},{label:"Never started",statuses:[_.ABANDONED],reasons:["tester_unresponsive"]},{label:"Overtime",statuses:[_.ABANDONED],reasons:["tester_overtime"]},{label:"Incomplete",statuses:[_.INCOMPLETE]},{label:"Error",statuses:[_.ERROR]},{label:"Untestable",statuses:[_.UNTESTABLE]}],Lt=[...ue.filter(n=>n.label!=="Failed"),...te.filter(n=>n.label==="Incomplete"||n.label==="Error"||n.label==="Untestable")];var st=[...ue.filter(n=>n.label!=="Cancelled"&&n.label!=="Expired"&&n.label!=="Failed").map(at),...te.map(at)];function at(n){return n.reasons?{statuses:n.statuses,reasons:n.reasons}:{statuses:n.statuses}}function lt(n){return ue.find(t=>t.label===n)??te.find(t=>t.label===n)}function Ke(n){if(n.length===0)return;let t=n.includes(ce),e=[];for(let r of n){if(r===ce)continue;let o=lt(r);o&&e.push(at(o))}return t?{include:e,other:st}:e.length>0?{include:e}:void 0}function we(n){return n===ce||te.some(t=>t.label===n)}var $t={high:1800*1e3,medium:7200*1e3,low:1440*60*1e3};ot();var ct="runhuman-qa-in-progress",ut="runhuman-qa-success",dt="runhuman-qa-fail",Qt=Object.freeze([ct,ut,dt]);var pt=["ios","android","pc","mac"],mt=["english","spanish"];var Ce={type:"multi-select",matchMode:"any",values:pt,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:mt,labels:{english:"English",spanish:"Spanish"},slackLabels:{english:"English speaker",spanish:"Spanish speaker"},categoryName:"language",sectionLabel:"Language",tooltip:"Tester must speak all selected languages"};var Zt=["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"],ku=new Set(Zt);var je=["structured-output","transcript","console-logs","network-requests","conversation","events","key-moments"];var oe=["daily","weekly","biweekly","monthly","once"],Be={daily:"Daily",weekly:"Weekly",biweekly:"Biweekly",monthly:"Monthly",once:"One-time"};var Ve={0:"Sun",1:"Mon",2:"Tue",3:"Wed",4:"Thu",5:"Fri",6:"Sat"};var Ee=["starter","pro","max","enterprise","enterprise_pro"],eo=new Set(Ee),ft=["autonomous","autonomy_plus"],to=new Set(ft),gt=[25,50,100,250],oo=new Set(gt);var no=Object.fromEntries(Ee.map((n,t)=>[n,t]));function ht(n){return n==="inherit"||n==="always"||n==="never"}A();function bt(n,t){let e;try{e=JSON.parse(n)}catch(i){let s=i instanceof SyntaxError?i.message:String(i);throw new j(`Invalid JSON in ${t}: ${s}`)}if(typeof e!="object"||e===null||Array.isArray(e))throw new j(`${t} must be a JSON object mapping field names to JSON Schema definitions`);let r=e,o=qe(r);if(!o.valid)throw new j(`Invalid output schema in ${t}:
|
|
33
32
|
- ${o.errors.join(`
|
|
34
|
-
- `)}`);return
|
|
33
|
+
- `)}`);return r}A();var Ae={organization:{name:"Organization ID",flag:"--organization",configKey:"organization",envVar:"RUNHUMAN_ORGANIZATION",switchCommand:"runhuman orgs switch <organization-id>"},project:{name:"Project ID",flag:"--project",configKey:"project",envVar:"RUNHUMAN_PROJECT",switchCommand:"runhuman projects switch <project-id>"}};function pa(n,t){return n?"flag":process.env[t.envVar]?"env":"global config"}function E(n,t,e,r){let o=Ae[n],i=t||e[o.configKey];if(!i)throw new j(`${o.name} is required. You can:
|
|
35
34
|
\u2022 Pass ${o.flag} <id> for this command
|
|
36
35
|
\u2022 Set a default: ${o.switchCommand}
|
|
37
|
-
\u2022 Set via env: export ${o.envVar}=<id>`);let
|
|
36
|
+
\u2022 Set via env: export ${o.envVar}=<id>`);let s=pa(t,o);return s!=="flag"&&r.info(`Using default ${n}: ${i} [from ${s}]`),i}U();Re();function fo(n,t){return n.split(",").map(e=>e.trim()).filter(e=>e.length>0).map(e=>{let r=Number(e);if(!Number.isInteger(r)||r<=0)throw new j(`Invalid value "${e}" in ${t}: expected positive integers (e.g. 42,43)`);return r})}function Et(){let n=new Aa("create");return n.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|both (default: both)").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 capable of recording social-media-style videos (Max+ tier)").option("--require-sideload","Require tester capable of sideloading mobile apps (Android APK or iOS IPA). Pair with --required-devices ios or --required-devices android. Max+ tier.").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("--email <pref>","Per-job email-on-completion override: inherit|always|never (default: inherit, defers to project then account default)").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 g().loadConfig({apiKey:e.apiKey}),i=new d({json:e.json,quiet:e.quiet,color:o.color}),s=new h(o);if(e.template&&e.templateFile)throw new j("Cannot use both --template and --template-file. Choose one.");let c=e.template||e.templateFile;if(!e.description&&!c)throw new j("Description is required (use -d flag or --template/--template-file)");if(e.resultsTemplate&&e.resultsTemplateInline)throw new j("Cannot use both --results-template and --results-template-inline. Choose one.");let u=await import("fs/promises"),l;if(e.schema){let z=await u.readFile(e.schema,"utf-8");l=bt(z,`--schema ${e.schema}`)}else e.schemaInline&&(l=bt(e.schemaInline,"--schema-inline"));let p;e.metadata&&(p=it(e.metadata,"--metadata"));let m;e.slackNotificationJson&&(m=it(e.slackNotificationJson,"--slack-notification-json"));let b;if(e.email!==void 0){if(!ht(e.email))throw new j(`Invalid --email value "${e.email}". Valid values: inherit, always, never`);b=e.email}let w;e.prNumbers&&(w=fo(e.prNumbers,"--pr-numbers"));let C;e.issueNumbers&&(C=fo(e.issueNumbers,"--issue-numbers"));let T;if(e.requiredDevices){let z=e.requiredDevices.split(",");for(let Ie of z)if(!Ce.values.includes(Ie))throw new j(`Invalid device "${Ie}". Valid values: ${Ce.values.join(", ")}`);T=z}let P;if(e.requiredLanguages){let z=e.requiredLanguages.split(",");for(let Ie of z)if(!Se.values.includes(Ie))throw new j(`Invalid language "${Ie}". Valid values: ${Se.values.join(", ")}`);P=z}let D;e.templateFile&&(D=await u.readFile(e.templateFile,"utf-8"));let M;e.resultsTemplate?M=await u.readFile(e.resultsTemplate,"utf-8"):e.resultsTemplateInline&&(M=e.resultsTemplateInline);let G;e.githubRepos?G=e.githubRepos.split(",").map(z=>z.trim()):e.githubRepo&&(G=[e.githubRepo]);let _e=E("project",e.project,o,i),H=await y(s,"project",_e),ae=t;if(!ae&&!c){let z=await s.getProject(H);if(z.defaultUrl)ae=z.defaultUrl;else if(o.defaultUrl)ae=o.defaultUrl,i.warn(`Project '${z.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 ${H} --default-url <url>\`.`);else throw new j(`URL is required. Provide one of:
|
|
38
37
|
\u2022 positional URL argument
|
|
39
38
|
\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:
|
|
41
|
-
${
|
|
42
|
-
${
|
|
43
|
-
${
|
|
44
|
-
`}function
|
|
45
|
-
`;let e=t.map(
|
|
39
|
+
\u2022 a default URL on the project (set it via \`runhuman projects update <projectId> --default-url <url>\` or the dashboard)`)}let k={projectId:H,url:ae,description:e.description||void 0,targetDurationMinutes:e.duration||o.defaultDuration,deviceClass:e.deviceClass||o.defaultDeviceClass};l&&(k.outputSchema=l),p&&(k.metadata=p),m&&(k.slackNotification=m),b&&(k.emailOnCompletion=b),w&&(k.prNumbers=w),C&&(k.issueNumbers=C),T&&(k.requiredDevices=T),P&&(k.requiredLanguages=P),D&&(k.templateContent=D),M&&(k.resultsTemplate=M),G&&(k.githubRepos=G),e.template&&(k.template=e.template),e.autoCreateGithubIssuesRepo&&(k.autoCreateGithubIssuesRepo=e.autoCreateGithubIssuesRepo),e.requireSocialVideos&&(k.requireSocialVideos=!0),e.requireSideload&&(k.requireSideload=!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 ee=e.githubToken||process.env.GITHUB_TOKEN;ee&&(k.githubToken=ee);let De=e.json?null:Ta("Creating QA test job...").start(),$=await s.createJob(k);if(De&&ne(De,"Job created successfully!"),$.deprecationWarnings&&i.deprecationWarnings($.deprecationWarnings),$.warnings&&i.warnings($.warnings),e.json){let z=d.result({jobId:$.jobId,message:$.message,dashboardUrl:s.dashboardUrl($.jobId),...$.deprecationWarnings&&{deprecationWarnings:$.deprecationWarnings},...$.warnings&&{warnings:$.warnings}});i.output(z)}else e.quiet?console.log($.jobId):(i.detail("Job ID",$.jobId),k.url&&i.detail("URL",k.url),i.detail("Dashboard",s.dashboardUrl($.jobId)),console.log(),i.hints([["Track progress",`runhuman job status ${$.jobId}`],["Wait for result",`runhuman job wait ${$.jobId}`]]));if(e.sync){let{waitForJob:z}=await Promise.resolve().then(()=>(We(),jt));await z($.jobId,s,i,e.wait||300)}}catch(r){let o=f(r);new d({json:e.json,quiet:!1}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}x();R();v();O();A();ge();U();import{Command as va}from"commander";function At(){let n=new va("status");return n.aliases(["show","get","info"]).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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c={projectId:e.project?await y(s,"project",e.project):void 0,organizationId:e.organization?await y(s,"organization",e.organization):void 0},u=await re(s,t,c,o,i),l=await s.getJob(u);if(e.json){let p=d.result(l);i.output(p)}else i.heading(`Job Status: ${u}`),i.detail("Status",i.formatStatus(l.status)),l.testerAlias&&i.detail("Tester",l.testerAlias),l.url&&i.detail("URL",l.url),l.description&&i.detail("Description",l.description),l.createdAt&&i.detail("Created",i.formatTimestamp(l.createdAt)),l.completedAt&&i.detail("Completed",i.formatTimestamp(l.completedAt)),l.testDurationSeconds&&i.detail("Duration",i.formatDuration(l.testDurationSeconds)),l.costUsd&&i.detail("Cost",`$${l.costUsd.toFixed(3)}`),l.deviceClass&&i.detail("Device Class",l.deviceClass),l.requiredDevices?.length&&i.detail("Required Devices",l.requiredDevices.join(", ")),l.requiredLanguages?.length&&i.detail("Required Langs",l.requiredLanguages.join(", ")),l.requireSocialVideos&&i.detail("Social Videos","required"),l.requireSideload&&i.detail("Sideload","required"),l.maxExtensionMinutes!==void 0&&l.maxExtensionCount!==void 0&&l.extensionsUsed!==void 0&&i.detail("Extensions",`${l.extensionsUsed}/${l.maxExtensionCount} used (${l.maxExtensionMinutes} min total)`),i.detail("Dashboard",s.dashboardUrl(u)),console.log(),l.status==="pending"||l.status==="queued"||l.status==="waiting"||l.status==="working"?i.hints([["Wait for completion",`runhuman job wait ${u}`]]):l.status==="completed"&&i.hints([["View results",`runhuman job results ${u}`]])}catch(r){let o=new d({json:e.json}),i=f(r);o.outputError(i.message,i.details),process.exit(i.exitCode)}}),n}We();x();R();v();O();A();ge();U();import{Command as xa}from"commander";var go="=".repeat(60);function K(n){return`
|
|
40
|
+
${go}
|
|
41
|
+
${n}
|
|
42
|
+
${go}
|
|
43
|
+
`}function Oe(n){return new Date(n).toLocaleTimeString()}function Ra(n){let t=Math.floor(n/60),e=Math.floor(n%60);return`${String(t).padStart(2,"0")}:${String(e).padStart(2,"0")}`}function ho(n){let t=n.transcription?.sessions.flatMap(r=>r.segments)??[];if(t.length===0)return K("Transcript")+` No transcript available
|
|
44
|
+
`;let e=t.map(r=>{let o=r.words[0];return` [${o?Ra(o.start):"00:00"}] ${r.text}`});return K("Transcript")+e.join(`
|
|
46
45
|
`)+`
|
|
47
|
-
`}function
|
|
48
|
-
`;let e=t.map(o=>{let
|
|
46
|
+
`}function bo(n){let t=n.testerData?.consoleMessages;if(!t?.length)return K("Console Logs")+` No console logs recorded
|
|
47
|
+
`;let e=t.map(o=>{let i=Oe(o.timestamp);return` [${o.type}] ${i} ${o.message}`}),r=K("Console Logs")+e.join(`
|
|
49
48
|
`)+`
|
|
50
|
-
`;if(
|
|
49
|
+
`;if(n.testerData?.truncationInfo){let o=n.testerData.truncationInfo;r+=`
|
|
51
50
|
(Truncated: ${JSON.stringify(o)})
|
|
52
|
-
`}return
|
|
53
|
-
`;let e=t.map(
|
|
51
|
+
`}return r}function yo(n){let t=n.testerData?.networkRequests;if(!t?.length)return K("Network Requests")+` No network requests recorded
|
|
52
|
+
`;let e=t.map(r=>{let o=Oe(r.timestamp),i=r.status??"---";return` ${r.method} ${i} ${r.url} (${o})`});return K("Network Requests")+e.join(`
|
|
54
53
|
`)+`
|
|
55
|
-
`}function
|
|
56
|
-
`;let e=t.map(
|
|
54
|
+
`}function Io(n){let t=n.testerData?.events;if(!t?.length)return K("User Events")+` No events recorded
|
|
55
|
+
`;let e=t.map(r=>{let o=Oe(r.timestamp),i;switch(r.type){case"click":i=`Click at (${r.x}, ${r.y})${r.element?` on ${r.element}`:""}`;break;case"page_load":i=`${r.event}: ${r.url}`;break;case"form_submit":i=`Form submit (${r.formInfo.fieldCount} fields)`;break;case"spa_route_change":i=`Route: ${r.route}`;break;case"keyboard_input":i=`Typed in ${r.elementInfo.tagName}`;break;case"tab_change":i=`Tab: ${r.title||r.url}`;break}return` [${r.type}] ${o} ${i}`});return K("User Events")+e.join(`
|
|
57
56
|
`)+`
|
|
58
|
-
`}function
|
|
59
|
-
`;let t=
|
|
57
|
+
`}function wo(n){if(!n.keyMoments?.length)return K("Key Moments")+` No key moments available
|
|
58
|
+
`;let t=n.keyMoments.map(e=>{let r=Oe(e.timestamp);return` [${e.type}] ${r} ${e.description} (significance: ${e.significance})`});return K("Key Moments")+t.join(`
|
|
60
59
|
`)+`
|
|
61
|
-
`}function
|
|
62
|
-
`;let t=
|
|
60
|
+
`}function Co(n){if(!n.conversationHistory?.length)return K("Conversation History")+` No conversation messages
|
|
61
|
+
`;let t=n.conversationHistory.map(e=>{let r=Oe(e.timestamp),o=e.role.charAt(0).toUpperCase()+e.role.slice(1);return` [${r}] ${o}: ${e.content}`});return K("Conversation History")+t.join(`
|
|
63
62
|
`)+`
|
|
64
|
-
`}function
|
|
65
|
-
`)});return
|
|
63
|
+
`}function So(n){if(!n.extractedIssues?.length)return"";let t=n.extractedIssues.map((e,r)=>{let o=[` ${r+1}. [${e.severity.toUpperCase()}] ${e.title}`,` ${e.description}`];if(e.reproductionSteps?.length&&(o.push(" Steps:"),e.reproductionSteps.forEach((i,s)=>{let c=typeof i=="string"?i:i.text;o.push(` ${s+1}. ${c}`)})),e.relatedIssues?.length){let i=e.relatedIssues.map(s=>`#${s.issueNumber} (${s.relation}, ${Math.round(s.confidence*100)}%)`).join(", ");o.push(` Related: ${i}`)}return o.join(`
|
|
64
|
+
`)});return K(`Extracted Issues (${n.extractedIssues.length})`)+t.join(`
|
|
66
65
|
|
|
67
66
|
`)+`
|
|
68
|
-
`}function
|
|
69
|
-
${e.issueUrl}`});return
|
|
67
|
+
`}function jo(n){if(!n.createdIssues?.length)return"";let t=n.createdIssues.map(e=>{let r=e.isNew?"new":"comment on existing",o=e.matchReason?` \u2014 ${e.matchReason}`:"";return` #${e.issueNumber}: ${e.title} (${r}${o})
|
|
68
|
+
${e.issueUrl}`});return K(`Created GitHub Issues (${n.createdIssues.length})`)+t.join(`
|
|
70
69
|
|
|
71
70
|
`)+`
|
|
72
|
-
`}function
|
|
71
|
+
`}function Tt(){let n=new xa("results");return n.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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c={projectId:e.project?await y(s,"project",e.project):void 0,organizationId:e.organization?await y(s,"organization",e.organization):void 0},u=await re(s,t,c,o,i),l=await s.getJob(u);if(e.json){let w=d.result(l);i.output(w);return}i.heading(`Test Results: ${u}`),i.sectionHeader("Job Information"),i.detail("Job ID",l.id),i.detail("Status",l.status),l.testerAlias&&i.detail("Tester",l.testerAlias),l.url&&i.detail("URL",l.url),l.description&&i.detail("Description",l.description),l.createdAt&&i.detail("Created",i.formatTimestamp(l.createdAt)),l.completedAt&&i.detail("Completed",i.formatTimestamp(l.completedAt)),l.testDurationSeconds&&i.detail("Duration",i.formatDuration(l.testDurationSeconds)),l.costUsd&&i.detail("Cost",`$${l.costUsd.toFixed(3)}`),l.deviceClass&&i.detail("Device Class",l.deviceClass),l.requiredDevices?.length&&i.detail("Required Devices",l.requiredDevices.join(", ")),l.requiredLanguages?.length&&i.detail("Required Langs",l.requiredLanguages.join(", ")),l.requireSocialVideos&&i.detail("Social Videos","required"),l.requireSideload&&i.detail("Sideload","required"),l.result&&Object.keys(l.result).length>0&&!e.raw&&(i.sectionHeader("Structured Results (Extracted)"),i.resultSummary(l.result)),l.testerResponse&&!e.schemaOnly&&(i.sectionHeader("Tester Feedback"),console.log(" "+l.testerResponse.split(`
|
|
73
72
|
`).join(`
|
|
74
|
-
`)));let p=
|
|
75
|
-
`))}catch(
|
|
76
|
-
`),
|
|
73
|
+
`)));let p=So(l);p&&console.log(p);let m=jo(l);m&&console.log(m);let b=e.all;(b||e.transcript)&&console.log(ho(l)),(b||e.consoleLogs)&&console.log(bo(l)),(b||e.network)&&console.log(yo(l)),(b||e.events)&&console.log(Io(l)),(b||e.keyMoments)&&console.log(wo(l)),(b||e.conversation)&&console.log(Co(l)),i.separator(),console.log(),l.richResultsGated&&(console.log(" Rich test data (transcript, logs, network) requires a paid plan."),console.log(` Upgrade at https://runhuman.com/pricing
|
|
74
|
+
`))}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}x();R();v();O();A();import{Command as Ua}from"commander";U();A();var Ye=ue.map(n=>n.label),Oa=[...te.map(n=>n.label),ce],Eo=[...Ye,...Oa];function Ao(n){return n.split(",").map(t=>t.trim()).filter(t=>t.length>0)}function To(n){let t=n.toLowerCase();for(let e of Eo)if(e.toLowerCase()===t)return e;return null}function vo(n,{isAdmin:t}){if(!n||n.trim()===""||n.trim().toLowerCase()==="all")return;let e=Ao(n),r=[];for(let i of e){let s=To(i);if(!s)throw new j(Pa(i,{isAdmin:t}));if(!t&&we(s))throw new j(ka(i));r.push(s)}let o=Ke(r);if(o)return He(o)}function Ro(n){return!n||n.trim()===""||n.trim().toLowerCase()==="all"?!1:Ao(n).some(t=>{let e=To(t);return e?we(e):!1})}function Pa(n,{isAdmin:t}){let e=t?Eo:Ye,r=t?"":" (admins may additionally use fine-grain labels.)";return`Invalid filter "${n}". Allowed labels: all, ${e.join(", ")}.${r}`}function ka(n){return`Filter "${n}" is an admin-only label. Allowed labels for this account: all, ${Ye.join(", ")}.`}var xo=Ye;A();var _a=`Status filter: all|${xo.join("|")} (comma-separated, case-insensitive; admins may additionally pass fine-grain labels)`;function vt(){let n=new Ua("list");return n.description("List all jobs with optional filtering").argument("[filter]",_a).option("-p, --project <id>","Filter by project").option("--org <id>","Scope to all projects in an organization (mutually exclusive with --project)").option("-n, --limit <number>","Number of results (default: 20)",parseInt).option("--offset <number>","Pagination offset (default: 0)",parseInt).option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{if(e.org&&e.project)throw new j("--org and --project are mutually exclusive. Pick one.");let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c,u;if(e.org)u=await y(s,"organization",e.org);else{let C=E("project",e.project,o,i);c=await y(s,"project",C)}let l=Ro(t)?(await s.getCurrentUser()).isAdmin:!1,p=vo(t,{isAdmin:l}),m={limit:e.limit||20,offset:e.offset||0};c&&(m.projectId=c),u&&(m.organizationId=u),p&&(m.chipFilter=p);let{items:b,pagination:w}=await s.listJobs(m);if(e.json){let C=d.result({items:b},{pagination:w});i.output(C)}else{if(w.total===0){i.heading("Recent Jobs (0)"),console.log(`No jobs found.
|
|
75
|
+
`),i.hints([["Create a job",'runhuman job create <url> -d "Test description"']]);return}i.heading(`Recent Jobs (${b.length} of ${w.total})`),console.log(i.formatJobList(b)),console.log(),i.hints([["View details","runhuman job status <jobId>"],["View results","runhuman job results <jobId>"]])}}catch(r){let o=new d({json:e.json}),i=f(r);o.outputError(i.message,i.details),process.exit(i.exitCode)}}),n}x();R();v();import{Command as Da}from"commander";import Na from"chokidar";A();U();import{existsSync as Qe,mkdirSync as Ma,readFileSync as Rt,writeFileSync as La,unlinkSync as $a}from"fs";import{join as Oo}from"path";import{homedir as Po}from"os";var ie=Oo(Po(),".config","runhuman","watch.pid");function xt(){let n=new Da("watch");return n.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 Fa();return}if(e.status){Ja();return}ko()&&(console.log(`
|
|
77
76
|
Watch mode is already running`),console.log(` Use --stop to stop it first
|
|
78
|
-
`),process.exit(1));let o=await new
|
|
77
|
+
`),process.exit(1));let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=o.watch||{},s=t.length>0?t:i.patterns||["src/**/*"],c=e.ignore||i.ignore||["**/node_modules/**","**/dist/**","**/build/**","**/.git/**"],u=e.debounce||i.debounce||2e3,l=e.description||i.description||"Auto-test from watch mode",p=e.template||i.template,m=new d({color:o.color}),b=new h(o),w=E("project",void 0,o,m),C=await y(b,"project",w),T=e.url||i.url;if(!T){let H=await b.getProject(C);H.defaultUrl?T=H.defaultUrl:o.defaultUrl?(T=o.defaultUrl,m.warn(`Project '${H.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 ${C} --default-url <url>\`.`)):(console.log(`
|
|
79
78
|
\u274C Error: URL required
|
|
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 "+
|
|
79
|
+
`),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 "+C+" --default-url <url>`)\n"),process.exit(1))}let P=p;m.heading("Starting watch mode"),m.detail("Watching",s.join(", ")),m.detail("Ignoring",c.join(", ")),m.detail("Debounce",`${u}ms`),m.detail("URL",T),p&&m.detail("Template",p),console.log(`
|
|
81
80
|
Press Ctrl+C to stop
|
|
82
|
-
`),
|
|
83
|
-
Creating test job for ${
|
|
84
|
-
`);try{let
|
|
81
|
+
`),za(process.pid);let D=null,M=new Set,G=Na.watch(s,{ignored:c,persistent:!0,ignoreInitial:!0});G.on("change",H=>{M.add(H),console.log(` Changed: ${H}`),D&&clearTimeout(D),D=setTimeout(async()=>{let ae=Array.from(M);M.clear(),console.log(`
|
|
82
|
+
Creating test job for ${ae.length} file(s)...
|
|
83
|
+
`);try{let k={projectId:C,url:T,description:`${l}
|
|
85
84
|
|
|
86
85
|
Changed files:
|
|
87
|
-
${
|
|
88
|
-
`)}`,template:
|
|
86
|
+
${ae.map(De=>`- ${De}`).join(`
|
|
87
|
+
`)}`,template:P},ee=await b.createJob(k);console.log(` Job created: ${ee.jobId}`),console.log(` Dashboard: ${b.dashboardUrl(ee.jobId)}
|
|
89
88
|
`),console.log(` Watching for more changes...
|
|
90
|
-
`)}catch(
|
|
89
|
+
`)}catch(k){let ee=f(k);m.outputError(ee.message,ee.details),console.log(`
|
|
91
90
|
Watching for more changes...
|
|
92
|
-
`)}},u)}),
|
|
93
|
-
\u274C Watch error: ${
|
|
94
|
-
`)});let
|
|
91
|
+
`)}},u)}),G.on("error",H=>{console.error(`
|
|
92
|
+
\u274C Watch error: ${H instanceof Error?H.message:String(H)}
|
|
93
|
+
`)});let _e=()=>{console.log(`
|
|
95
94
|
|
|
96
95
|
Stopping watch mode...
|
|
97
|
-
`),
|
|
96
|
+
`),G.close(),Pe(),process.exit(0)};process.on("SIGINT",_e),process.on("SIGTERM",_e)}catch(r){let o=f(r);new d({}).outputError(o.message,o.details),Pe(),process.exit(o.exitCode)}}),n}function za(n){let t=Oo(Po(),".config","runhuman");Qe(t)||Ma(t,{recursive:!0}),La(ie,n.toString())}function Pe(){Qe(ie)&&$a(ie)}function ko(){if(!Qe(ie))return!1;try{let n=parseInt(Rt(ie,"utf-8"));return process.kill(n,0),!0}catch{return Pe(),!1}}async function Fa(){if(!Qe(ie)){console.log(`
|
|
98
97
|
Watch mode is not running
|
|
99
|
-
`);return}try{let
|
|
98
|
+
`);return}try{let n=parseInt(Rt(ie,"utf-8"));process.kill(n,"SIGTERM"),Pe(),console.log(`
|
|
100
99
|
Watch mode stopped
|
|
101
|
-
`)}catch{
|
|
100
|
+
`)}catch{Pe(),console.log(`
|
|
102
101
|
Watch process not found (already stopped)
|
|
103
|
-
`)}}function
|
|
104
|
-
Watch mode is running`),console.log(` PID: ${
|
|
105
|
-
Stop with: runhuman watch --stop
|
|
102
|
+
`)}}function Ja(){if(ko()){let n=parseInt(Rt(ie,"utf-8"));console.log(`
|
|
103
|
+
Watch mode is running`),console.log(` PID: ${n}`),console.log(`
|
|
104
|
+
Stop with: runhuman job watch --stop
|
|
106
105
|
`)}else console.log(`
|
|
107
106
|
Watch mode is not running
|
|
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
|
|
107
|
+
`)}import{Command as qa}from"commander";import Ot from"cli-table3";x();R();A();v();O();var Uo=je.join(", ");function Ha(n){return je.includes(n)}function _o(){let n=new qa("artifact");return n.description("Download a raw session artifact (transcript, logs, events, etc.)").argument("<jobId>","Job ID").argument("<type>",`Artifact type: ${Uo}`).option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e,r)=>{try{if(!Ha(e))throw new j(`Invalid artifact type "${e}". Valid types are: ${Uo}`);let i=await new g().loadConfig({apiKey:r.apiKey,apiUrl:r.apiUrl}),s=I(r,i),u=await new h(i).getJobArtifact(t,e);if(r.json){s.output(d.result(u));return}Ka(s,e,u)}catch(o){let i=f(o);new d({json:r.json}).outputError(i.message,i.details),process.exit(i.exitCode)}}),n}function Ka(n,t,e){switch(t){case"structured-output":Ba(n,e);return;case"transcript":Va(n,e);return;case"console-logs":Ga(n,e);return;case"network-requests":Wa(n,e);return;case"conversation":Ya(n,e);return;case"events":Qa(n,e);return;case"key-moments":Xa(n,e);return}}function Ba(n,t){n.heading("Structured Output"),console.log(JSON.stringify(t.result,null,2)),console.log(),n.hints([["Extract with jq","runhuman job artifact <jobId> structured-output --json | jq .data.result"]])}function Va(n,t){n.heading("Transcript");let e=t.transcription.sessions;if(e.length===0){console.log("(no transcript captured)"),console.log();return}e.forEach((r,o)=>{e.length>1&&console.log(`Session ${o+1}:`),r.segments.forEach(i=>{console.log(` ${i.text}`)}),console.log()})}function Ga(n,t){let e=t.consoleMessages;if(n.heading(`Console Logs (${e.length})`),e.length===0){console.log("(no console messages captured)"),console.log();return}let r=new Ot({head:n.tableHead(["Type","Timestamp","Message"]),colWidths:[10,26,60],wordWrap:!0});e.forEach(o=>{r.push([o.type,o.timestamp,o.message.slice(0,500)])}),console.log(r.toString()),console.log()}function Wa(n,t){let e=t.networkRequests;if(n.heading(`Network Requests (${e.length})`),e.length===0){console.log("(no network requests captured)"),console.log();return}let r=new Ot({head:n.tableHead(["Method","Status","URL"]),colWidths:[8,8,80],wordWrap:!0});e.forEach(o=>{let i=o.status===void 0?"-":String(o.status);r.push([o.method,i,o.url.slice(0,200)])}),console.log(r.toString()),console.log()}function Ya(n,t){let e=t.conversationHistory;if(n.heading(`Conversation (${e.length} messages)`),e.length===0){console.log("(no conversation history captured)"),console.log();return}e.forEach(r=>{console.log(`[${r.role}]`),console.log(r.content.trim()),console.log()})}function Qa(n,t){let{events:e,errors:r}=t;if(n.heading(`Events (${e.length} events, ${r.length} errors)`),e.length===0&&r.length===0){console.log("(no events or errors captured)"),console.log();return}if(e.length>0){let o=new Ot({head:n.tableHead(["Type","Timestamp","Detail"]),colWidths:[16,26,60],wordWrap:!0});e.forEach(i=>{o.push([i.type,String(i.timestamp),JSON.stringify(i).slice(0,200)])}),console.log(o.toString()),console.log()}r.length>0&&(n.heading("Page Errors"),r.forEach(o=>{console.log(` ${o.timestamp} ${o.message}`)}),console.log())}function Xa(n,t){let e=t.keyMoments;if(n.heading(`Key Moments (${e.length})`),e.length===0){console.log("(no key moments extracted)"),console.log();return}e.forEach((r,o)=>{console.log(` ${o+1}. [${r.timestamp}] (${r.type}) ${r.description}`)}),console.log()}x();R();A();v();O();import{createWriteStream as Za}from"fs";import{pipeline as es}from"stream/promises";import{basename as ts,resolve as os}from"path";import ns from"axios";import{Command as rs}from"commander";function Do(){let n=new rs("recording");return n.description("Download a job recording mp4").argument("<jobId>","Job ID").option("-o, --output <path>","Output file path").option("--session <index>","Recording session index to download","0").option("--url","Print the presigned recording URL instead of downloading").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let r=is(e.session),i=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=I(e,i),c=new h(i),{recordings:u}=await c.getJobRecording(t);if(u.length===0)throw new j("No recordings are available for this job");let l=u.find(m=>m.sessionIndex===r);if(!l){let m=u.map(b=>b.sessionIndex).join(", ");throw new j(`Recording session ${r} not found. Available sessions: ${m}`)}if(e.url){e.json?s.output(d.result({jobId:t,recording:l})):console.log(l.url);return}let p=os(e.output??as(t,r,u.length));if(await ss(l.url,p),e.json){s.output(d.result({jobId:t,outputPath:p,recording:l}));return}s.success(`Recording downloaded to ${p}`)}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}function is(n){let t=Number(n??"0");if(!Number.isInteger(t)||t<0)throw new j("--session must be a non-negative integer");return t}function as(n,t,e){let r=e>1?`-session-${t}`:"";return`${ts(n)}-recording${r}.mp4`}async function ss(n,t){let e=await ns.get(n,{responseType:"stream"});await es(e.data,Za(t))}x();R();A();v();O();import{Command as No}from"commander";function Mo(){let n=new No("share");return n.description("Enable public sharing for a job and print the share URL").argument("<jobId>","Job ID").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),c=await new h(o).shareJob(t),u=typeof c.shareUrl=="string"?c.shareUrl:void 0;if(e.json){i.output(d.result({jobId:t,shared:!0,shareUrl:u}));return}i.success(`Sharing enabled for job ${t}`),u&&i.detail("Public URL",u),console.log()}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}function Lo(){let n=new No("unshare");return n.description("Disable public sharing for a job").argument("<jobId>","Job ID").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o);if(await new h(o).unshareJob(t),e.json){i.output(d.result({jobId:t,shared:!1}));return}i.success(`Sharing disabled for job ${t}`),console.log()}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}x();R();A();v();O();import{Command as ls}from"commander";function $o(){let n=new ls("create-issue");return n.description("File one extracted finding from a completed job as a GitHub issue").argument("<jobId>","Job ID").requiredOption("--index <n>","Zero-based index into job.extractedIssues",parseInt).option("--repo <owner/repo>","Override target GitHub repo (otherwise uses job default)").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let r=e.index;if(!Number.isInteger(r)||r<0)throw new j(`--index must be a non-negative integer (got "${e.index}")`);let i=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),s=I(e,i),c=new h(i),u=await c.getJob(t);if(u.status!=="completed")throw new j(`Job ${t} is not completed (status: ${u.status}). Issue filing requires a completed job.`);let l=u.extractedIssues;if(!l||r>=l.length)throw new j(`Job ${t} has no finding at index ${r} (job has ${l?l.length:0} findings).`);let p=await c.createIssueFromFinding(t,{issue:l[r],extractedIssueIndex:r,githubRepo:e.repo}),{created:m,duplicateInfo:b}=p;if(e.json){s.output(d.result(p));return}s.success("Issue created"),s.detail("URL",m.issueUrl),s.detail("Number",`#${m.issueNumber}`),s.detail("Title",m.title),!m.isNew&&m.matchReason&&s.detail("Match",m.matchReason),b&&b.isDuplicate&&b.matchingIssueNumber&&(console.log(),s.detail("Duplicate of",`#${b.matchingIssueNumber}`),b.matchReason&&s.detail("Match reason",b.matchReason)),console.log()}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}x();R();v();O();A();ge();U();Re();import{Command as cs}from"commander";import us from"ora";var ds=1e4,zo=600;async function ps(n,t,e,r){let o=Date.now(),i=r*1e3,s=null;for(e.options.json||(s=us("Waiting for enhanced-video render...").start());;){let c=Date.now()-o;if(c>=i)throw s&&Z(s,"Timeout waiting for enhanced-video render"),new Y(`Enhanced-video render for ${n} did not complete within ${r} seconds \u2014 check ${t.dashboardUrl(n)} to continue tracking`);let u=await t.getEnhancedVideoStatus(n);if(s){let l=e.formatDuration(Math.floor(c/1e3)),p=u.status??"pending";s.text=`Waiting for enhanced-video render... (${l} elapsed, status: ${p})`}if(u.status==="succeeded")return s&&ne(s,"Enhanced video rendered"),u;if(u.status==="failed")throw s&&Z(s,"Enhanced-video render failed"),new N(`Enhanced-video render for ${n} failed: ${u.lastError??"unknown error"}`,8,{jobId:n,status:"failed",detail:u.lastError??void 0});await new Promise(l=>setTimeout(l,ds))}}function ms(n){return n==="no_recording"?"Cannot render enhanced video: no LiveKit recording is available for this job.":n==="no_transcript"?"Cannot render enhanced video: no transcript is available for this job.":null}function fs(n,t,e){return`${n.dashboardUrl(t).split("/dashboard")[0]}/api/public/enhanced-video/${t}/${e}`}function gs(n,t,e,r){n.heading(`Enhanced Video Render: ${e}`);let o=r.status??"none";n.detail("Status",o),n.detail("Attempts",String(r.attempts)),r.lastError&&n.detail("Last Error",r.lastError),r.videoUrl&&n.detail("Video URL",r.videoUrl),r.publicToken&&!r.publicTokenDisabled&&n.detail("Public Share",fs(t,e,r.publicToken)),r.createdAt&&n.detail("Created",n.formatTimestamp(r.createdAt)),r.finishedAt&&n.detail("Finished",n.formatTimestamp(r.finishedAt)),n.detail("Dashboard",t.dashboardUrl(e)),console.log(),(o==="queued"||o==="running")&&n.hints([["Wait for completion",`runhuman job render-enhanced-video ${e} --wait`]])}function Fo(){let n=new cs("render-enhanced-video");return n.description("Render an enhanced video (logo bookends, captions, key-moment overlays) for a completed job").argument("<jobId>","Job ID to render an enhanced video for").option("-p, --project <id>","Scope search to a specific project").option("-o, --organization <id>","Scope search to a specific organization").option("--wait","Block until the render reaches a terminal state",!1).option("--timeout <seconds>",`Max wait time when --wait is set (default: ${zo})`,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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c={projectId:e.project?await y(s,"project",e.project):void 0,organizationId:e.organization?await y(s,"organization",e.organization):void 0},u=await re(s,t,c,o,i),l=await s.triggerEnhancedVideoRender(u);e.wait&&(l.status==="queued"||l.status==="running")&&(l=await ps(u,s,i,e.timeout||zo)),e.json?i.output(d.result(l)):gs(i,s,u,l)}catch(r){let o=new d({json:e.json});if(r instanceof j){let s=ms(r.details?.reason);s!==null&&(o.outputError(s,r.details),process.exit(r.exitCode))}let i=f(r);o.outputError(i.message,i.details),process.exit(i.exitCode)}}),n}x();R();A();v();O();import{Command as ws}from"commander";import hs from"http";import{URL as bs}from"url";import ys from"open";async function Xe(n){let{apiUrl:t,autoOpenBrowser:e=!0}=n;return new Promise((r,o)=>{let i=hs.createServer((c,u)=>{if(!c.url){u.writeHead(400),u.end("Bad request");return}let l=new bs(c.url,"http://localhost");if(l.pathname==="/callback"){let p=l.searchParams.get("token"),m=l.searchParams.get("email"),b=l.searchParams.get("projectId")||"",w=l.searchParams.get("error");if(w){u.writeHead(200,{"Content-Type":"text/html"}),u.end(Jo(w)),i.close(),o(new Error(w));return}if(p&&m){u.writeHead(200,{"Content-Type":"text/html"}),u.end(Is(m)),i.close(),r({token:p,email:m,projectId:b});return}u.writeHead(400,{"Content-Type":"text/html"}),u.end(Jo("Missing token or email in callback")),i.close(),o(new Error("Missing token or email in callback"));return}u.writeHead(404),u.end("Not found")});i.listen(0,"127.0.0.1",async()=>{let c=i.address();if(!c||typeof c=="string"){o(new Error("Failed to start local server"));return}let l=`http://127.0.0.1:${c.port}/callback`,p=`${t}/cli/auth?callback=${encodeURIComponent(l)}`;if(console.log(""),e){console.log("Opening browser for authentication...");try{await ys(p)}catch{console.log("Could not open browser automatically."),console.log(`
|
|
108
|
+
Please visit: ${p}`)}}else console.log(`Please visit: ${p}`);console.log(""),console.log("Waiting for authentication...")});let s=setTimeout(()=>{i.close(),o(new Error("Authentication timed out. Please try again."))},60*1e3);i.on("close",()=>clearTimeout(s)),i.on("error",c=>{clearTimeout(s),o(new Error(`Failed to start local server: ${c.message}`))})})}function Is(n){return`<!DOCTYPE html>
|
|
110
109
|
<html>
|
|
111
110
|
<head>
|
|
112
111
|
<title>Runhuman CLI - Authenticated</title>
|
|
@@ -144,11 +143,11 @@ Please visit: ${p}`)}}else console.log(`Please visit: ${p}`);console.log(""),con
|
|
|
144
143
|
<body>
|
|
145
144
|
<div class="container">
|
|
146
145
|
<h1>Successfully authenticated!</h1>
|
|
147
|
-
<p>Logged in as <span class="email">${
|
|
146
|
+
<p>Logged in as <span class="email">${n}</span></p>
|
|
148
147
|
<p class="close-note">You can close this tab and return to the terminal.</p>
|
|
149
148
|
</div>
|
|
150
149
|
</body>
|
|
151
|
-
</html>`}function
|
|
150
|
+
</html>`}function Jo(n){return`<!DOCTYPE html>
|
|
152
151
|
<html>
|
|
153
152
|
<head>
|
|
154
153
|
<title>Runhuman CLI - Authentication Failed</title>
|
|
@@ -188,52 +187,54 @@ Please visit: ${p}`)}}else console.log(`Please visit: ${p}`);console.log(""),con
|
|
|
188
187
|
<div class="container">
|
|
189
188
|
<h1>Authentication failed</h1>
|
|
190
189
|
<p>Please try again from the terminal.</p>
|
|
191
|
-
<div class="error">${
|
|
190
|
+
<div class="error">${n}</div>
|
|
192
191
|
</div>
|
|
193
192
|
</body>
|
|
194
|
-
</html>`}function
|
|
195
|
-
`)}catch(e){let
|
|
196
|
-
`);for(let u of
|
|
197
|
-
`);for(let u of
|
|
198
|
-
`),process.exit(0)),await new
|
|
193
|
+
</html>`}function qo(){let n=new ws("login");return J(n.description("Authenticate with Runhuman").option("--token <token>","Login with API key (skip browser)").option("--no-browser","Print auth URL instead of opening browser")).action(async t=>{try{let e=new g,r=await e.loadConfig(),o=I(t,r);if(t.token)await Cs(e,o,t.token,t.json);else{let i=t.browser!==!1&&r.autoOpenBrowser!==!1;await Ss(e,o,r.apiUrl,i,t.json)}}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n}async function Cs(n,t,e,r){n.saveCredentials({accessToken:e});let o=await n.loadConfig({apiKey:e}),i=new h(o),{user:s}=await i.getCurrentUser();n.saveUserInfo(s),r?t.output(d.result({user:s,loggedIn:!0})):(t.success("Successfully logged in!"),t.detail("Email",s.email),t.detail("Account ID",s.id),console.log())}async function Ss(n,t,e,r,o){o||console.log("Logging in to Runhuman...");let i=await Xe({apiUrl:e||"https://runhuman.com",autoOpenBrowser:r});n.saveCredentials({accessToken:i.token}),await n.set("project",i.projectId,!0);let s=await n.loadConfig({apiKey:i.token}),c=new h(s),{user:u}=await c.getCurrentUser();n.saveUserInfo(u),o?t.output(d.result({user:u,projectId:i.projectId,loggedIn:!0})):(console.log(""),t.success("Successfully logged in!"),t.detail("Email",u.email),t.detail("Account ID",u.id),t.detail("Default Project",i.projectId),console.log()),process.exit(0)}R();A();v();O();import{Command as js}from"commander";function Ho(){let n=new js("logout");return J(n.description("Log out and clear stored credentials")).action(async t=>{try{let e=new g,r=I(t);e.clearCredentials(),e.clearUserInfo(),t.json?r.output(d.result({loggedOut:!0})):(r.success("Logged out successfully!"),console.log(` Credentials have been cleared.
|
|
194
|
+
`))}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n}x();R();A();v();O();import{Command as As}from"commander";import{dirname as Es}from"path";async function Bo(n){let t=await n.list();return{organization:Ko("organization",t,n),project:Ko("project",t,n)}}function Ko(n,t,e){let r=Ae[n],o=r.configKey,i=t.env?.[o];if(i)return{id:i,source:{type:"env",envVar:r.envVar}};let s=t.project?.[o];if(s){let u=e.getProjectConfigFilepath();if(!u)throw new Error(`Project config supplied ${n} but the loader did not record a filepath`);return{id:s,source:{type:"project",filepath:u}}}let c=t.global?.[o];return c?{id:c,source:{type:"global"}}:null}function Vo(n){switch(n.type){case"env":return`${n.envVar} env var`;case"project":return`.runhumanrc at ${Es(n.filepath)}`;case"global":return"global config"}}function Go(n){return`Run \`${Ae[n].switchCommand}\` to set one.`}function Yo(){let n=new As("whoami");return J(n.description("Show who you are signed in as and which organization, project, and API the CLI will use by default").option("--api-key <key>","API key").option("--api-url <url>","API URL")).action(async t=>{try{let e=new g,r=await e.loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=await Bo(e),[c,u,l]=await Promise.all([i.getCurrentUser(),Ts(i,s.organization),vs(i,s.project)]),p=Rs(u,l),m={user:c.user,organization:u,project:l,apiUrl:{value:r.apiUrl},warnings:p};t.json?o.output(d.result(m)):(xs(o,m),o.warnings(p))}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n}async function Ts(n,t){if(!t)return null;let e=await n.getOrganization(t.id);return{id:e.id,name:e.name,source:t.source}}async function vs(n,t){if(!t)return null;let e=await n.getProject(t.id);return{id:e.id,name:e.name,organizationId:e.organizationId,source:t.source}}function Rs(n,t){let e=[];return n&&t&&t.organizationId!==n.id&&e.push(`Default project ${t.id} belongs to organization ${t.organizationId}, but the default organization is ${n.id}. Commands that use the default project will operate in a different org than you may expect.`),e}function xs(n,t){n.heading("Current Context"),n.detail("User",t.user.email),n.detail("Account ID",t.user.id),console.log(),Wo(n,"Organization",t.organization,"organization"),Wo(n,"Project",t.project,"project"),console.log(),n.detail("API",t.apiUrl.value),console.log()}function Wo(n,t,e,r){e?(n.detail(t,`${e.name} (${e.id})`),n.detail(" source",Vo(e.source))):(n.detail(t,"not set"),n.detail(" hint",Go(r)))}R();A();v();import{Command as Os}from"commander";function Qo(){let n=new Os("config");return n.description("Manage CLI configuration"),n.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 g().get(t),i=new d({json:e.json});if(e.json){let s=d.result({[t]:o});i.output(s)}else console.log(o!==void 0?o:"(not set)")}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.command("set").description("Set a configuration value").argument("<key>","Configuration key").argument("<value>","Configuration value").option("-g, --global","Set globally (not project-specific)").option("-j, --json","Output as JSON").action(async(t,e,r)=>{try{await new g().set(t,e,r.global);let i=new d({json:r.json});r.json?i.output(d.result({key:t,value:e,scope:r.global?"global":"project",set:!0})):i.success(`Set ${t} = ${e}`+(r.global?" (global)":" (project)"))}catch(o){let i=f(o);new d({json:r.json}).outputError(i.message,i.details),process.exit(i.exitCode)}}),n.command("list").description("List all configuration values").option("--show-secrets","Show API keys (default: masked)").option("-j, --json","Output as JSON").action(async t=>{try{let r=await new g().list(),o=new d({json:t.json});if(t.json){let i=d.result(r);o.output(i)}else{o.heading("Configuration");let i=["organization","project","apiKey","apiUrl"],s={organization:"RUNHUMAN_ORGANIZATION",project:"RUNHUMAN_PROJECT",apiKey:"RUNHUMAN_API_KEY",apiUrl:"RUNHUMAN_API_URL"};console.log(`Resolution chain (highest priority \u2192 lowest):
|
|
195
|
+
`);for(let u of i){let l=r.effective[u],p=r.env?.[u],m=r.project?.[u],b=r.global?.[u],w=u==="apiKey",C=D=>w&&!t.showSecrets?Ps(String(D)):String(D),T=p?"env":m?"project":b?"global":l?"default":null;console.log(` ${u}:`),console.log(l?` \u2713 ${C(l)} [from ${T}]`:" (not set)");let P=s[u];p&&console.log(` \u251C\u2500 env (${P}): ${C(p)}`),m&&console.log(` \u251C\u2500 project (.runhumanrc): ${C(m)}`),b&&console.log(` \u2514\u2500 global (~/.config/runhuman): ${C(b)}`),console.log()}let c=Object.keys(r.effective).filter(u=>!i.includes(u));if(c.length>0){console.log(`Other settings:
|
|
196
|
+
`);for(let u of c){let l=r.effective[u];l!==void 0&&console.log(` ${u}:`.padEnd(24)+String(l))}console.log()}console.log("Environment Variables:"),console.log(" RUNHUMAN_ORGANIZATION Default organization ID"),console.log(" RUNHUMAN_PROJECT Default project ID"),console.log(" RUNHUMAN_API_KEY API key"),console.log(" RUNHUMAN_API_URL API URL"),console.log(" RUNHUMAN_NO_COLOR=1 Disable colors"),console.log(),o.hints([["Set value","runhuman config set <key> <value>"],["Get value","runhuman config get <key>"]])}}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n.command("reset").description("Reset configuration to defaults").option("-g, --global","Reset global config").option("--project","Reset project config").option("--all","Reset all config").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.yes||(console.log(`\u26A0\uFE0F This will reset ${e} configuration to defaults.`),console.log(`Use --yes to skip this prompt.
|
|
197
|
+
`),process.exit(0)),await new g().reset(e);let o=new d({json:t.json});if(t.json){let i=d.result({scope:e,reset:!0});o.output(i)}else o.success(`Reset ${e} configuration`)}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n}function Ps(n){return n.length<=8?"****":n.substring(0,4)+"*".repeat(n.length-8)+n.substring(n.length-4)}A();v();import{Command as ks}from"commander";import Us from"inquirer";import{writeFileSync as _s}from"fs";import{join as Ds}from"path";function Xo(){let n=new ks("init");return n.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!
|
|
199
198
|
`),console.log("Let's set up your project. This will create:"),console.log(" - A configuration file (.runhumanrc)"),console.log(` - Setup your project defaults
|
|
200
|
-
`));let
|
|
199
|
+
`));let r=t.name,o=t.githubRepos?t.githubRepos.split(",").map(c=>c.trim()):t.githubRepo?[t.githubRepo]:void 0;if(!t.yes&&!t.json){let c=await Us.prompt([{type:"input",name:"projectName",message:"Project name:",default:"My Project",when:!r},{type:"input",name:"githubRepos",message:"Link to GitHub repositories (optional, comma-separated owner/repo):",when:!o}]);r=r||c.projectName,!o&&c.githubRepos&&(o=c.githubRepos.split(",").map(u=>u.trim()).filter(Boolean))}let i={githubRepos:o?.length?o:void 0,defaultDuration:5,defaultDeviceClass:"desktop"},s=Ds(process.cwd(),".runhumanrc");_s(s,JSON.stringify(i,null,2)),t.json?e.output(d.result({configPath:s,config:i,initialized:!0})):(e.success("Project initialized successfully!"),console.log(` Configuration saved to: .runhumanrc
|
|
201
200
|
`),console.log(` All set! Try creating your first test:
|
|
202
|
-
`),console.log(` runhuman create https://example.com -d "Test homepage"
|
|
201
|
+
`),console.log(` runhuman job create https://example.com -d "Test homepage"
|
|
203
202
|
`),console.log(` Learn more: https://runhuman.com/docs/cli
|
|
204
|
-
`)}catch(e){let
|
|
205
|
-
`),o.hints([["Create a project","runhuman projects create <name> -o <org-id>"]]);return}o.heading(`Your Projects (${u.length} of ${
|
|
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
|
-
`),
|
|
208
|
-
`),
|
|
209
|
-
`),console.log("
|
|
203
|
+
`))}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n}x();R();A();v();O();import{Command as Ns}from"commander";import Zo from"inquirer";U();ge();import Ms from"cli-table3";function en(){let n=new Ns("projects");return n.description("Manage projects"),J(n.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("--api-key <key>","API key").option("--api-url <url>","API URL")).action(async t=>{try{let r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=t.limit||20,c=t.offset||0,[{items:u,pagination:l},{items:p}]=await Promise.all([i.listProjects({limit:s,offset:c}),i.listOrganizations({})]),m=new Map(p.map(b=>[b.id,b.name]));if(t.json){let b=d.result({items:u},{pagination:l});o.output(b)}else{if(l.total===0){o.heading("Your Projects (0)"),console.log(`No projects found.
|
|
204
|
+
`),o.hints([["Create a project","runhuman projects create <name> -o <org-id>"]]);return}o.heading(`Your Projects (${u.length} of ${l.total})`);let b=new Ms({head:o.tableHead(["Name","Organization","Project ID","Created"]),colWidths:[25,25,25,15]});u.forEach(P=>{let D=m.get(P.organizationId)||"(unknown)";b.push([P.name,D,P.id,o.formatShortDate(P.createdAt)])}),console.log(b.toString());let w=c+1,C=c+u.length;console.log(`
|
|
205
|
+
Showing ${w}-${C} of ${l.total} projects`);let T=[["View project","runhuman projects show <id>"],["Filter by org","runhuman orgs projects <org-id>"]];l.hasMore&&T.push(["Next page",`runhuman projects list --offset ${c+s}`]),console.log(),o.hints(T)}}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),J(n.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("--api-key <key>","API key").option("--api-url <url>","API URL")).action(async(t,e)=>{try{let r=new g,o=await r.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=E("organization",e.organization,o,i),u=await y(s,"organization",c),l=await s.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 r.set("project",l.id,!1),e.json){let p=d.result(l);i.output(p)}else i.success("Project created successfully!"),console.log(" Project ID: "+l.id),console.log(" Name: "+l.name),e.setDefault&&console.log(" Set as default project"),console.log()}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),J(n.command("show").alias("info").alias("get").description("Show detailed project information").argument("<projectId>","Project ID to show").option("--api-key <key>","API key").option("--api-url <url>","API URL")).action(async(t,e)=>{try{let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=await xe(s,t,{},o,i),u=await s.getProject(c),[l,p]=await Promise.all([s.getOrganization(u.organizationId),s.getOrganizationBilling(u.organizationId)]);if(e.json){let m=d.result({project:u,organization:l,balance:{balance:p.balance,balanceUsd:p.balanceUsd}});i.output(m)}else i.heading("Project Details"),i.detail("Project ID",u.id),i.detail("Name",u.name),i.detail("Organization",l.name+" ("+l.id+")"),i.detail("Org Balance","$"+p.balanceUsd.toFixed(2)),u.defaultUrl&&i.detail("Default URL",u.defaultUrl),u.githubRepos?.length&&i.detail("GitHub Repos",u.githubRepos.map(m=>m.repo).join(", ")),i.detail("Created",i.formatTimestamp(u.createdAt)),i.detail("Last Updated",i.formatTimestamp(u.updatedAt)),console.log(),i.hints([["View organization","runhuman orgs show "+l.id],["List org projects","runhuman orgs projects "+l.id],["Check org balance","runhuman orgs balance "+l.id]])}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),J(n.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("--api-key <key>","API key").option("--api-url <url>","API URL")).action(async(t,e)=>{try{let r=new g,o=await r.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=await xe(s,t,{},o,i),u=await s.getProject(c);await r.set("project",c,e.global!==!1),e.json?i.output(d.result({projectId:c,projectName:u.name,scope:e.global!==!1?"global":"local",switched:!0})):(i.success(`Default project set to: ${u.name} (${c})`),e.global!==!1?console.log(" Scope: global (all directories)"):console.log(" Scope: local (current directory only)"),console.log())}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),J(n.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("--api-key <key>","API key").option("--api-url <url>","API URL")).action(async(t,e)=>{try{let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s={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},c=new h(o),u=await xe(c,t,{},o,i),l=await c.updateProject(u,s);if(e.json){let p=d.result(l);i.output(p)}else i.success("Project updated successfully!")}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),J(n.command("transfer").description("Transfer a project to another organization").argument("<projectId>","Project ID to transfer").requiredOption("--to-org <organizationId>","Target organization ID").option("--api-key <key>","API key").option("--api-url <url>","API URL")).action(async(t,e)=>{try{let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=await xe(s,t,{},o,i),u=await y(s,"organization",e.toOrg,{requireFullId:{reason:"transfer target"}}),[l,p]=await Promise.all([s.getProject(c),s.getOrganization(u)]);if(!e.json&&!e.yes){let{confirmed:b}=await Zo.prompt([{type:"confirm",name:"confirmed",message:`Transfer "${l.name}" to organization "${p.name}"? The current organization will lose access to this project.`,default:!1}]);if(!b)return}let m=await s.initiateTransfer(c,u);e.json?i.output(d.result(m)):m.type==="immediate"?(i.success(`Project transferred to ${p.name}!`),i.detail("Project",m.project.name),i.detail("New Organization",p.name),console.log(),i.hints([["View project","runhuman projects show "+c]])):(i.success("Transfer request sent (pending approval)."),i.detail("Transfer ID",m.transfer.id),i.detail("Project",l.name),i.detail("To Organization",p.name),console.log(),i.hints([["Check status","runhuman transfers list"],["Cancel transfer","runhuman transfers cancel "+m.transfer.id]]))}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),J(n.command("delete").alias("rm").description("Delete a project").argument("<projectId>","Project ID to delete").option("--api-key <key>","API key").option("--api-url <url>","API URL")).action(async(t,e)=>{try{let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=await y(s,"project",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.yes){let{confirmed:u}=await Zo.prompt([{type:"confirm",name:"confirmed",message:`Delete project ${c}?`,default:!1}]);if(!u)return}await s.deleteProject(c),e.json?i.output(d.result({id:c,deleted:!0})):i.success("Project deleted successfully!")}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}x();R();A();v();O();U();import{Command as Gs}from"commander";import sn from"cli-table3";x();R();A();v();O();U();import{Command as Pt}from"commander";import Ls from"cli-table3";import $s from"readline";function zs(n){let t=$s.createInterface({input:process.stdin,output:process.stdout});return new Promise(e=>{t.question(n,r=>{t.close(),e(r.toLowerCase()==="y")})})}function tn(){let n=new Pt("members");return n.description("List members of an organization"),n.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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=await y(s,"organization",t),[u,l]=await Promise.all([s.getOrganization(c),s.listOrganizationMembers(c)]);if(e.json){let p={total:l.total,limit:l.total,offset:0,hasMore:!1};i.output(d.result({organizationId:c,organizationName:u.name,items:l.items,memberLimit:l.memberLimit,activeMemberCount:l.activeMemberCount,suspendedMemberCount:l.suspendedMemberCount},{pagination:p}))}else{i.heading(`Members of ${u.name} (${l.activeMemberCount} active${l.suspendedMemberCount>0?`, ${l.suspendedMemberCount} suspended`:""})`);let p=new Ls({head:i.tableHead(["Name","Email","Role","Status","Joined"]),colWidths:[22,28,10,12,14]});l.items.forEach(m=>{let b=[m.firstName,m.lastName].filter(Boolean).join(" ")||"-",w=m.isOwner?`${m.role} *`:m.role;p.push([b,m.email,w,m.status,i.formatShortDate(m.joinedAt)])}),console.log(p.toString()),console.log(),l.memberLimit>0&&(console.log(` Member limit: ${l.activeMemberCount}/${l.memberLimit}`),console.log()),i.hints([["Invite member",`runhuman orgs invite ${c} user@example.com`],["Remove member",`runhuman orgs remove ${c} <user-id>`]])}}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}function on(){let n=new Pt("invite");return n.description("Invite a member to an organization"),n.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,r)=>{try{let i=await new g().loadConfig({apiKey:r.apiKey,apiUrl:r.apiUrl}),s=I(r,i),c=new h(i),u=await y(c,"organization",t),l=await c.getOrganization(u),p={email:e,role:r.role},m=await c.inviteOrganizationMember(u,p);r.json?s.output(d.result({organizationId:u,organizationName:l.name,email:e,role:r.role,invitationId:m.invitationId,message:m.message,invited:!0})):(s.success("Invitation sent"),console.log(),s.detail("Organization",l.name),s.detail("Email",e),s.detail("Role",r.role),s.detail("Invitation ID",m.invitationId),console.log(),console.log(` ${m.message}`),console.log(),s.hints([["View members",`runhuman orgs members ${u}`]]))}catch(o){let i=f(o);new d({json:r.json}).outputError(i.message,i.details),process.exit(i.exitCode)}}),n}function nn(){let n=new Pt("remove");return n.alias("delete"),n.description("Remove a member from an organization"),n.argument("<organizationId>","Organization ID").argument("<userId>","User ID to remove").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e,r)=>{try{let i=await new g().loadConfig({apiKey:r.apiKey,apiUrl:r.apiUrl}),s=I(r,i),c=new h(i),u=await y(c,"organization",t),[l,p]=await Promise.all([c.getOrganization(u),c.listOrganizationMembers(u)]),m=p.items.find(w=>w.userId===e),b=m?[m.firstName,m.lastName].filter(Boolean).join(" ")||m.email:e;if(!r.yes&&!r.json&&(console.log(` Remove ${b} from ${l.name}?`),console.log(" This will revoke their access to all projects in this organization."),console.log(),!await zs(" Continue? [y/N]: "))){console.log(" Cancelled.");return}await c.removeOrganizationMember(u,e),r.json?s.output(d.result({organizationId:u,organizationName:l.name,removedUserId:e,removedMemberName:b,removed:!0})):(s.success(`Member '${b}' removed from organization '${l.name}'`),console.log(),s.hints([["View members",`runhuman orgs members ${u}`]]))}catch(o){let i=f(o);new d({json:r.json}).outputError(i.message,i.details),process.exit(i.exitCode)}}),n}x();R();A();v();O();import{Command as Fs}from"commander";import Js from"cli-table3";import qs from"readline";U();function Hs(n){let t=qs.createInterface({input:process.stdin,output:process.stdout});return new Promise(e=>{t.question(n,r=>{t.close(),e(r.toLowerCase()==="y")})})}function rn(){let n=new Fs("invitations");return n.alias("invites"),n.description("List or revoke pending organization invitations"),n.command("list").alias("ls").description("List pending invitations for an organization").option("-o, --organization <id>","Organization ID (defaults to current org context)").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=E("organization",t.organization,r,o),c=await y(i,"organization",s),{items:u}=await i.listOrganizationInvitations(c);if(t.json){o.output(d.result({items:u}));return}if(o.heading(`Pending Invitations (${u.length})`),u.length===0){console.log(`No pending invitations.
|
|
206
|
+
`),o.hints([["Invite a member",`runhuman orgs invite ${c} user@example.com`]]);return}let l=new Js({head:o.tableHead(["Invitation ID","Email","Role","Created"]),colWidths:[30,30,14,16]});u.forEach(p=>{l.push([p.id,p.email,p.role,o.formatShortDate(p.createdAt)])}),console.log(l.toString()),console.log(),o.hints([["Revoke one","runhuman orgs invitations revoke <invitationId>"]])}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n.command("revoke").description("Revoke a pending invitation").argument("<invitationId>","Invitation ID to revoke").option("-o, --organization <id>","Organization ID (defaults to current org context)").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=E("organization",e.organization,o,i),u=await y(s,"organization",c);if(!e.yes&&!e.json&&(console.log(` Revoke invitation ${t}?`),!await Hs(" Continue? [y/N]: "))){console.log(" Cancelled.");return}if(await s.revokeOrganizationInvitation(u,t),e.json){i.output(d.result({invitationId:t,revoked:!0}));return}i.success(`Invitation ${t} revoked`),console.log()}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}x();R();A();v();O();import{Command as Ks}from"commander";import Bs from"cli-table3";U();var Vs=new Set(["7d","30d","90d","all"]);function an(){let n=new Ks("usage");return n.description("Show usage analytics for an organization").argument("[organizationId]","Organization ID (defaults to current org context)").option("--period <window>","Preset period: 7d | 30d | 90d | all").option("--year <year>","Specific year to filter by (e.g. 2026)").option("--month <month>","Specific month within --year (1-12)").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{if(e.period&&!Vs.has(e.period))throw new j(`Invalid --period "${e.period}". Valid values: 7d, 30d, 90d, all`);if(e.month&&!e.year)throw new j("--month requires --year");if(e.period&&(e.year||e.month))throw new j("--period is mutually exclusive with --year / --month");let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=E("organization",t,o,i),u=await y(s,"organization",c),l={};e.period&&(l.period=e.period),e.year&&(l.year=String(e.year)),e.month&&(l.month=String(e.month));let p=await s.getOrganizationUsage(u,l);if(e.json){i.output(d.result(p));return}let{summary:m,byProject:b}=p;i.heading("Usage Summary"),i.detail("Period",`${m.dateRange.from} \u2192 ${m.dateRange.to}`),i.detail("Total jobs",String(m.totalJobs)),i.detail("Completed / unsuccessful / active",`${m.statusBreakdown.completed} / ${m.statusBreakdown.unsuccessful} / ${m.statusBreakdown.active}`),i.detail("Total cost",`$${m.totalCostUsd.toFixed(2)}`),console.log();let w=[...b].sort((C,T)=>T.jobCount-C.jobCount).slice(0,5);if(w.length>0){i.heading(`Top Projects (${w.length})`);let C=new Bs({head:i.tableHead(["Project","Jobs","Completed","Cost"]),colWidths:[40,10,12,12]});w.forEach(T=>{C.push([T.name,String(T.jobCount),String(T.completedCount),`$${T.costUsd.toFixed(2)}`])}),console.log(C.toString()),console.log()}i.hints([["Full detail (over-time, by-hour, by-source)","runhuman orgs usage --json"]])}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}function ln(){let n=new Gs("orgs");return n.description("Manage organizations"),n.addCommand(tn()),n.addCommand(on()),n.addCommand(nn()),n.addCommand(rn()),n.addCommand(an()),n.command("list").alias("ls").description("List all organizations you belong to").option("-n, --limit <number>","Number of results (default: 20)",parseInt).option("--offset <number>","Pagination offset (default: 0)",parseInt).option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=t.limit||20,c=t.offset||0,{items:u,total:l}=await i.listOrganizations({limit:s,offset:c});if(t.json){let p={total:l,limit:s,offset:c,hasMore:c+u.length<l};o.output(d.result({items:u},{pagination:p}))}else{o.heading(`Your Organizations (${u.length} of ${l})`);let p=new sn({head:o.tableHead(["Organization ID","Name","Projects","Created"]),colWidths:[25,30,12,15]});u.forEach(m=>{p.push([m.id,m.name,m.projectCount?.toString()||"0",o.formatShortDate(m.createdAt)])}),console.log(p.toString()),console.log(),o.hints([["View org details","runhuman orgs show <id>"],["Check org balance","runhuman orgs balance <id>"]])}}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n.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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=await y(s,"organization",t),u=await s.getOrganization(c);if(e.json){let l=d.result(u);i.output(l)}else i.heading("Organization Details"),i.detail("Organization ID",u.id),i.detail("Name",u.name),i.detail("Created",i.formatTimestamp(u.createdAt)),console.log(),i.hints([["List projects","runhuman orgs projects "+u.id],["Check balance","runhuman orgs balance "+u.id]])}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=await y(s,"organization",t),[u,l]=await Promise.all([s.getOrganization(c),s.getOrganizationBilling(c)]);if(e.json){let p=d.result({balance:l});i.output(p)}else i.heading(`Organization Balance: ${u.name}`),i.detail("Current Balance","$"+l.balanceUsd.toFixed(2)),i.detail("Billing Active",l.hasBilling?"Yes":"No"),i.detail("Estimated Tests","~"+Math.floor(l.balance)+" tests at $1.00 per test"),console.log(),l.hasBilling?i.hints([["Manage billing","https://runhuman.com/dashboard/organizations/"+c+"/billing"]]):i.hints([["Set up billing","https://runhuman.com/dashboard/organizations/"+c+"/billing"]])}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.command("switch").alias("use").description("Set default organization for CLI commands").argument("<organizationId>","Organization ID to use as default").option("-g, --global","Set as global default instead of local").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let r=new g,o=await r.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=await y(s,"organization",t),u=await s.getOrganization(c);await r.set("organization",c,e.global!==!1),e.json?i.output(d.result({organizationId:c,organizationName:u.name,scope:e.global!==!1?"global":"local",switched:!0})):(i.success(`Default organization set to: ${u.name} (${c})`),e.global!==!1?console.log(" Scope: global (all directories)"):console.log(" Scope: local (current directory only)"),console.log())}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.command("projects").description("List all projects in an organization").argument("<organizationId>","Organization ID").option("-n, --limit <number>","Number of results (default: 50)",parseInt).option("--offset <number>","Pagination offset (default: 0)",parseInt).option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=await y(s,"organization",t),u=e.limit||50,l=e.offset||0,[p,{items:m,total:b}]=await Promise.all([s.getOrganization(c),s.listOrganizationProjects(c,{limit:u,offset:l})]);if(e.json){let w={total:b,limit:u,offset:l,hasMore:l+m.length<b};i.output(d.result({organizationId:c,items:m},{pagination:w}))}else{if(b===0){i.heading(`Projects in ${p.name} (0 projects)`),console.log(`No projects found in this organization.
|
|
207
|
+
`),i.hints([["Create a project","runhuman projects create <name> --organization "+c],["View organization","runhuman orgs show "+c]]);return}i.heading(`Projects in ${p.name} (${m.length} of ${b})`);let w=new sn({head:i.tableHead(["Name","Project ID","Default URL","Created"]),colWidths:[25,25,30,15]});m.forEach(C=>{let T=C.defaultUrl?C.defaultUrl.length>27?C.defaultUrl.substring(0,24)+"...":C.defaultUrl:"-";w.push([C.name,C.id,T,i.formatShortDate(C.createdAt)])}),console.log(w.toString()),console.log(),i.hints([["View project details","runhuman projects show <project-id>"],["Create new project","runhuman projects create <name> --organization "+c],["View organization","runhuman orgs show "+c]])}}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}x();R();A();v();O();import{Command as Ws}from"commander";import Ys from"inquirer";U();import Qs from"cli-table3";function un(){let n=new Ws("keys");return n.description("Manage API keys"),n.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("-n, --limit <count>","Maximum results per page",t=>parseInt(t,10)).option("--offset <count>","Skip the first N results",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 r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=E("organization",t.organization,r,o),c=await y(i,"organization",s),{items:u,pagination:l}=await i.listApiKeys(c,{limit:t.limit,offset:t.offset});if(t.json)o.output(d.result({items:u},{pagination:l}));else{if(l.total===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 '+c]]);return}o.heading(`API Keys (${u.length} of ${l.total})`);let p=new Qs({head:o.tableHead(["Key ID","Name","Key","Last Used","Created"]),colWidths:[20,25,20,20,20]});u.forEach(m=>{let b=t.showKeys?m.key:cn(m.key),w=m.lastUsedAt?o.formatShortDate(m.lastUsedAt):"Never";p.push([m.id,m.name,b,w,o.formatShortDate(m.createdAt)])}),console.log(p.toString()),console.log(),o.hints([["Create new key",'runhuman keys create "Key Name" --organization '+c],["Show full key","runhuman keys show <keyId>"]])}}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n.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("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=E("organization",e.organization,o,i),u=await y(s,"organization",c),l=await s.createApiKey(u,t);if(e.json){let p=d.result(l);i.output(p)}else i.success("API Key created successfully!"),i.detail("Key ID",l.id),i.detail("Name",l.name),console.log(),i.detail("API Key",l.key),console.log(" "+"^".repeat(l.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="+l.key),console.log(` runhuman job create https://myapp.com -d "Test"
|
|
210
210
|
`),console.log(" Store securely:"),console.log(" - Use environment variables (recommended)"),console.log(" - Use secret management tools"),console.log(` - Never commit to git!
|
|
211
|
-
`)}catch(
|
|
212
|
-
`),o.hints([["Create a template",'runhuman templates create "Template Name" --project '+
|
|
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 ${
|
|
215
|
-
Showing ${p+1}-${p+m.length} of ${
|
|
216
|
-
`),o.hints([["Create a schedule",'runhuman schedules create "My Schedule" --template <id> --project '+
|
|
217
|
-
Showing ${
|
|
218
|
-
`),
|
|
211
|
+
`)}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=await y(s,"key",t),u=await s.getApiKey(c);if(e.json){let l=d.result(u);i.output(l)}else i.heading("API Key Details"),i.detail("Key ID",u.id),i.detail("Name",u.name),i.detail("API Key",e.showKey?u.key:cn(u.key)),i.detail("Created",i.formatTimestamp(u.createdAt)),u.lastUsedAt&&i.detail("Last Used",i.formatTimestamp(u.lastUsedAt)),console.log()}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.command("delete").aliases(["rm","revoke"]).description("Delete an API key").argument("<keyId>","Key ID to delete").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=await y(s,"key",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.yes){let{confirmed:u}=await Ys.prompt([{type:"confirm",name:"confirmed",message:`Delete API key ${c}?`,default:!1}]);if(!u)return}await s.deleteApiKey(c),e.json?i.output(d.result({id:c,deleted:!0})):i.success("API key deleted successfully!")}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}function cn(n){return n.length<=12?"****":n.substring(0,8)+"*".repeat(n.length-12)+n.substring(n.length-4)}x();R();A();v();O();import{Command as el}from"commander";import bn from"inquirer";U();import tl from"cli-table3";A();var dn=["desktop","mobile"],pn=["name","duration"],mn=["asc","desc"];function fn(n,t=[]){return[...t,n]}function gn(n){if(n.device){for(let t of n.device)if(!dn.includes(t))throw new j(`Invalid --device value "${t}". Valid values: ${dn.join(", ")}`)}return{search:n.search,devices:n.device&&n.device.length>0?n.device:void 0,hasSchema:n.hasSchema,includeDefaults:n.includeDefaults}}function hn(n){return{field:Xs(n.sort),direction:Zs(n.direction)}}function Xs(n){if(!pn.includes(n))throw new j(`Invalid --sort value "${n}". Valid values: ${pn.join(", ")}`);return n}function Zs(n){if(!mn.includes(n))throw new j(`Invalid --direction value "${n}". Valid values: ${mn.join(", ")}`);return n}function Ze(n,t){if(n===void 0)return;let e=typeof n=="number"?n:Number(n);if(!Number.isInteger(e)||e<0)throw new j(`${t} must be a non-negative integer (got "${n}")`);return e}async function ol(n,t,e){if(t.force)return!0;(!process.stdin.isTTY||t.json)&&(e.outputError(`A template named "${n}" already exists. Use --force to overwrite it.`),process.exit(7));let{overwrite:r}=await bn.prompt([{type:"confirm",name:"overwrite",message:`A template named "${n}" already exists. Overwrite it?`,default:!1}]);return r}function yn(){let n=new el("templates");return n.description("Manage test templates"),n.command("list").alias("ls").description("List all test templates").option("-p, --project <id>","Filter by project (required)").option("--search <text>","Filter by name or description (case-insensitive)").option("--device <class>","Filter by device class (desktop|mobile). Repeatable for union.",fn).option("--has-schema","Only show templates that define an output schema").option("--include-defaults","Include unmodified system default templates (hidden by default)").option("--sort <field>","Sort by field (name|duration)","name").option("--direction <dir>","Sort direction (asc|desc)","asc").option("-n, --limit <count>","Maximum results per page",t=>parseInt(t,10)).option("--offset <count>","Skip the first N results",t=>parseInt(t,10)).option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=E("project",t.project,r,o),c=await y(i,"project",s),u=gn(t),l=hn(t),p={limit:t.limit,offset:t.offset,search:u.search,deviceClass:u.devices,hasSchema:u.hasSchema,includeDefaults:u.includeDefaults,sort:l.field,direction:l.direction},{items:m,pagination:b}=await i.listTemplates(c,p);if(t.json)o.output(d.result({items:m},{pagination:b}));else{if(b.total===0){o.heading("Test Templates (0)"),console.log(`No templates match the active filters.
|
|
212
|
+
`),o.hints([["Create a template",'runhuman templates create "Template Name" --project '+c]]);return}o.heading(`Test Templates (${m.length} of ${b.total})`);let w=new tl({head:o.tableHead(["ID","Name","Description","Created"]),colWidths:[30,25,35,20]});m.forEach(C=>{let T=C.testDescription&&C.testDescription.length>30?C.testDescription.substring(0,27)+"...":C.testDescription||"-";w.push([C.id,C.name,T,o.formatShortDate(C.createdAt)])}),console.log(w.toString()),console.log(),o.hints([["Create new template",'runhuman templates create "Template Name" --project '+c],["View template","runhuman templates show <templateId>"]])}}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n.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/both)").option("--schema <path>","Path to JSON schema file").option("--schema-inline <json>","Inline JSON schema").option("--max-extension-count <n>","Number of duration extensions allowed mid-job (0 disables; API default 3)",parseInt).option("--max-extension-minutes <m>","Total extension time in minutes across all extensions (0 disables; API default 15)",parseInt).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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=E("project",e.project,o,i),u=await y(s,"project",c),l;if(e.schema){let{readFileSync:C}=await import("fs"),T=C(e.schema,"utf-8");l=JSON.parse(T)}else e.schemaInline&&(l=JSON.parse(e.schemaInline));let p=Ze(e.maxExtensionCount,"--max-extension-count"),m=Ze(e.maxExtensionMinutes,"--max-extension-minutes"),b={name:t,testDescription:e.description,targetDurationMinutes:e.duration,deviceClass:e.deviceClass,outputSchema:l,...p!==void 0&&{maxExtensionCount:p},...m!==void 0&&{maxExtensionMinutes:m}},w;try{w=await s.createTemplate(u,b)}catch(C){if(!(C instanceof pe))throw C;if(!await ol(t,e,i)){console.log("Cancelled.");return}let D=(await s.listTemplates(u,{search:t,includeDefaults:!0,limit:100})).items.find(M=>M.name.toLowerCase()===t.toLowerCase());if(!D)throw new Error(`Template "${t}" not found despite 409 conflict`);w=await s.updateTemplate(u,D.id,b),e.json?i.output(d.result(w)):(i.success("Template overwritten successfully!"),i.detail("Template ID",w.id),i.detail("Name",w.name));return}e.json?i.output(d.result(w)):(i.success("Template created successfully!"),i.detail("Template ID",w.id),i.detail("Name",w.name),w.testDescription&&i.detail("Description",w.testDescription),console.log(),i.hints([["Use this template",'runhuman job create https://myapp.com --template "'+w.name+'"']]))}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=E("project",e.project,o,i),u=await y(s,"project",c),l=await y(s,"template",t),p=await s.getTemplate(u,l);if(e.json){let m=d.result(p);i.output(m)}else i.heading("Template Details"),i.detail("Template ID",p.id),i.detail("Name",p.name),i.detail("Description",p.testDescription||"-"),i.detail("Project",p.projectId),p.targetDurationMinutes&&i.detail("Duration",p.targetDurationMinutes+" minutes"),p.deviceClass&&i.detail("Device Class",p.deviceClass),i.detail("Max extensions",p.maxExtensionCount===void 0?"(API default: 3)":String(p.maxExtensionCount)),i.detail("Total extension time",p.maxExtensionMinutes===void 0?"(API default: 15 minutes)":`${p.maxExtensionMinutes} minutes`),i.detail("Created",i.formatTimestamp(p.createdAt)),p.outputSchema&&(console.log(`
|
|
213
|
+
Output Schema:`),console.log(JSON.stringify(p.outputSchema,null,2))),console.log(),i.hints([["Use this template",'runhuman job create https://myapp.com --template "'+p.name+'"']])}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.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/both)").option("--schema <path>","Path to new JSON schema file").option("--schema-inline <json>","Inline JSON schema").option("--max-extension-count <n>","Number of duration extensions allowed mid-job (0 disables; API default 3)",parseInt).option("--max-extension-minutes <m>","Total extension time in minutes across all extensions (0 disables; API default 15)",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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=E("project",e.project,o,i),u=await y(s,"project",c),l=await y(s,"template",t),p;if(e.schema){let{readFileSync:C}=await import("fs"),T=C(e.schema,"utf-8");p=JSON.parse(T)}else e.schemaInline&&(p=JSON.parse(e.schemaInline));let m={name:e.name,testDescription:e.description,targetDurationMinutes:e.duration,deviceClass:e.deviceClass,outputSchema:p,maxExtensionCount:Ze(e.maxExtensionCount,"--max-extension-count"),maxExtensionMinutes:Ze(e.maxExtensionMinutes,"--max-extension-minutes")},b=Object.fromEntries(Object.entries(m).filter(([,C])=>C!==void 0));if(Object.keys(b).length===0)throw new Error("No updates provided. Use --name, --description, --duration, --device-class, --schema, --max-extension-count, or --max-extension-minutes");let w=await s.updateTemplate(u,l,m);if(e.json){let C=d.result(w);i.output(C)}else i.success("Template updated successfully!"),i.detail("Template ID",w.id),i.detail("Name",w.name),console.log()}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.command("delete").alias("rm").description("Delete a template").argument("<templateId>","Template ID to delete").option("-p, --project <id>","Project ID (required)").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=E("project",e.project,o,i),u=await y(s,"project",c),l=await y(s,"template",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.yes){let{confirmed:p}=await bn.prompt([{type:"confirm",name:"confirmed",message:`Delete template ${l}?`,default:!1}]);if(!p)return}await s.deleteTemplate(u,l),e.json?i.output(d.result({id:l,deleted:!0})):i.success("Template deleted successfully!")}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}x();R();A();v();O();import{Command as fl}from"commander";U();function kt(n,t){return`${String(n).padStart(2,"0")}:${String(t).padStart(2,"0")}`}function et(n){let t=n.match(/^(\d{1,2}):(\d{2})$/);if(!t)throw new Error(`Invalid time format "${n}". Use HH:MM (e.g., 09:00, 14:30).`);let e=parseInt(t[1],10),r=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(r))throw new Error(`Minute must be 0, 15, 30, or 45, got ${r}.`);return{hour:e,minute:r}}function tt(n){let t={sun:0,mon:1,tue:2,wed:3,thu:4,fri:5,sat:6};return n.split(",").map(r=>r.trim().toLowerCase()).map(r=>{let o=t[r];if(o===void 0)throw new Error(`Unknown weekday "${r}". Use: sun, mon, tue, wed, thu, fri, sat.`);return o})}function nl(n){return n.length===0?"-":n.map(t=>Ve[t]).join(", ")}function Ut(n,t,e,r){let o=Be[n];switch(n){case"weekly":case"biweekly":return t.length>0?`${o} on ${nl(t)}`:o;case"monthly":return e===0?`${o} on last day`:e?`${o} on day ${e}`:o;case"once":return r?`${o} on ${r}`:o;default:return o}}function he(n,t){n.detail("Schedule ID",t.id),n.detail("Name",t.name),t.templateName?n.detail("Template",t.templateName):n.detail("Template ID",t.templateId),n.detail("Status",t.status),n.detail("Frequency",Ut(t.frequency,t.weekdays,t.dayOfMonth,t.scheduledDate)),n.detail("Time",`${kt(t.scheduledHour,t.scheduledMinute)} ${t.timezone}`),n.detail("Next Run",n.formatTimestamp(t.nextExecutionAt)),t.lastExecutedAt&&n.detail("Last Run",n.formatTimestamp(t.lastExecutedAt)),t.urlOverride&&n.detail("URL Override",t.urlOverride),n.detail("Created",n.formatTimestamp(t.createdAt))}x();R();A();v();O();import{Command as rl}from"commander";U();function In(){return new rl("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("--auto-only-tester-surfaced","When auto-creating issues, only file findings the tester explicitly surfaced (skip telemetry-only)").option("--reopen-on-duplicate","When a duplicate match resolves to a closed issue, reopen it (default: leave closed duplicates closed)").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(n,t)=>{try{let r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=E("project",t.project,r,o),c=await y(i,"project",s),u=await y(i,"template",t.template),l=il(t.frequency),{hour:p,minute:m}=t.time?et(t.time):{hour:9,minute:0},b=t.timezone||"America/New_York",w={name:n,templateId:u,frequency:l,scheduledHour:p,scheduledMinute:m,timezone:b};t.weekdays&&(w.weekdays=tt(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),t.autoOnlyTesterSurfaced&&(w.autoCreateOnlyTesterSurfacedOverride=!0),t.reopenOnDuplicate&&(w.reopenIssuesOnDuplicateOverride=!0),al(w);let C=await i.createSchedule(c,w);t.json?o.output(d.result(C)):(o.success("Schedule created successfully!"),he(o,C),console.log(),o.hints([["View schedule",`runhuman schedules show ${C.id}`],["List schedules",`runhuman schedules list --project ${c}`]]))}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}})}function il(n){if(!n)return"weekly";if(!oe.includes(n))throw new j(`Invalid frequency "${n}". Must be one of: ${oe.join(", ")}`);return n}function al(n){if(n.frequency==="monthly"&&n.dayOfMonth===void 0)throw new j("Monthly schedules require --day-of-month (1-28, or 0 for last day).");if(n.frequency==="once"&&!n.scheduledDate)throw new j("One-time schedules require --date (YYYY-MM-DD).");if((n.frequency==="weekly"||n.frequency==="biweekly")&&(!n.weekdays||n.weekdays.length===0))throw new j(`${n.frequency} schedules require --weekdays (e.g., mon,wed,fri).`)}x();R();A();v();O();import{Command as sl}from"commander";U();function wn(){return new sl("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(n,t)=>{try{let r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=E("project",t.project,r,o),c=await y(i,"project",s),u=await y(i,"schedule",n),l=await i.getSchedule(c,u);t.json?o.output(d.result(l)):(o.heading("Schedule Details"),he(o,l),console.log(),o.hints([["View history",`runhuman schedules history ${l.id}`],["Pause schedule",`runhuman schedules pause ${l.id}`],["Update schedule",`runhuman schedules update ${l.id} --frequency daily`]]))}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}})}x();R();A();v();O();import{Command as ll}from"commander";U();function Cn(){return new ll("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("--auto-only-tester-surfaced <bool>","Tester-surfaced-only override (true/false). Only applies when issue creation is on.").option("--reopen-on-duplicate <bool>","Reopen-on-duplicate override (true/false). Only applies when issue creation is on. Defaults to leaving closed duplicates closed.").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(n,t)=>{try{let r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=E("project",t.project,r,o),c=await y(i,"project",s),u=await y(i,"schedule",n),l=cl(t);if(l.templateId&&(l.templateId=await y(i,"template",l.templateId)),Object.keys(l).length===0)throw new j("No updates provided. Use --name, --template, --frequency, --time, --timezone, --weekdays, --day-of-month, --date, --url, --status, --auto-github-issues, --auto-only-tester-surfaced, or --reopen-on-duplicate.");let p=await i.updateSchedule(c,u,l);t.json?o.output(d.result(p)):(o.success("Schedule updated successfully!"),he(o,p),console.log())}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}})}function cl(n){let t={};if(n.name&&(t.name=n.name),n.template&&(t.templateId=n.template),n.frequency){let e=n.frequency;if(!oe.includes(e))throw new j(`Invalid frequency "${e}". Must be one of: ${oe.join(", ")}`);t.frequency=e}if(n.time){let{hour:e,minute:r}=et(n.time);t.scheduledHour=e,t.scheduledMinute=r}if(n.timezone&&(t.timezone=n.timezone),n.weekdays&&(t.weekdays=tt(n.weekdays)),n.dayOfMonth!==void 0&&(t.dayOfMonth=n.dayOfMonth),n.date&&(t.scheduledDate=n.date),n.url!==void 0&&(t.urlOverride=n.url||null),n.autoGithubIssues!==void 0&&(t.autoCreateGithubIssuesOverride=n.autoGithubIssues==="true"),n.autoOnlyTesterSurfaced!==void 0&&(t.autoCreateOnlyTesterSurfacedOverride=n.autoOnlyTesterSurfaced==="true"),n.reopenOnDuplicate!==void 0&&(t.reopenIssuesOnDuplicateOverride=n.reopenOnDuplicate==="true"),n.status){let e=n.status;if(e!=="active"&&e!=="paused")throw new j('Status must be "active" or "paused".');t.status=e}return t}x();R();A();v();O();import{Command as ul}from"commander";import dl from"inquirer";U();function Sn(){return new ul("delete").alias("rm").description("Delete a schedule").argument("<scheduleId>","Schedule ID to delete").option("-p, --project <id>","Project ID (required)").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(n,t)=>{try{let r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=E("project",t.project,r,o),c=await y(i,"project",s),u=await y(i,"schedule",n,{requireFullId:{reason:"deletion"}});if(!t.json&&!t.yes){let{confirmed:l}=await dl.prompt([{type:"confirm",name:"confirmed",message:`Delete schedule ${u}?`,default:!1}]);if(!l)return}await i.deleteSchedule(c,u),t.json?o.output(d.result({id:u,deleted:!0})):o.success("Schedule deleted successfully!")}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}})}x();R();A();v();O();import{Command as pl}from"commander";U();import ml from"cli-table3";function jn(){return new pl("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(n,t)=>{try{let r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=E("project",t.project,r,o),c=await y(i,"project",s),u=await y(i,"schedule",n),l=t.limit||20,p=t.offset||0,{items:m,pagination:b}=await i.listScheduleExecutions(c,u,{limit:l,offset:p});if(t.json){o.output(d.result({items:m},{pagination:b}));return}if(b.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 ${b.total})`);let w=new ml({head:o.tableHead(["Execution ID","Status","Job ID","Scheduled For","Executed At"]),colWidths:[12,28,12,22,22]});for(let C of m)w.push([C.id.substring(0,8)+"\u2026",o.formatStatus(C.status),C.jobId?C.jobId.substring(0,8)+"\u2026":"-",o.formatTimestamp(C.scheduledAt),o.formatTimestamp(C.executedAt)]);console.log(w.toString()),b.hasMore?(console.log(`
|
|
215
|
+
Showing ${p+1}-${p+m.length} of ${b.total}`),o.hints([["Next page",`runhuman schedules history ${u} --offset ${p+l}`]])):console.log()}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}})}import gl from"cli-table3";function An(){let n=new fl("schedules");return n.description("Manage recurring test schedules"),n.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 r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=E("project",t.project,r,o),c=await y(i,"project",s),u=t.limit||20,l=t.offset||0,{items:p,pagination:m}=await i.listSchedules(c,{limit:u,offset:l});if(t.json){o.output(d.result({items: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 '+c]]);return}o.heading(`Schedules (${p.length} of ${m.total})`);let b=new gl({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 C=Ut(w.frequency,w.weekdays,w.dayOfMonth,w.scheduledDate);b.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,C.length>20?C.substring(0,17)+"\u2026":C,kt(w.scheduledHour,w.scheduledMinute),o.formatStatus(w.status),o.formatShortDate(w.nextExecutionAt)])}console.log(b.toString()),m.hasMore?(console.log(`
|
|
217
|
+
Showing ${l+1}-${l+p.length} of ${m.total}`),o.hints([["Next page",`runhuman schedules list --offset ${l+u} --project ${c}`],["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 '+c]]))}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n.addCommand(In()),n.addCommand(wn()),n.addCommand(Cn()),n.addCommand(Sn()),n.addCommand(jn()),n.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 En(t,"paused",e)}),n.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 En(t,"active",e)}),n}async function En(n,t,e){try{let o=await new g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=E("project",e.project,o,i),u=await y(s,"project",c),l=await y(s,"schedule",n),p=await s.updateSchedule(u,l,{status:t});if(e.json)i.output(d.result(p));else{let m=t==="paused"?"paused":"resumed";i.success(`Schedule ${m} successfully!`),i.detail("Schedule ID",p.id),i.detail("Name",p.name),i.detail("Status",p.status),console.log()}}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}x();R();A();v();O();import{Command as bl}from"commander";U();import vn from"cli-table3";x();R();A();v();O();import{Command as hl}from"commander";U();function Tn(){let n=new hl("installation");return n.description("Manage GitHub App installations for an organization"),n.command("refresh").description("Force-refresh an installation's repo / permissions list").requiredOption("--installation <id>","GitHub installation ID").option("-o, --organization <id>","Organization ID (defaults to current org context)").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=E("organization",t.organization,r,o),c=await y(i,"organization",s);if(await i.refreshGithubInstallation(c,String(t.installation)),t.json){o.output(d.result({organizationId:c,installationId:String(t.installation),refreshed:!0}));return}o.success(`Installation ${t.installation} refreshed for org ${c}`),console.log()}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n}function Rn(){let n=new bl("github");return n.alias("gh"),n.description("GitHub integration commands"),n.addCommand(Tn()),n.command("link").description("Link a GitHub repository to your project").argument("<repo>","Repository in format owner/repo").option("-p, --project <id>","Project ID (required)").option("-j, --json","Output as JSON").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let r=new g,o=await r.loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=E("project",e.project,o,i),u=await y(s,"project",c);if(!t.match(/^([^/]+)\/([^/]+)$/))throw new Error("Invalid repository format. Use: owner/repo");await s.updateProject(u,{addGithubRepos:[t]}),e.json?i.output(d.result({repository:t,linked:!0,projectId:u})):(i.success("GitHub repository linked successfully!"),i.detail("Repository",t),i.detail("Project",u),console.log(),await r.saveProjectConfig({githubRepos:[t]}),console.log(` Repository saved to project config (.runhumanrc)
|
|
218
|
+
`),i.hints([["List issues","runhuman github issues "+t],["Test an issue","runhuman github test <issueNumber> --repo "+t],["Bulk test","runhuman github bulk-test --repo "+t]]))}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.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 r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=E("organization",t.organization,r,o),c=await y(i,"organization",s),u=await i.listGithubRepos(c,{search:t.search});if(t.json)o.output(d.result(u));else{o.heading(`GitHub Repositories (${u.items.length})`);let l=new vn({head:o.tableHead(["Repository","Added"]),colWidths:[45,20]});u.items.forEach(p=>{l.push([p.fullName,o.formatShortDate(p.createdAt)])}),console.log(l.toString()),console.log(),o.hints([["Test an issue","runhuman github test <issueNumber> --repo owner/repo"]])}}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n.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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=t.match(/^([^/]+)\/([^/]+)$/);if(!s)throw new Error("Invalid repository format. Use: owner/repo");let[,c,u]=s,p=await new h(o).listGithubIssues(c,u,{state:e.state,labels:e.labels?.split(",")});if(e.json)i.output(d.result({items:p}));else{i.heading(`GitHub Issues for ${t} (${p.length})`);let m=new vn({head:i.tableHead(["#","Title","State","Labels","Created"]),colWidths:[8,40,10,20,15]});p.forEach(b=>{let w=b.labels?.join(", ")||"-",C=w.length>18?w.substring(0,15)+"...":w;m.push(["#"+b.number,b.title.length>38?b.title.substring(0,35)+"...":b.title,b.state,C,i.formatShortDate(b.createdAt)])}),console.log(m.toString()),console.log(),i.hints([["Test an issue","runhuman github test <issueNumber> --repo "+t]])}}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=e.repo||o.githubRepos?.[0];if(!i)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 s=I(e,o),c=i.match(/^([^/]+)\/([^/]+)$/);if(!c)throw new Error("Invalid repository format. Use: owner/repo");let[,u,l]=c,p=new h(o),m=await p.getGithubIssue(u,l,parseInt(t)),b=E("project",void 0,o,s),C={projectId:await y(p,"project",b),url:e.url,description:`Test GitHub issue #${t}: ${m.title}
|
|
219
219
|
|
|
220
|
-
${m.body}`,metadata:{githubIssue:{repo:`${u}/${
|
|
221
|
-
`);return}let w=
|
|
222
|
-
Creating ${
|
|
223
|
-
`);let
|
|
220
|
+
${m.body}`,metadata:{githubIssue:{repo:`${u}/${l}`,issueNumber:parseInt(t),issueUrl:m.url}},template:e.template},T=await p.createJob(C);if(e.json){let P=d.result(T);s.output(P)}else if(s.success("QA test job created for issue #"+t),s.detail("Job ID",T.jobId),s.detail("Issue","#"+t+" - "+m.title),s.detail("URL",e.url),console.log(),s.hints([["Check status","runhuman job status "+T.jobId]]),e.sync){let{waitForJob:P}=await Promise.resolve().then(()=>(We(),jt));await P(T.jobId,p,s,600)}}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.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 r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=t.repo||r.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 i=I(t,r),s=o.match(/^([^/]+)\/([^/]+)$/);if(!s)throw new Error("Invalid repository format. Use: owner/repo");let[,c,u]=s,l=new h(r),p=await l.listGithubIssues(c,u,{state:t.state,labels:t.labels?.split(",")}),m=parseInt(t.limit),b=p.slice(0,m);if(b.length===0){console.log(`No issues found matching the criteria.
|
|
221
|
+
`);return}let w=E("project",void 0,r,i),C=await y(l,"project",w);console.log(`
|
|
222
|
+
Creating ${b.length} test jobs...
|
|
223
|
+
`);let T=[];for(let P of b){let D={projectId:C,url:t.url,description:`Test GitHub issue #${P.number}: ${P.title}
|
|
224
224
|
|
|
225
|
-
${
|
|
226
|
-
Created ${
|
|
227
|
-
`);else{let
|
|
228
|
-
`);else{let
|
|
225
|
+
${P.body}`,metadata:{githubIssue:{repo:`${c}/${u}`,issueNumber:P.number,issueUrl:P.url}},template:t.template};try{let M=await l.createJob(D);T.push({issue:P.number,jobId:M.jobId,status:"created"}),console.log(` ok Issue #${P.number} \u2192 Job ${M.jobId}`)}catch(M){let G=f(M);T.push({issue:P.number,error:G.message,status:"failed"}),console.log(` FAIL Issue #${P.number} \u2192 Failed: ${G.message}`)}}if(t.json)i.output(d.result({items:T}));else{let P=T.filter(M=>M.status==="created").length,D=T.filter(M=>M.status==="failed").length;console.log(`
|
|
226
|
+
Created ${P} jobs`),D>0&&console.log(`Failed ${D} jobs`),console.log(),i.hints([["Check all jobs","runhuman job list"]])}}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n}x();R();A();v();O();U();import{Command as yl}from"commander";import xn from"cli-table3";function On(){let n=new yl("transfers");return n.description("Manage project transfers between organizations"),n.command("list").alias("ls").description("List pending incoming and outgoing transfers").option("-n, --limit <count>","Maximum results per page (applies to each list)",t=>parseInt(t,10)).option("--offset <count>","Skip the first N results (applies to each list)",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 r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s={limit:t.limit,offset:t.offset},[c,u]=await Promise.all([i.listPendingTransfers(s),i.listOutgoingTransfers(s)]);if(t.json){let l=d.result({incoming:{items:c.items,pagination:c.pagination},outgoing:{items:u.items,pagination:u.pagination}});o.output(l);return}if(o.heading(`Incoming Transfers (${c.items.length} of ${c.pagination.total})`),c.items.length===0)console.log(`No pending incoming transfers.
|
|
227
|
+
`);else{let l=new xn({head:o.tableHead(["Transfer ID","Project","From","To Org","Created"]),colWidths:[20,25,25,25,15]});c.items.forEach(p=>{l.push([p.id.substring(0,18),p.projectName,p.initiatedByEmail??"Unknown",p.toOrganizationName??"-",o.formatShortDate(p.createdAt)])}),console.log(l.toString()),console.log()}if(o.heading(`Outgoing Transfers (${u.items.length} of ${u.pagination.total})`),u.items.length===0)console.log(`No pending outgoing transfers.
|
|
228
|
+
`);else{let l=new xn({head:o.tableHead(["Transfer ID","Project","To Org","Status","Created"]),colWidths:[20,25,25,15,15]});u.items.forEach(p=>{l.push([p.id.substring(0,18),p.projectName,p.toOrganizationName??"-",p.status,o.formatShortDate(p.createdAt)])}),console.log(l.toString()),console.log()}o.hints([["Accept transfer","runhuman transfers accept <transferId>"],["Reject transfer","runhuman transfers reject <transferId>"],["Cancel transfer","runhuman transfers cancel <transferId>"]])}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n.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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=await y(s,"transfer",t);await s.acceptTransfer(c),e.json?i.output(d.result({id:c,accepted:!0})):(i.success("Transfer accepted!"),i.detail("Transfer ID",c),console.log())}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=await y(s,"transfer",t);await s.rejectTransfer(c),e.json?i.output(d.result({id:c,rejected:!0})):(i.success("Transfer rejected."),i.detail("Transfer ID",c),console.log())}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.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 g().loadConfig({apiKey:e.apiKey,apiUrl:e.apiUrl}),i=I(e,o),s=new h(o),c=await y(s,"transfer",t);await s.cancelTransfer(c),e.json?i.output(d.result({id:c,cancelled:!0})):(i.success("Transfer cancelled."),i.detail("Transfer ID",c),console.log())}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}x();R();A();v();O();U();import{Command as Il}from"commander";import wl from"inquirer";import{readFileSync as Pn}from"fs";import Cl from"cli-table3";var Ue=["credentials","environment","setup","procedures","known-issues","contacts","general"];function _t(n){if(!n)return;let t=n.split(",").map(r=>r.trim()).filter(Boolean),e=t.filter(r=>!Ue.includes(r));if(e.length>0)throw new Error(`Unknown tag(s): ${e.join(", ")}. Allowed: ${Ue.join(", ")}`);return t}function kn(n,t){if(n!==void 0&&t!==void 0)throw new Error("Provide either --body or --body-file, not both");if(n!==void 0)return n;if(t!==void 0)return t==="-"?Pn(0,"utf-8"):Pn(t,"utf-8")}async function ke(n){let e=await new g().loadConfig({apiKey:n.apiKey,apiUrl:n.apiUrl}),r=I(n,e);return{apiClient:new h(e),output:r,config:e}}async function Un(n){if(!(await n.getCurrentUser()).isAdmin)throw new Error("Only Volter staff can use --internal. Omit the flag to create a note visible to your org and its testers.")}function Sl(n,t){n.heading("Tester Note"),n.detail("ID",t.id),n.detail("Title",t.title),n.detail("Organization",`${t.organizationName} (${t.organizationId})`),t.projectId?n.detail("Project",`${t.projectName??"-"} (${t.projectId})`):n.detail("Project","Org-wide (no project)"),n.detail("Tags",t.tags.length?t.tags.join(", "):"-"),n.detail("Visibility",t.isInternal?"internal":"customer-visible"),n.detail("Created",`${n.formatTimestamp(t.createdAt)} by ${t.createdByEmail}`),n.detail("Edited",`${n.formatTimestamp(t.editedAt)} by ${t.editedByEmail}`),console.log(`
|
|
229
229
|
`+t.body+`
|
|
230
|
-
`)}function
|
|
231
|
-
`);return}
|
|
232
|
-
Run `+
|
|
233
|
-
`);return}await
|
|
234
|
-
`),e.isRunhumanProject?await
|
|
235
|
-
Run `+
|
|
230
|
+
`)}function _n(){let n=new Il("notes");return n.description("Manage tester knowledge-base notes (paid feature \u2014 requires an active organization subscription)"),n.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 (${Ue.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("-n, --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:r}=await ke(t),o=_t(t.tags),i=t.organization?await y(e,"organization",t.organization):void 0,s=t.project?await y(e,"project",t.project):void 0,c=await e.listTesterNotes({search:t.search,tags:o,organizationId:i,projectId:s,limit:t.limit,offset:t.offset});if(t.json){let l=t.limit===void 0?c.items.length:t.limit,p=t.offset===void 0?0:t.offset,m={total:c.total,limit:l,offset:p,hasMore:p+c.items.length<c.total};r.output(d.result({items:c.items},{pagination:m}));return}if(c.total===0){r.heading("Tester Notes (0)"),console.log(`No notes matched your filters.
|
|
231
|
+
`);return}r.heading(`Tester Notes (${c.items.length} of ${c.total})`);let u=new Cl({head:r.tableHead(["ID","Title","Org","Project","Tags","Edited"]),colWidths:[14,28,20,18,22,14]});for(let l of c.items)u.push([l.id.slice(0,8),l.title.length>26?l.title.slice(0,23)+"...":l.title,l.organizationName,l.projectName??"-",l.tags.join(", ")||"-",r.formatShortDate(l.editedAt)]);console.log(u.toString()),console.log(),r.hints([["Show a note","runhuman notes show <noteId>"],["Create a note",'runhuman notes create "Title" --organization <orgId>']])}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n.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:r,output:o}=await ke(e),i=await y(r,"note",t),s=await r.getTesterNote(i);e.json?o.output(d.result(s)):Sl(o,s)}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.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 (${Ue.join(", ")})`).option("--internal","Mark internal-only (Volter staff only; default is org/tester-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:r,output:o,config:i}=await ke(e),s=kn(e.body,e.bodyFile);if(s===void 0)throw new Error("Provide a body via --body or --body-file");e.internal&&await Un(r);let c=e.project?await y(r,"project",e.project):void 0,u;if(e.organization)u=await y(r,"organization",e.organization);else if(c)u=(await r.getProject(c)).organizationId;else{let m=E("organization",void 0,i,o);u=await y(r,"organization",m)}let l={title:t,body:s,organizationId:u,projectId:c??null,tags:_t(e.tags),isInternal:!!e.internal},p=await r.createTesterNote(l);e.json?o.output(d.result(p)):(o.success("Note created"),o.detail("ID",p.id),o.detail("Title",p.title))}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.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 (${Ue.join(", ")})`).option("--internal","Mark internal-only (Volter staff only)").option("--no-internal","Mark org/tester-visible (drops the internal flag)").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:r,output:o}=await ke(e);e.internal===!0&&await Un(r);let i=await y(r,"note",t),s=kn(e.body,e.bodyFile),c={};if(e.title!==void 0&&(c.title=e.title),s!==void 0&&(c.body=s),e.organization!==void 0&&(c.organizationId=await y(r,"organization",e.organization)),e.project!==void 0&&(c.projectId=e.project===""?null:await y(r,"project",e.project)),e.tags!==void 0&&(c.tags=_t(e.tags)),e.internal===!0?c.isInternal=!0:e.internal===!1&&(c.isInternal=!1),Object.keys(c).length===0)throw new Error("Nothing to update. Pass at least one of --title, --body, --body-file, --organization, --project, --tags, --internal, --no-internal.");let u=await r.updateTesterNote(i,c);e.json?o.output(d.result(u)):(o.success("Note updated"),o.detail("ID",u.id),o.detail("Title",u.title))}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n.command("delete").alias("rm").description("Delete a note").argument("<noteId>","Note id (full ID required)").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async(t,e)=>{try{let{apiClient:r,output:o}=await ke(e),i=await y(r,"note",t,{requireFullId:{reason:"deletion"}});if(!e.json&&!e.yes){let{confirmed:s}=await wl.prompt([{type:"confirm",name:"confirmed",message:`Delete note ${i}?`,default:!1}]);if(!s)return}await r.archiveTesterNote(i),e.json?o.output(d.result({id:i,deleted:!0})):o.success("Note deleted")}catch(r){let o=f(r);new d({json:e.json}).outputError(o.message,o.details),process.exit(o.exitCode)}}),n}x();R();A();v();O();U();import{Command as jl}from"commander";import El from"open";function Dn(){let n=new jl("billing");return n.description("Billing reads: balance, subscription, customer portal"),n.command("balance").description("Show credit balance for the authenticated user/org").option("-o, --organization <id>","Organization override (defaults to user's primary org)").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=await Dt(i,t.organization,r.organization),c=await i.getBillingBalance(s?{organizationId:s}:void 0);if(t.json){o.output(d.result(c));return}o.heading("Billing Balance"),o.detail("Organization ID",c.organizationId),o.detail("Balance",`$${c.balance.toFixed(2)}`),o.detail("Has credits",c.hasCredits?"yes":"no"),o.detail("Active subscription",c.hasActiveSubscription?"yes":"no"),console.log()}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n.command("subscription").description("Show active subscription (tier, period, add-ons)").option("-o, --organization <id>","Organization override (defaults to user's primary org)").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=await Dt(i,t.organization,r.organization),c=await i.getBillingSubscription(s?{organizationId:s}:void 0);if(t.json){o.output(d.result(c));return}if(o.heading("Subscription"),o.detail("Organization ID",c.organizationId),!c.subscription){o.detail("Plan","none (no active subscription)"),console.log(),o.hints([["Manage billing","runhuman billing portal --open"]]);return}let u=c.subscription;o.detail("Tier",u.tier),o.detail("Status",u.status),o.detail("Cycle",`${u.billingCycle} (${u.recurringInterval})`),o.detail("Amount",`${(u.amount/100).toFixed(2)} ${u.currency.toUpperCase()}`),o.detail("Period ends",u.currentPeriodEnd),u.cancelAtPeriodEnd&&o.detail("Cancels at period end","yes"),c.addons.length>0&&(console.log(),o.heading(`Add-ons (${c.addons.length})`),c.addons.forEach(l=>{o.detail(l.addon,`${l.status}, ends ${l.currentPeriodEnd}`)})),console.log()}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n.command("portal").description("Print (or open) the Polar customer-portal URL for billing self-service").option("-o, --organization <id>","Organization override (defaults to user's primary org)").option("--open","Open the portal URL in the browser instead of just printing it").option("--return-url <url>","URL to return to after the portal session ends").option("--api-key <key>","API key").option("--api-url <url>","API URL").action(async t=>{try{let r=await new g().loadConfig({apiKey:t.apiKey,apiUrl:t.apiUrl}),o=I(t,r),i=new h(r),s=await Dt(i,t.organization,r.organization),c={};s&&(c.organizationId=s),t.returnUrl&&(c.returnUrl=t.returnUrl);let u=await i.getBillingPortal(Object.keys(c).length>0?c:void 0);if(t.json){o.output(d.result(u));return}o.heading("Customer Portal"),o.detail("URL",u.portalUrl),o.detail("Expires",u.expiresAt),console.log(),t.open?(await El(u.portalUrl),o.success("Opened in browser")):o.hints([["Open in browser","runhuman billing portal --open"]])}catch(e){let r=f(e);new d({json:t.json}).outputError(r.message,r.details),process.exit(r.exitCode)}}),n}async function Dt(n,t,e){let r=t??e;if(r)return y(n,"organization",r)}R();x();import{existsSync as Al}from"fs";import{join as Tl}from"path";import{execSync as vl}from"child_process";import be from"inquirer";import F from"chalk";import Mn from"ora";v();Re();async function Ln(){let n=new g,t=new d({color:!0});console.log(""),console.log(F.bold("Runhuman")+" - Human QA testing for your apps"),console.log("");let e=await Nn(n);if(!e.isAuthenticated){let{shouldLogin:r}=await be.prompt([{type:"confirm",name:"shouldLogin",message:"You're not logged in. Would you like to sign in?",default:!0}]);if(!r){console.log(`
|
|
232
|
+
Run `+F.cyan("runhuman login")+` when you're ready to sign in.
|
|
233
|
+
`);return}await Rl(n,t),Object.assign(e,await Nn(n))}console.log(F.green("Logged in as "+e.userEmail)+`
|
|
234
|
+
`),e.isRunhumanProject?await xl(n,e):await Ol(n,e)}async function Nn(n){let t={isAuthenticated:!1,isGitRepo:!1,isRunhumanProject:!1},e=n.loadCredentials();if(e?.accessToken)try{let o=await n.loadConfig({apiKey:e.accessToken}),i=new h(o),{user:s}=await i.getCurrentUser();t.isAuthenticated=!0,t.userEmail=s.email}catch{t.isAuthenticated=!1}try{vl("git rev-parse --is-inside-work-tree",{stdio:"ignore"}),t.isGitRepo=!0}catch{t.isGitRepo=!1}let r=Tl(process.cwd(),".runhumanrc");if(Al(r)){t.isRunhumanProject=!0;try{let o=await n.loadConfig();t.projectConfig={githubRepos:o.githubRepos}}catch{}}return t}async function Rl(n,t){let e=await n.loadConfig(),r=e.apiUrl||"https://runhuman.com";console.log("");let o=Mn("Opening browser for authentication...").start();try{let i=await Xe({apiUrl:r,autoOpenBrowser:e.autoOpenBrowser!==!1});o.stop(),n.saveCredentials({accessToken:i.token});let s=await n.loadConfig({apiKey:i.token}),c=new h(s),{user:u}=await c.getCurrentUser();n.saveUserInfo(u),t.success("Successfully logged in!"),console.log("")}catch(i){throw o.stop(),i}}async function xl(n,t){console.log(F.dim("Runhuman project detected")),console.log("");let{action:e}=await be.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 be.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await $n(n,t);break;case"run-template":console.log(`
|
|
235
|
+
Run `+F.cyan("runhuman templates")+" to see available templates."),console.log("Then run "+F.cyan("runhuman job create --template <name>")+` to use one.
|
|
236
236
|
`);break;case"list-jobs":console.log(`
|
|
237
|
-
Run `+
|
|
238
|
-
`);break;case"exit":break}}async function
|
|
239
|
-
Run: runhuman projects switch <project-id>`);return}let
|
|
237
|
+
Run `+F.cyan("runhuman job list")+` to see your recent jobs.
|
|
238
|
+
`);break;case"exit":break}}async function Ol(n,t){t.isGitRepo?console.log(F.dim("Git repository detected (not yet set up with Runhuman)")):console.log(F.dim("Not in a git repository")),console.log("");let{action:e}=await be.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 be.Separator,{name:"Exit",value:"exit"}]}]);switch(e){case"quick-test":await $n(n,t);break;case"setup-repo":await Pl(n);break;case"connect-github":await kl(n);break;case"exit":break}}async function $n(n,t){let e=await be.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"}]),r=Mn("Creating test job...").start();try{let o=n.loadCredentials(),i=await n.loadConfig({apiKey:o?.accessToken});if(!i.project){Z(r,"No project selected"),console.log(`
|
|
239
|
+
Run: runhuman projects switch <project-id>`);return}let c=await new h(i).createJob({projectId:i.project,url:e.url,description:e.description});ne(r,"Test job created!"),console.log(""),console.log(" Job ID: "+F.cyan(c.jobId)),console.log(""),console.log("A human tester will test your app shortly."),console.log("Run "+F.cyan(`runhuman job wait ${c.jobId}`)+" to wait for results."),console.log("")}catch(o){throw Z(r,"Failed to create test job"),o}}async function Pl(n){console.log(""),console.log("Setting up Runhuman in the current repository..."),console.log(""),await n.saveProjectConfig({defaultDuration:5,defaultDeviceClass:"desktop"}),console.log(""),console.log(F.green("Created .runhumanrc")),console.log(""),console.log("Next steps:"),console.log(" 1. Install the GitHub App to enable @runhuman comments"),console.log(" "+F.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(" "+F.cyan("runhuman projects update <projectId> --default-url https://your-app.com")),console.log(""),console.log(" 3. Create your first test:"),console.log(" "+F.cyan('runhuman job create https://your-app.com -d "Test login flow"')),console.log("")}async function kl(n){let r=`${(await n.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(" "+F.cyan(r)),console.log(""),console.log("After installation, you can comment "+F.bold("@runhuman")+" on any issue"),console.log("to trigger a QA test."),console.log("")}O();import{readFileSync as Ul}from"fs";import{join as _l,dirname as Dl}from"path";import{fileURLToPath as Nl}from"url";var Ml=Nl(import.meta.url),Ll=Dl(Ml),$l=_l(Ll,"../package.json"),zl=JSON.parse(Ul($l,"utf-8")),Fl=zl.version,L=new zn;L.name("runhuman").description("CLI for Runhuman - AI-orchestrated human QA testing").version(Fl);var q=new zn("job");q.alias("jobs");q.description("Manage QA test jobs");q.addCommand(Et());q.addCommand(At());q.addCommand(Ge());q.addCommand(Tt());q.addCommand(vt());q.addCommand(xt());q.addCommand(_o());q.addCommand(Do());q.addCommand(Mo());q.addCommand(Lo());q.addCommand($o());q.addCommand(Fo());L.addCommand(q);function ye(n,t){let e=n().hook("preAction",r=>{process.stderr.write(`\u26A0 deprecation: \`runhuman ${r.name()}\` is deprecated. Use \`runhuman job ${t}\` instead.
|
|
240
|
+
`)});L.addCommand(e,{hidden:!0})}ye(Et,"create");ye(At,"status");ye(Ge,"wait");ye(Tt,"results");ye(vt,"list");ye(xt,"watch");L.addCommand(qo());L.addCommand(Ho());L.addCommand(Yo());L.addCommand(Qo());L.addCommand(Xo());var Fn=en();Fn.alias("project");L.addCommand(Fn);var Jn=ln();Jn.aliases(["org","organizations","organization"]);L.addCommand(Jn);var qn=un();qn.alias("key");L.addCommand(qn);var Hn=yn();Hn.alias("template");L.addCommand(Hn);var Kn=An();Kn.alias("schedule");L.addCommand(Kn);L.addCommand(Rn());var Bn=On();Bn.alias("transfer");L.addCommand(Bn);var Vn=_n();Vn.alias("note");L.addCommand(Vn);L.addCommand(Dn());Ct(L);process.argv.slice(2).length?L.parse(process.argv):Ln().catch(n=>{console.error(n.message),process.exit(1)});
|