@qawolf/cli 1.33.0 → 1.35.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/README.md +25 -1
- package/dist/cli.js +385 -23
- package/dist/runner-sdk.js +232 -21
- package/dist/types/runnerSdk/index.d.ts +3 -1
- package/dist/types/runnerSdk/types.d.ts +7 -1
- package/package.json +2 -2
- package/skills/qawolf-cli/SKILL.md +12 -3
- package/skills/qawolf-cli/references/run-results.md +29 -2
- package/skills/qawolf-cli/references/runner.md +38 -0
package/dist/cli.js
CHANGED
|
@@ -15210,9 +15210,10 @@ function createLoggingSystem(opts) {
|
|
|
15210
15210
|
}
|
|
15211
15211
|
|
|
15212
15212
|
// src/core/publicApi/notFoundSubject.ts
|
|
15213
|
-
var
|
|
15213
|
+
var runnerRoutesWithoutLivePod = new Set([
|
|
15214
15214
|
"runner.launch",
|
|
15215
|
-
"runner.list"
|
|
15215
|
+
"runner.list",
|
|
15216
|
+
"runner.recordings"
|
|
15216
15217
|
]);
|
|
15217
15218
|
var runRoutesThatResolveOneRun = new Set([
|
|
15218
15219
|
"run.diagnose",
|
|
@@ -15234,7 +15235,7 @@ function field(input, name) {
|
|
|
15234
15235
|
return typeof value === "string" ? value : undefined;
|
|
15235
15236
|
}
|
|
15236
15237
|
function notFoundSubject(contractName, input) {
|
|
15237
|
-
if (contractName.startsWith("runner.") && !
|
|
15238
|
+
if (contractName.startsWith("runner.") && !runnerRoutesWithoutLivePod.has(contractName)) {
|
|
15238
15239
|
return { kind: "runner", runnerId: field(input, "id") };
|
|
15239
15240
|
}
|
|
15240
15241
|
if (runRoutesThatResolveOneRun.has(contractName)) {
|
|
@@ -16634,7 +16635,7 @@ var agentSessionStatuses = [
|
|
|
16634
16635
|
"failed",
|
|
16635
16636
|
"cancelled"
|
|
16636
16637
|
];
|
|
16637
|
-
var agentSessionStatusSchema = _enum(agentSessionStatuses).describe('Where the work stands. "working" means the AI is still on it. "waiting-for-you" means it asked a question and cannot continue until agent.send answers it. "completed", "failed" and "cancelled" are settled and will not change.');
|
|
16638
|
+
var agentSessionStatusSchema = _enum(agentSessionStatuses).describe('Where the work stands. "working" means the AI is still on it. "waiting-for-you" means it asked a question and cannot continue until agent.send answers it. "completed" means it finished what it was asked, and a new agent.send continues it. "failed" and "cancelled" are settled and will not change.');
|
|
16638
16639
|
var agentReplySchema = object({
|
|
16639
16640
|
askedAt: exports_iso.datetime().describe("When the AI wrote this, as an ISO 8601 timestamp."),
|
|
16640
16641
|
choices: array(string2()).optional().describe("The answers the AI offered, when the reply is a question with a fixed set of them. Answer by sending one of these back through agent.send."),
|
|
@@ -16813,7 +16814,7 @@ var defaultIdSchemas = {
|
|
|
16813
16814
|
trigger: string2().min(1).describe("The id of the trigger."),
|
|
16814
16815
|
workspace: string2().min(1).describe("The id of the workspace.")
|
|
16815
16816
|
};
|
|
16816
|
-
var workspaceForEnvironmentRef = (workspace) => workspace.optional().describe("The workspace the environment belongs to
|
|
16817
|
+
var workspaceForEnvironmentRef = (workspace) => workspace.optional().describe("The workspace the environment belongs to, which an alias resolves in. Read it from whoami.");
|
|
16817
16818
|
|
|
16818
16819
|
// node_modules/@qawolf/api-contracts/dist/v1/run/flowSelection.js
|
|
16819
16820
|
var makeFlowSelectionSchema = (ids) => object({
|
|
@@ -16987,7 +16988,7 @@ var runnerNameSchema = _enum([
|
|
|
16987
16988
|
"android",
|
|
16988
16989
|
"ios"
|
|
16989
16990
|
]);
|
|
16990
|
-
var runnerWorkspaceRequirement = "
|
|
16991
|
+
var runnerWorkspaceRequirement = "Read it from whoami.";
|
|
16991
16992
|
var runnerWorkspaceIdDescription = `The workspace the runner belongs to. ${runnerWorkspaceRequirement}`;
|
|
16992
16993
|
var runnerIdSchema = string2().min(1).max(63).regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, "A runner id may contain only lowercase letters, digits and dashes, and must start and end with a letter or digit.");
|
|
16993
16994
|
var runnerWatchUrlSchema = url().describe("A QA Wolf page showing what the runner is doing right now, where the person who launched it can watch and take over with their own mouse and keyboard. Opening it requires being signed in to QA Wolf. This link belongs to the user: put it in a normal message as soon as the runner is launched, so they can follow along while the browser is being driven. It is theirs to open, never a page for the caller to fetch or navigate to. The page ends when the runner is terminated, so share it while the runner is alive and report what you found at the end instead of repeating a dead link.");
|
|
@@ -17568,23 +17569,28 @@ var makeTriggerActionSchema = (ids) => object({
|
|
|
17568
17569
|
instructions: string2().min(1).optional().describe("Guidance for QA Wolf. Applies to generativeSuite only."),
|
|
17569
17570
|
investigateFailures: boolean2().optional().describe("Whether QA Wolf investigates failures in the resulting runs. Defaults to true."),
|
|
17570
17571
|
kind: triggerActionKindSchema.default("createSuite"),
|
|
17572
|
+
runConcurrencyLimit: string2().trim().min(1).max(255).optional().describe(`How many runs of the resulting suite may execute at once: a whole number from 1 to 1000, "unlimited", or a concurrency expression, which the server validates. It replaces the environment's limit rather than capping below it. Omit it to leave the environment's limit in charge.`),
|
|
17571
17573
|
tagNames: array(string2().min(1)).default([]).describe("Tags naming the flows to run. Combined with flowIds.")
|
|
17572
17574
|
}).describe("What the trigger runs when it fires.");
|
|
17573
17575
|
var makeTriggerScheduleFields = (ids) => ({
|
|
17574
17576
|
action: makeTriggerActionSchema(ids),
|
|
17575
17577
|
cadence: triggerCadenceSchema,
|
|
17576
17578
|
dayOfWeek: triggerDayOfWeekSchema.optional().describe("The day a weekly schedule fires on, read in timezoneId. Required when cadence is weekly."),
|
|
17579
|
+
daysOfWeek: array(triggerDayOfWeekSchema).min(1).refine((daysOfWeek) => new Set(daysOfWeek).size === daysOfWeek.length, {
|
|
17580
|
+
message: "Expected each day to be named at most once."
|
|
17581
|
+
}).optional().describe("The only days an hourly or daily schedule fires on, read in timezoneId. Name each day at most once. Omitting it fires on every day."),
|
|
17577
17582
|
environmentId: ids.environmentId.describe("The environment the scheduled runs happen in."),
|
|
17578
17583
|
minuteOfHour: number2().int().min(0).max(59).optional().describe("Minutes past the hour an hourly schedule fires. Defaults to 0."),
|
|
17579
17584
|
timeOfDay: string2().regex(/^([01]\d|2[0-3]):[0-5]\d$/).optional().describe('The wall clock time a daily or weekly schedule fires, as "HH:mm". Required when cadence is daily or weekly.'),
|
|
17580
|
-
timezoneId: string2().min(1).refine(isTimezone, "Expected an IANA timezone, such as America/New_York.").optional().describe("The IANA timezone
|
|
17585
|
+
timezoneId: string2().min(1).refine(isTimezone, "Expected an IANA timezone, such as America/New_York.").optional().describe("The IANA timezone the schedule is read in, such as America/New_York. Required when cadence is daily or weekly, and when an hourly schedule names daysOfWeek, whose day boundaries it sets. An hourly schedule firing on every day needs none, and is read in America/Los_Angeles.")
|
|
17581
17586
|
});
|
|
17582
17587
|
var makeTriggerDeploymentFields = (ids) => ({
|
|
17583
17588
|
action: makeTriggerActionSchema(ids),
|
|
17584
17589
|
branchPattern: string2().min(1).optional().describe("Only fire for deployments of a branch matching this pattern."),
|
|
17585
|
-
deployTargetPattern: string2().min(1).optional().describe("Only fire for deployments
|
|
17590
|
+
deployTargetPattern: string2().min(1).optional().describe("Only fire for deployments whose deploy target matches this pattern. A deploy target is the URL or label the deploy reported, which many deployments do not carry."),
|
|
17586
17591
|
environmentIds: array(ids.environment).default([]).describe("Only fire for deployments to these environments. Mutually exclusive with environmentPattern."),
|
|
17587
|
-
environmentPattern: string2().min(1).optional().describe("Only fire for deployments to environments matching this pattern. Mutually exclusive with environmentIds.")
|
|
17592
|
+
environmentPattern: string2().min(1).optional().describe("Only fire for deployments to environments matching this pattern. Mutually exclusive with environmentIds."),
|
|
17593
|
+
servicePattern: string2().min(1).optional().describe("Only fire for deployments whose service matches this pattern. A deployment that names no service never matches it.")
|
|
17588
17594
|
});
|
|
17589
17595
|
|
|
17590
17596
|
// node_modules/@qawolf/api-contracts/dist/v1/trigger/configurationRules.js
|
|
@@ -17620,20 +17626,28 @@ var requiredScheduleFields = {
|
|
|
17620
17626
|
hourly: [],
|
|
17621
17627
|
weekly: ["dayOfWeek", "timeOfDay", "timezoneId"]
|
|
17622
17628
|
};
|
|
17629
|
+
var optionalScheduleFields = {
|
|
17630
|
+
daily: ["daysOfWeek"],
|
|
17631
|
+
hourly: ["daysOfWeek", "minuteOfHour", "timezoneId"],
|
|
17632
|
+
weekly: []
|
|
17633
|
+
};
|
|
17623
17634
|
var cadencesUsingField = {
|
|
17624
17635
|
dayOfWeek: "a weekly cadence",
|
|
17636
|
+
daysOfWeek: "an hourly or daily cadence",
|
|
17625
17637
|
minuteOfHour: "an hourly cadence",
|
|
17626
17638
|
timeOfDay: "a daily or weekly cadence",
|
|
17627
|
-
timezoneId: "
|
|
17639
|
+
timezoneId: "an hourly, daily or weekly cadence"
|
|
17628
17640
|
};
|
|
17629
17641
|
var scheduleFields = [
|
|
17630
17642
|
"dayOfWeek",
|
|
17643
|
+
"daysOfWeek",
|
|
17631
17644
|
"minuteOfHour",
|
|
17632
17645
|
"timeOfDay",
|
|
17633
17646
|
"timezoneId"
|
|
17634
17647
|
];
|
|
17635
17648
|
var checkScheduleFields = (value, ctx) => {
|
|
17636
17649
|
const required = requiredScheduleFields[value.cadence];
|
|
17650
|
+
const optional = optionalScheduleFields[value.cadence];
|
|
17637
17651
|
for (const field of scheduleFields) {
|
|
17638
17652
|
const isSupplied = value[field] !== undefined;
|
|
17639
17653
|
if (required.includes(field)) {
|
|
@@ -17646,7 +17660,7 @@ var checkScheduleFields = (value, ctx) => {
|
|
|
17646
17660
|
}
|
|
17647
17661
|
continue;
|
|
17648
17662
|
}
|
|
17649
|
-
if (isSupplied && !(
|
|
17663
|
+
if (isSupplied && !optional.includes(field)) {
|
|
17650
17664
|
ctx.addIssue({
|
|
17651
17665
|
code: "custom",
|
|
17652
17666
|
message: `${field} applies to ${cadencesUsingField[field]} only.`,
|
|
@@ -17655,8 +17669,18 @@ var checkScheduleFields = (value, ctx) => {
|
|
|
17655
17669
|
}
|
|
17656
17670
|
}
|
|
17657
17671
|
};
|
|
17672
|
+
var checkHourlyZone = (value, ctx) => {
|
|
17673
|
+
if (value.cadence !== "hourly" || value.daysOfWeek === undefined || value.timezoneId !== undefined)
|
|
17674
|
+
return;
|
|
17675
|
+
ctx.addIssue({
|
|
17676
|
+
code: "custom",
|
|
17677
|
+
message: "timezoneId is required when an hourly cadence names daysOfWeek.",
|
|
17678
|
+
path: ["timezoneId"]
|
|
17679
|
+
});
|
|
17680
|
+
};
|
|
17658
17681
|
var checkSchedule = (value, ctx) => {
|
|
17659
17682
|
checkScheduleFields(value, ctx);
|
|
17683
|
+
checkHourlyZone(value, ctx);
|
|
17660
17684
|
if (value.action.kind === "generativeSuite") {
|
|
17661
17685
|
ctx.addIssue({
|
|
17662
17686
|
code: "custom",
|
|
@@ -18022,12 +18046,14 @@ var makeDeploymentResourceSchema = (ids) => resource({
|
|
|
18022
18046
|
commitAuthorName: string2().optional().describe("The author of the deployed commit."),
|
|
18023
18047
|
commitMessage: string2().optional().describe("The message of the deployed commit."),
|
|
18024
18048
|
commitSha: string2().optional().describe("The sha of the deployed commit."),
|
|
18049
|
+
commitUrl: string2().optional().describe("A link to the deployed commit, as declared by the deployment report."),
|
|
18025
18050
|
deployedAt: exports_iso.datetime().describe("When QA Wolf first received the deployment."),
|
|
18026
18051
|
deployTarget: string2().optional().describe("Identifies the deployed app within the environment: the URL it serves, or the build path of a mobile build. A trigger's deployTargetPattern matches against it."),
|
|
18027
18052
|
environmentId: ids.environment.describe("The QA Wolf environment the deployment reported into."),
|
|
18028
18053
|
environmentName: string2().optional().describe("The environment's name. Absent when the environment has since been deleted."),
|
|
18029
18054
|
id: ids.deployment,
|
|
18030
18055
|
provider: _enum(["github", "gitlab", "api"]).optional().describe("How QA Wolf received the deployment: a GitHub webhook, a GitLab webhook, or the deployment.reportStatus API."),
|
|
18056
|
+
service: string2().optional().describe("Which application was deployed, as the deployer named it. Absent when the deployer named none."),
|
|
18031
18057
|
status: _enum(["pending", "success", "failure", "inactive"]).optional().describe("The deployment's lifecycle status, in the vocabulary deployment.reportStatus accepts. Absent when no status was ever reported.")
|
|
18032
18058
|
}, {
|
|
18033
18059
|
urlFieldDescription: "Absolute URL of the workspace's deployments page."
|
|
@@ -18057,8 +18083,55 @@ var makeFindDeploymentsContract = (ids) => {
|
|
|
18057
18083
|
};
|
|
18058
18084
|
};
|
|
18059
18085
|
|
|
18086
|
+
// node_modules/@qawolf/api-contracts/dist/v1/deployment/didNotRunReason.js
|
|
18087
|
+
var didNotRunReasonSchema = _enum([
|
|
18088
|
+
"billing-prevented",
|
|
18089
|
+
"branch-still-syncing",
|
|
18090
|
+
"deployment-no-longer-live",
|
|
18091
|
+
"duplicate-run",
|
|
18092
|
+
"environment-not-linked-to-branch",
|
|
18093
|
+
"environment-not-ready",
|
|
18094
|
+
"environment-terminated",
|
|
18095
|
+
"internal-error",
|
|
18096
|
+
"low-risk-change",
|
|
18097
|
+
"no-flows-to-run",
|
|
18098
|
+
"no-matching-trigger",
|
|
18099
|
+
"no-pull-request",
|
|
18100
|
+
"no-relevant-flows",
|
|
18101
|
+
"rate-limited",
|
|
18102
|
+
"run-not-created",
|
|
18103
|
+
"skipped",
|
|
18104
|
+
"test-configuration-error",
|
|
18105
|
+
"trigger-not-found",
|
|
18106
|
+
"workspace-inactive",
|
|
18107
|
+
"workspace-not-ready"
|
|
18108
|
+
]).describe([
|
|
18109
|
+
"Why the matched trigger created no run, as a stable value safe to branch on in a CI pipeline. The accompanying `message` is the wording for a person to read and may be reworded at any time; this value will not be.",
|
|
18110
|
+
"billing-prevented: a billing restriction on the workspace stopped the run.",
|
|
18111
|
+
"branch-still-syncing: the environment was still syncing its flows.",
|
|
18112
|
+
"deployment-no-longer-live: the deployment was torn down or replaced by a newer one before the run started.",
|
|
18113
|
+
"duplicate-run: another run already covered this same deployment.",
|
|
18114
|
+
"environment-not-linked-to-branch: the environment has no branch to take flows from yet.",
|
|
18115
|
+
"environment-not-ready: the environment was still being prepared; retrying later can succeed.",
|
|
18116
|
+
"environment-terminated: the environment for the run is terminated.",
|
|
18117
|
+
"internal-error: QA Wolf could not start the run and the cause is on our side.",
|
|
18118
|
+
"low-risk-change: adaptive flow selection assessed the change as low risk, so nothing needed to run.",
|
|
18119
|
+
"no-flows-to-run: no flow was eligible, because the trigger's flows are in maintenance, still drafts, or missing on this branch.",
|
|
18120
|
+
"no-matching-trigger: no deployment trigger matched.",
|
|
18121
|
+
"no-pull-request: the trigger uses adaptive flow selection, which needs an open pull request, and this deployment had none.",
|
|
18122
|
+
"no-relevant-flows: adaptive flow selection found no existing flow covering this change.",
|
|
18123
|
+
"rate-limited: too many runs were requested at once; retrying later can succeed.",
|
|
18124
|
+
"run-not-created: the run never reached a decision.",
|
|
18125
|
+
"skipped: the run was skipped for a reason with no more specific value.",
|
|
18126
|
+
"test-configuration-error: the workspace's flows or dependency rules need fixing before this can run.",
|
|
18127
|
+
"trigger-not-found: the trigger no longer exists.",
|
|
18128
|
+
"workspace-inactive: the workspace is not active.",
|
|
18129
|
+
"workspace-not-ready: the workspace has not finished setting up."
|
|
18130
|
+
].join(`
|
|
18131
|
+
`));
|
|
18132
|
+
|
|
18060
18133
|
// node_modules/@qawolf/api-contracts/dist/v1/deployment/listTriggerEvaluations.js
|
|
18061
|
-
var conditionKindSchema = _enum(["environment", "deployTarget", "branch"]).describe("What the condition checks on the deployment: the environment it reported into, the deploy target
|
|
18134
|
+
var conditionKindSchema = _enum(["environment", "deployTarget", "branch", "service"]).describe("What the condition checks on the deployment: the environment it reported into, the deploy target it serves, the git branch of the deployed commit, or the service it deployed.");
|
|
18062
18135
|
var conditionVerdictSchema = discriminatedUnion("outcome", [
|
|
18063
18136
|
object({
|
|
18064
18137
|
kind: conditionKindSchema,
|
|
@@ -18081,12 +18154,13 @@ var makeRunOutcomeSchema = (ids) => discriminatedUnion("status", [
|
|
|
18081
18154
|
}),
|
|
18082
18155
|
object({ status: literal("starting") }),
|
|
18083
18156
|
object({
|
|
18084
|
-
|
|
18157
|
+
message: string2().describe("Why the matched trigger created no run, in plain language."),
|
|
18158
|
+
reason: didNotRunReasonSchema,
|
|
18085
18159
|
status: literal("did-not-run"),
|
|
18086
18160
|
supersededByRunId: ids.run.optional().describe("The run that covered this deployment instead, when an earlier delivery of the same deployment had already created one.")
|
|
18087
18161
|
}),
|
|
18088
18162
|
object({ status: literal("unknown") })
|
|
18089
|
-
]).describe("What became of the match. Matching is not running: a matched trigger can still be stopped from creating a run, and did-not-run carries the reason. starting means run creation is still in progress; unknown means QA Wolf has no record of what followed the match.");
|
|
18163
|
+
]).describe("What became of the match. Matching is not running: a matched trigger can still be stopped from creating a run, and did-not-run carries the reason. starting means run creation is still in progress; unknown means QA Wolf has no record of what followed the match. did-not-run also carries a stable reason to branch on, beside the message for a person to read.");
|
|
18090
18164
|
var makeVerdictSchema = (ids) => discriminatedUnion("outcome", [
|
|
18091
18165
|
object({
|
|
18092
18166
|
conditionSets: conditionSetsField,
|
|
@@ -18135,6 +18209,7 @@ var deploymentReportMetadata = strictObject({
|
|
|
18135
18209
|
commitAuthorName: string2().trim().min(1).max(255).describe("The display name of the person who authored the deployed commit.").optional(),
|
|
18136
18210
|
commitMessage: string2().trim().min(1).max(2048).describe("The message of the deployed commit. The deployments list shows its first line.").optional(),
|
|
18137
18211
|
commitSha: string2().trim().min(1).max(255).describe("The commit sha of the deployed application revision.").optional(),
|
|
18212
|
+
commitUrl: httpUrl().max(2048).describe("A link to the deployed commit in your code host. Send this when QA Wolf cannot resolve the commit itself, for example when the QA Wolf GitHub App is not installed.").optional(),
|
|
18138
18213
|
pullRequestNumber: number2().int().positive().describe("The number of the pull request the deployment belongs to. Requires repository.").optional(),
|
|
18139
18214
|
ref: string2().trim().min(1).max(255).describe("The branch or ref that was deployed.").optional(),
|
|
18140
18215
|
repository: string2().trim().min(1).max(255).describe('The owner/name of the repository, e.g. "acme/web".').optional()
|
|
@@ -18169,6 +18244,7 @@ var makeReportDeploymentStatusContract = (ids) => {
|
|
|
18169
18244
|
environmentVariables: record(string2(), string2()).describe("Environment variable overrides for the runs this deployment requests. A report that omits this field keeps the stored values. A report that sends it replaces them.").optional(),
|
|
18170
18245
|
metadata: deploymentReportMetadata.describe("Details the deployer declares about the deployed revision; a code host integration's own record of the same commit is shown in preference to the message and author declared here.").optional(),
|
|
18171
18246
|
providerDeploymentId: string2().trim().min(1).max(255).describe('Your identifier for the deployment, e.g. "vercel_dpl_123". Reports with the same providerDeploymentId update one deployment within this workspace.'),
|
|
18247
|
+
service: string2().trim().min(1).max(255).describe('Which application was deployed, e.g. "checkout-api", when several services deploy into one environment. A report that omits it keeps the stored value.').optional(),
|
|
18172
18248
|
status: deploymentStatusSchema.describe("The deployment lifecycle status. A deployment's first success report evaluates global triggers asynchronously, and it is the only report that does. The response does not include the resulting runs."),
|
|
18173
18249
|
workspaceId: ids.workspace.describe("The workspace to report the deployment status into. A team API key must name its own workspace.")
|
|
18174
18250
|
}).superRefine((value, context) => {
|
|
@@ -18839,6 +18915,70 @@ var makeGetIssueContract = (ids) => {
|
|
|
18839
18915
|
};
|
|
18840
18916
|
};
|
|
18841
18917
|
|
|
18918
|
+
// node_modules/@qawolf/api-contracts/dist/v1/investigation/finding.js
|
|
18919
|
+
var investigationFindingStateDescription = 'What the investigation concluded about the cause. Known values: "investigating", "fixed", "fix-proposed", "reported", "report-proposed", "flake", "question" and "dismissed". New values can appear.';
|
|
18920
|
+
var makeInvestigationFindingSchema = (ids) => object({
|
|
18921
|
+
actual: string2().optional(),
|
|
18922
|
+
commitHash: string2().optional(),
|
|
18923
|
+
decidedBy: _enum(["agent", "user"]).optional().describe("Who decided: the AI on its own, or a person who approved or dismissed it. Absent until someone decides."),
|
|
18924
|
+
disputeReason: string2().optional().describe("Why a person disputed what the AI did. Present on a proposal the AI made again after that dispute."),
|
|
18925
|
+
expected: string2().optional(),
|
|
18926
|
+
findingId: string2(),
|
|
18927
|
+
flowIds: array(ids.flow),
|
|
18928
|
+
headline: string2().optional(),
|
|
18929
|
+
issueId: ids.issue.optional(),
|
|
18930
|
+
kind: string2().describe('What the failure is. Known values: "maintenance", "bug", "flake" and "unknown". New values can appear.'),
|
|
18931
|
+
matchReason: string2().optional(),
|
|
18932
|
+
priority: issuePrioritySchema.optional(),
|
|
18933
|
+
question: object({
|
|
18934
|
+
answers: array(object({
|
|
18935
|
+
description: string2().optional(),
|
|
18936
|
+
title: string2(),
|
|
18937
|
+
value: string2().describe('What choosing this answer means. Known values: "apply", "dismiss", "report", "maintenance" and "bug". New values can appear.')
|
|
18938
|
+
})),
|
|
18939
|
+
text: string2()
|
|
18940
|
+
}).optional().describe("The question waiting for a person, with the answers offered."),
|
|
18941
|
+
response: object({
|
|
18942
|
+
answer: string2().optional(),
|
|
18943
|
+
at: exports_iso.datetime(),
|
|
18944
|
+
reason: string2().optional(),
|
|
18945
|
+
type: string2().describe('Known values: "answer" and "dispute". New values can appear.')
|
|
18946
|
+
}).optional().describe("A person's answer or dispute that the investigation hasn't acted on yet."),
|
|
18947
|
+
state: string2().describe(investigationFindingStateDescription),
|
|
18948
|
+
steps: array(string2()).optional(),
|
|
18949
|
+
subSessionIds: array(ids.chatSession),
|
|
18950
|
+
summary: string2().optional(),
|
|
18951
|
+
updatedAt: exports_iso.datetime()
|
|
18952
|
+
});
|
|
18953
|
+
|
|
18954
|
+
// node_modules/@qawolf/api-contracts/dist/v1/investigation/get.js
|
|
18955
|
+
var makeGetInvestigationContract = (ids) => {
|
|
18956
|
+
const input = object({ runId: ids.run });
|
|
18957
|
+
const output = resource({
|
|
18958
|
+
findings: array(makeInvestigationFindingSchema(ids)),
|
|
18959
|
+
runId: ids.run,
|
|
18960
|
+
sessionId: ids.chatSession.optional().describe("The investigation session. Absent when the run has none."),
|
|
18961
|
+
status: _enum(["investigating", "finished", "not-investigated"]).describe('Whether the investigation session is still working. "not-investigated" when the run has no investigation.')
|
|
18962
|
+
}, { urlFieldDescription: "The run in the QA Wolf app." });
|
|
18963
|
+
return {
|
|
18964
|
+
annotations: {
|
|
18965
|
+
destructiveHint: false,
|
|
18966
|
+
openWorldHint: false,
|
|
18967
|
+
readOnlyHint: true
|
|
18968
|
+
},
|
|
18969
|
+
description: "Read what the QA Wolf AI investigating a run has concluded: one finding per cause of its failures, " + "with the question waiting for a person and any answer or dispute the investigation hasn't acted on yet.",
|
|
18970
|
+
input,
|
|
18971
|
+
kind: "read",
|
|
18972
|
+
name: "investigation.get",
|
|
18973
|
+
output
|
|
18974
|
+
};
|
|
18975
|
+
};
|
|
18976
|
+
|
|
18977
|
+
// node_modules/@qawolf/api-contracts/dist/v1/investigation/index.js
|
|
18978
|
+
var makeInvestigationContracts = (ids) => ({
|
|
18979
|
+
get: makeGetInvestigationContract(ids)
|
|
18980
|
+
});
|
|
18981
|
+
|
|
18842
18982
|
// node_modules/@qawolf/api-contracts/dist/v1/issue/addFlows.js
|
|
18843
18983
|
var makeAddFlowsToIssueContract = (ids) => {
|
|
18844
18984
|
const input = object({
|
|
@@ -19096,6 +19236,13 @@ var makeDiagnoseRunContract = (ids) => {
|
|
|
19096
19236
|
};
|
|
19097
19237
|
|
|
19098
19238
|
// node_modules/@qawolf/api-contracts/dist/v1/run/summary.js
|
|
19239
|
+
var publicRunStatusValues = [
|
|
19240
|
+
"queued",
|
|
19241
|
+
"running",
|
|
19242
|
+
"passed",
|
|
19243
|
+
"failed",
|
|
19244
|
+
"canceled"
|
|
19245
|
+
];
|
|
19099
19246
|
var makeRunSummaryFields = (ids) => ({
|
|
19100
19247
|
completedAt: exports_iso.datetime().optional().describe("When the run finished executing. Absent while queued or running, and also absent for a terminal run that never completed execution (e.g. every flow was canceled or skipped)."),
|
|
19101
19248
|
createdAt: exports_iso.datetime(),
|
|
@@ -19106,7 +19253,7 @@ var makeRunSummaryFields = (ids) => ({
|
|
|
19106
19253
|
commitUrl: string2().optional().describe("Link to the commit on the code host.")
|
|
19107
19254
|
}).describe("The branch and commit under test. The fields are present when a deploy notification started the run, and absent for runs started another way, for example manually or with run.create."),
|
|
19108
19255
|
runId: ids.run,
|
|
19109
|
-
status: _enum(
|
|
19256
|
+
status: _enum(publicRunStatusValues).describe("Whole-run status, and the value to gate a pipeline on. Terminal statuses are passed, failed, and canceled; poll until one is reached. `failed` means the run found a bug that is still open and serious enough to block — a lower-priority bug, or one since resolved, reads `passed`, and the per-flow diagnoses are still in `flows`. The run stays `running` while any investigation is unresolved, including one that carried over to a later run, so a terminal status always means a settled answer.")
|
|
19110
19257
|
});
|
|
19111
19258
|
|
|
19112
19259
|
// node_modules/@qawolf/api-contracts/dist/v1/run/find.js
|
|
@@ -19139,7 +19286,9 @@ var makeFindRunsContract = (ids) => {
|
|
|
19139
19286
|
|
|
19140
19287
|
// node_modules/@qawolf/api-contracts/dist/v1/run/get.js
|
|
19141
19288
|
var makeGetRunContract = (ids) => {
|
|
19289
|
+
const flowStatus = _enum(publicRunStatusValues);
|
|
19142
19290
|
const input = object({
|
|
19291
|
+
flowStatuses: array(flowStatus).min(1).optional().describe("Return only the flows whose status is one of these. A suite run can " + "hold hundreds of flows, so start an investigation with failed " + "alone. A flow that passed after a retry reads passed, so add " + "passed when a flaky flow is missing from the answer. Omit to " + "return every flow. The run-level fields are unaffected."),
|
|
19143
19292
|
runId: ids.run
|
|
19144
19293
|
});
|
|
19145
19294
|
const artifactUrls = {
|
|
@@ -19196,11 +19345,12 @@ var makeGetRunContract = (ids) => {
|
|
|
19196
19345
|
attempts,
|
|
19197
19346
|
flowId: ids.flow,
|
|
19198
19347
|
name: string2(),
|
|
19199
|
-
status:
|
|
19348
|
+
status: flowStatus.exclude(["failed"])
|
|
19200
19349
|
});
|
|
19201
|
-
const flows = array(discriminatedUnion("status", [failedFlow, nonFailedFlow])).describe("The run's flows, ordered alphabetically by name.");
|
|
19350
|
+
const flows = array(discriminatedUnion("status", [failedFlow, nonFailedFlow])).describe("The run's flows, ordered alphabetically by name. Only the flows " + "matching flowStatuses when the request set it.");
|
|
19202
19351
|
const output = resource({
|
|
19203
19352
|
...makeRunSummaryFields(ids),
|
|
19353
|
+
blockingBugCount: number2().int().nonnegative().describe("How many bugs this run found are blocking: still open, and priority urgent, high, or unprioritized — unprioritized counts because nobody has ruled it out yet. This is what `failed` is derived from, so it explains a failure rather than adding a second verdict: gate on `status`, then read this to say how many bugs are holding the build. It counts a bug filed against a later run by an investigation that carried over from this one, and it drops a bug once that bug is resolved."),
|
|
19204
19354
|
flows,
|
|
19205
19355
|
runId: ids.run.describe("The run this response describes. Treat it as canonical: it can differ from the id you asked for. A deploy notification returns a run id before the run exists, and if a second notification for the same commit is folded into an earlier run, that id resolves to the earlier run instead.")
|
|
19206
19356
|
}, { urlFieldDescription: "Absolute URL of the run page." });
|
|
@@ -19210,7 +19360,7 @@ var makeGetRunContract = (ids) => {
|
|
|
19210
19360
|
openWorldHint: false,
|
|
19211
19361
|
readOnlyHint: true
|
|
19212
19362
|
},
|
|
19213
|
-
description: "Get a run's status, per-flow results, and
|
|
19363
|
+
description: "Get a run's status, per-flow results, links, and how many of its bugs are blocking.",
|
|
19214
19364
|
input,
|
|
19215
19365
|
kind: "read",
|
|
19216
19366
|
name: "run.get",
|
|
@@ -19462,8 +19612,12 @@ var makeImportPackageOnRunnerContract = (ids) => {
|
|
|
19462
19612
|
var makeLaunchRunnerContract = (ids) => {
|
|
19463
19613
|
const input = object({
|
|
19464
19614
|
id: runnerIdSchema.describe("Id to launch the runner under, chosen by the caller. Launching the same id again returns the runner already running under it."),
|
|
19615
|
+
initialUrl: navigableUrlSchema.optional().describe("The page to open as the browser starts, so the first screenshot already shows the application rather than a blank page."),
|
|
19465
19616
|
runnerName: runnerNameSchema.optional().describe("The runner family to launch. Defaults to the standard web family, playwright."),
|
|
19466
19617
|
workspaceId: ids.workspace.optional().describe(`The workspace to launch the runner in. ${runnerWorkspaceRequirement}`)
|
|
19618
|
+
}).refine((request) => request.initialUrl === undefined || (request.runnerName ?? "playwright") === "playwright", {
|
|
19619
|
+
message: "initialUrl opens a page in the runner's browser, so only the playwright runner takes it.",
|
|
19620
|
+
path: ["initialUrl"]
|
|
19467
19621
|
});
|
|
19468
19622
|
const output = makeRunnerSchema().extend({
|
|
19469
19623
|
alreadyRunning: boolean2().describe("True when a runner was already running under this id and was returned unchanged, so no second runner was started and nothing new is billed. Not an error either way."),
|
|
@@ -19475,7 +19629,7 @@ var makeLaunchRunnerContract = (ids) => {
|
|
|
19475
19629
|
openWorldHint: false,
|
|
19476
19630
|
readOnlyHint: false
|
|
19477
19631
|
},
|
|
19478
|
-
description: "Launch an interactive runner on the caller's team under an id the caller chooses. Launching the same id again returns the runner already running rather than starting a second one, and the same id with a different runnerName is refused. A runner is not permanent: it terminates on its own after a period of inactivity, and launching the same id after that starts and bills a new runner
|
|
19632
|
+
description: "Launch an interactive runner on the caller's team under an id the caller chooses. Send `initialUrl` so its browser comes up on the page the work starts from, which saves a separate navigation and the browser's cold start. Launching the same id again returns the runner already running rather than starting a second one, and the same id with a different runnerName is refused. A runner is not permanent: it terminates on its own after a period of inactivity, and launching the same id after that starts and bills a new runner, so read `alreadyRunning` to tell which happened. A success means the runner is answering: the call waits for the pod it started to come up, so the very next call to the runner reaches it. If the pod does not come up in time the call fails instead, leaving the runner running, and launching the same id again attaches to it.",
|
|
19479
19633
|
input,
|
|
19480
19634
|
kind: "write",
|
|
19481
19635
|
name: "runner.launch",
|
|
@@ -19521,7 +19675,7 @@ var makeListRunnersContract = (ids) => {
|
|
|
19521
19675
|
openWorldHint: false,
|
|
19522
19676
|
readOnlyHint: true
|
|
19523
19677
|
},
|
|
19524
|
-
description: "List the runners running on the caller's team right now. A
|
|
19678
|
+
description: "List the runners running on the caller's team right now. A listed runner may be driven by another machine or session, so sending it work can collide with theirs. A runner that was terminated, or that terminated on its own after inactivity, is not listed, because the run system keeps no record of a runner once it is gone. This is a lookup: it never starts a runner, and it does not reset a runner's inactivity clock the way reading its journal does. It is how to find a runner launched from another machine or in an earlier session, when the id it was launched under is no longer at hand.",
|
|
19525
19679
|
input,
|
|
19526
19680
|
kind: "read",
|
|
19527
19681
|
name: "runner.list",
|
|
@@ -19606,6 +19760,7 @@ var makeRunFlowOnRunnerContract = (ids) => {
|
|
|
19606
19760
|
env: runEnvironmentSchema.optional().describe("Environment variables to make available to the run."),
|
|
19607
19761
|
environmentId: ids.environmentRef.optional().describe("A QA Wolf environment whose variables the run is given, by id or alias. QA Wolf reads and decrypts them itself, so they never travel in the request and the count and length limits on `env` do not apply to them. Send this or `env`, not both."),
|
|
19608
19762
|
files: runFilesSchema.describe("Every file the run needs keyed by its path — the flow file, everything it imports, package.json and tsconfig.json. A runner holds no copy of your project, so what runs is exactly what is sent here."),
|
|
19763
|
+
flowId: ids.flow.optional().describe("The QA Wolf flow this run is for. The run then sees it as `QAWOLF_WORKFLOW_ID`, the same value a platform run of that flow sees, so fixtures named and cleaned up by it match across both. Omit it when the flow is not in QA Wolf yet, and the run has no `QAWOLF_WORKFLOW_ID`."),
|
|
19609
19764
|
id: runnerIdSchema.describe("Id of the runner to run the flow on."),
|
|
19610
19765
|
selection: runSelectionSchema.optional().describe("Run only these lines, inside the page the runner is already on, instead of running the whole flow from a fresh browser. Omit to run the whole entry point."),
|
|
19611
19766
|
unchangedFiles: unchangedFilesSchema.optional().describe("Files this runner already holds from an earlier run, keyed by path with a hex SHA-256 of the content it should hold. Send this to make `files` only what changed. The runner refuses with `needs-full-sync` if it holds none of a referenced path, so it never runs something other than what was asked for. Omit it to send every file."),
|
|
@@ -19857,6 +20012,7 @@ var makeContractsV1 = (ids) => {
|
|
|
19857
20012
|
removeTag: makeRemoveTagFromFlowsContract(resolvedIds),
|
|
19858
20013
|
update: makeUpdateFlowContract(resolvedIds)
|
|
19859
20014
|
},
|
|
20015
|
+
investigation: makeInvestigationContracts(resolvedIds),
|
|
19860
20016
|
issue: {
|
|
19861
20017
|
addFlows: makeAddFlowsToIssueContract(resolvedIds),
|
|
19862
20018
|
create: makeCreateIssueContract(resolvedIds),
|
|
@@ -22389,7 +22545,7 @@ function startUpdateCheck(deps) {
|
|
|
22389
22545
|
// package.json
|
|
22390
22546
|
var package_default = {
|
|
22391
22547
|
name: "@qawolf/cli",
|
|
22392
|
-
version: "1.
|
|
22548
|
+
version: "1.35.0",
|
|
22393
22549
|
description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
22394
22550
|
keywords: [
|
|
22395
22551
|
"automation",
|
|
@@ -22460,7 +22616,7 @@ var package_default = {
|
|
|
22460
22616
|
"@clack/prompts": "1.5.1",
|
|
22461
22617
|
"@napi-rs/keyring": "1.3.0",
|
|
22462
22618
|
"@oxc-node/core": "0.1.0",
|
|
22463
|
-
"@qawolf/api-contracts": "0.
|
|
22619
|
+
"@qawolf/api-contracts": "0.70.0",
|
|
22464
22620
|
"@qawolf/emails": "1.1.1",
|
|
22465
22621
|
"@qawolf/flow-targets": "1.0.0",
|
|
22466
22622
|
"@qawolf/flows": "0.1.4",
|
|
@@ -36271,6 +36427,9 @@ async function handleRunnerActions(ctx, options, deps) {
|
|
|
36271
36427
|
return framesNote === undefined ? failure : { ...failure, error: appendSentence(failure.error, framesNote) };
|
|
36272
36428
|
}
|
|
36273
36429
|
|
|
36430
|
+
// src/domains/interactiveRunner/deps.ts
|
|
36431
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
36432
|
+
|
|
36274
36433
|
// src/shell/interactiveRunner/collectRunFiles.ts
|
|
36275
36434
|
import { join as join53 } from "node:path";
|
|
36276
36435
|
|
|
@@ -36538,6 +36697,7 @@ function makeInteractiveRunnerDeps(options) {
|
|
|
36538
36697
|
cwd: options.cwd,
|
|
36539
36698
|
env: options.env,
|
|
36540
36699
|
makeRunnerId,
|
|
36700
|
+
makeRecordingId: randomUUID3,
|
|
36541
36701
|
readFile: (path) => options.fs.readFile(path),
|
|
36542
36702
|
readStdin: readStdin2,
|
|
36543
36703
|
runFilesManifest: makeRunFilesManifestStore({
|
|
@@ -38415,6 +38575,207 @@ function registerRunCommand(runner, signals) {
|
|
|
38415
38575
|
}, runnerDeps(ctx)))(opts, command));
|
|
38416
38576
|
}
|
|
38417
38577
|
|
|
38578
|
+
// src/core/messages/interactiveRunner/recording.ts
|
|
38579
|
+
var recordingMessages = {
|
|
38580
|
+
noRunnerIdForHistory: "Name the runner whose recordings to read with --runner <id> or QAWOLF_RUNNER_ID. History stays readable after the runner terminates.",
|
|
38581
|
+
unsupported: "Video recording requires a Playwright runner.",
|
|
38582
|
+
inProgress: "A recording is already in progress. Use runner record status to check its mode and id. Stop a manual recording by id, or wait for the run to finish an automatic recording.",
|
|
38583
|
+
notFound: "That recording was not found on the runner. Use runner record status for the active recording or runner list-recordings for published history.",
|
|
38584
|
+
idUsed: "That recording id has already been used. Start a new recording with a new UUID.",
|
|
38585
|
+
failed: "The recording operation failed. If this was a stop, retry it with the same recording id to finish stopping or publishing it. Check runner list-recordings for its published status.",
|
|
38586
|
+
failedCapture: "The recording failed. Check runner list-recordings for its published status.",
|
|
38587
|
+
recordingId: (id) => `Recording id: ${id}.`
|
|
38588
|
+
};
|
|
38589
|
+
|
|
38590
|
+
// src/domains/interactiveRunner/recordingOutput.ts
|
|
38591
|
+
function recordingFailure(reason) {
|
|
38592
|
+
switch (reason) {
|
|
38593
|
+
case "unsupported":
|
|
38594
|
+
return {
|
|
38595
|
+
error: recordingMessages.unsupported,
|
|
38596
|
+
exitCode: exitCodes.invalidArgs
|
|
38597
|
+
};
|
|
38598
|
+
case "screen-not-ready":
|
|
38599
|
+
return {
|
|
38600
|
+
error: interactiveRunnerMessages.screenNotReady,
|
|
38601
|
+
exitCode: exitCodes.network
|
|
38602
|
+
};
|
|
38603
|
+
case "recording-in-progress":
|
|
38604
|
+
return {
|
|
38605
|
+
error: recordingMessages.inProgress,
|
|
38606
|
+
exitCode: exitCodes.invalidArgs
|
|
38607
|
+
};
|
|
38608
|
+
case "recording-not-found":
|
|
38609
|
+
return {
|
|
38610
|
+
error: recordingMessages.notFound,
|
|
38611
|
+
exitCode: exitCodes.notFound
|
|
38612
|
+
};
|
|
38613
|
+
case "recording-id-used":
|
|
38614
|
+
return {
|
|
38615
|
+
error: recordingMessages.idUsed,
|
|
38616
|
+
exitCode: exitCodes.invalidArgs
|
|
38617
|
+
};
|
|
38618
|
+
case "runner-unreachable":
|
|
38619
|
+
return {
|
|
38620
|
+
error: interactiveRunnerMessages.runnerUnreachable,
|
|
38621
|
+
exitCode: exitCodes.network
|
|
38622
|
+
};
|
|
38623
|
+
case "recording-failed":
|
|
38624
|
+
return {
|
|
38625
|
+
error: recordingMessages.failed,
|
|
38626
|
+
exitCode: exitCodes.testFailure
|
|
38627
|
+
};
|
|
38628
|
+
}
|
|
38629
|
+
}
|
|
38630
|
+
function formatRecordingState(answer) {
|
|
38631
|
+
const { result, url } = answer;
|
|
38632
|
+
if (result.outcome === "failure")
|
|
38633
|
+
return recordingFailure(result.failureReason).error;
|
|
38634
|
+
const { active, auto } = result.state;
|
|
38635
|
+
const lines = [
|
|
38636
|
+
active ? `Recording ${active.id} (${active.mode}), started ${active.startedAt}.` : "No active recording.",
|
|
38637
|
+
`Automatic recording: ${auto}.`
|
|
38638
|
+
];
|
|
38639
|
+
if (result.recording) {
|
|
38640
|
+
lines.push(`Recording ${result.recording.id}: ${result.recording.status}.`);
|
|
38641
|
+
}
|
|
38642
|
+
return [...lines, url].join(`
|
|
38643
|
+
`);
|
|
38644
|
+
}
|
|
38645
|
+
function formatRecordings(answer) {
|
|
38646
|
+
const lines = answer.recordings.map((recording) => [
|
|
38647
|
+
`${recording.id} (${recording.mode}, ${recording.status})`,
|
|
38648
|
+
` ${recording.startedAt} to ${recording.endedAt}`,
|
|
38649
|
+
` ${recording.url}`,
|
|
38650
|
+
...recording.videoUrl ? [` Video: ${recording.videoUrl}`] : []
|
|
38651
|
+
].join(`
|
|
38652
|
+
`));
|
|
38653
|
+
if (lines.length === 0)
|
|
38654
|
+
lines.push("No published recordings found.");
|
|
38655
|
+
if (answer.nextPageToken !== undefined) {
|
|
38656
|
+
lines.push(`Next page token (pass with --page-token): ${answer.nextPageToken}`);
|
|
38657
|
+
}
|
|
38658
|
+
return lines.join(`
|
|
38659
|
+
`);
|
|
38660
|
+
}
|
|
38661
|
+
|
|
38662
|
+
// src/domains/interactiveRunner/recordingRequests.ts
|
|
38663
|
+
async function recordOnRunner(ctx, runner, command) {
|
|
38664
|
+
const contract = publicContractsV1.runner.record;
|
|
38665
|
+
const parsed = contract.input.safeParse({ id: runner.runnerId, command });
|
|
38666
|
+
if (!parsed.success) {
|
|
38667
|
+
return {
|
|
38668
|
+
ok: false,
|
|
38669
|
+
error: "Invalid recording request. Runner ids must be valid and recording ids must be UUIDs.",
|
|
38670
|
+
exitCode: exitCodes.invalidArgs
|
|
38671
|
+
};
|
|
38672
|
+
}
|
|
38673
|
+
const result = await ctx.platformClient.callPublicApi(contract, parsed.data, runnerCallOptions);
|
|
38674
|
+
return result.ok ? result : { ok: false, ...runnerRequestFailure(result, runner) };
|
|
38675
|
+
}
|
|
38676
|
+
async function readRunnerRecordings(ctx, runner, query) {
|
|
38677
|
+
const contract = publicContractsV1.runner.recordings;
|
|
38678
|
+
const parsed = contract.input.safeParse({ id: runner.runnerId, ...query });
|
|
38679
|
+
if (!parsed.success) {
|
|
38680
|
+
return {
|
|
38681
|
+
ok: false,
|
|
38682
|
+
error: "Invalid recordings request. Use a valid runner id, a UUID recording id, and a page token of at most 4096 characters.",
|
|
38683
|
+
exitCode: exitCodes.invalidArgs
|
|
38684
|
+
};
|
|
38685
|
+
}
|
|
38686
|
+
const result = await ctx.platformClient.callPublicApi(contract, parsed.data);
|
|
38687
|
+
return result.ok ? result : { ok: false, ...runnerRequestFailure(result, runner) };
|
|
38688
|
+
}
|
|
38689
|
+
|
|
38690
|
+
// src/domains/interactiveRunner/recording.ts
|
|
38691
|
+
async function handleRunnerRecord(ctx, options, deps) {
|
|
38692
|
+
const resolved = await resolveRunner(ctx, { autoLaunch: false, runner: options.runner }, deps);
|
|
38693
|
+
if (resolved.type === "failed")
|
|
38694
|
+
return { ...failureFields(resolved), exitCode: resolved.exitCode };
|
|
38695
|
+
const command = options.command.action === "start" ? {
|
|
38696
|
+
action: "start",
|
|
38697
|
+
recordingId: options.command.recordingId ?? deps.makeRecordingId()
|
|
38698
|
+
} : options.command;
|
|
38699
|
+
const response = await recordOnRunner(ctx, resolved, command);
|
|
38700
|
+
const idNote = "recordingId" in command ? recordingMessages.recordingId(command.recordingId) : undefined;
|
|
38701
|
+
if (!response.ok) {
|
|
38702
|
+
const fields = failureFields(response);
|
|
38703
|
+
return {
|
|
38704
|
+
...fields,
|
|
38705
|
+
...idNote ? { errorBody: [fields.errorBody, idNote].filter(Boolean).join(`
|
|
38706
|
+
`) } : {},
|
|
38707
|
+
exitCode: response.exitCode
|
|
38708
|
+
};
|
|
38709
|
+
}
|
|
38710
|
+
const answer = response.value;
|
|
38711
|
+
if (answer.result.outcome === "failure") {
|
|
38712
|
+
return {
|
|
38713
|
+
...recordingFailure(answer.result.failureReason),
|
|
38714
|
+
errorBody: [idNote, answer.url].filter(Boolean).join(`
|
|
38715
|
+
`)
|
|
38716
|
+
};
|
|
38717
|
+
}
|
|
38718
|
+
ctx.ui.output(answer, formatRecordingState(answer));
|
|
38719
|
+
if (answer.result.recording?.status === "failed") {
|
|
38720
|
+
return {
|
|
38721
|
+
error: recordingMessages.failedCapture,
|
|
38722
|
+
exitCode: exitCodes.testFailure,
|
|
38723
|
+
errorBody: [
|
|
38724
|
+
recordingMessages.recordingId(answer.result.recording.id),
|
|
38725
|
+
answer.url
|
|
38726
|
+
].join(`
|
|
38727
|
+
`)
|
|
38728
|
+
};
|
|
38729
|
+
}
|
|
38730
|
+
return;
|
|
38731
|
+
}
|
|
38732
|
+
async function handleRunnerRecordings(ctx, options, deps) {
|
|
38733
|
+
const resolved = await resolveRunner(ctx, {
|
|
38734
|
+
autoLaunch: false,
|
|
38735
|
+
noRunnerIdMessage: recordingMessages.noRunnerIdForHistory,
|
|
38736
|
+
runner: options.runner
|
|
38737
|
+
}, deps);
|
|
38738
|
+
if (resolved.type === "failed")
|
|
38739
|
+
return { ...failureFields(resolved), exitCode: resolved.exitCode };
|
|
38740
|
+
const response = await readRunnerRecordings(ctx, resolved, {
|
|
38741
|
+
...options.recordingId === undefined ? {} : { recordingId: options.recordingId },
|
|
38742
|
+
...options.pageToken === undefined ? {} : { pageToken: options.pageToken }
|
|
38743
|
+
});
|
|
38744
|
+
if (!response.ok)
|
|
38745
|
+
return { ...failureFields(response), exitCode: response.exitCode };
|
|
38746
|
+
ctx.ui.output(response.value, formatRecordings(response.value));
|
|
38747
|
+
return;
|
|
38748
|
+
}
|
|
38749
|
+
|
|
38750
|
+
// src/commands/runner/recording.register.ts
|
|
38751
|
+
function registerRunnerRecordingCommands(runner, signals) {
|
|
38752
|
+
const record = runner.command("record").description("Control video recording on a Playwright runner");
|
|
38753
|
+
declareCommandKind(record.command("start"), "write").description("Start manual video capture across runs. Requires a ready screen and suppresses automatic capture until stopped").option("--recording-id <uuid>", "Recording UUID. Generated when omitted; reuse it when retrying this start").option("--runner <id>", runnerFlagDescription).action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerRecord(ctx, {
|
|
38754
|
+
command: { action: "start", recordingId: opts.recordingId },
|
|
38755
|
+
runner: opts.runner
|
|
38756
|
+
}, runnerDeps(ctx)))(opts, command));
|
|
38757
|
+
declareCommandKind(record.command("stop <recording-id>"), "write").description("Stop the recording with this UUID and publish it. Retrying cannot stop a later recording").option("--runner <id>", runnerFlagDescription).action((recordingId, opts, command) => withAuthContext(signals, (ctx) => handleRunnerRecord(ctx, {
|
|
38758
|
+
command: { action: "stop", recordingId },
|
|
38759
|
+
runner: opts.runner
|
|
38760
|
+
}, runnerDeps(ctx)))(opts, command));
|
|
38761
|
+
declareCommandKind(record.command("status"), "read").description("Show the active recording and automatic recording setting").option("--runner <id>", runnerFlagDescription).action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerRecord(ctx, {
|
|
38762
|
+
command: { action: "status" },
|
|
38763
|
+
runner: opts.runner
|
|
38764
|
+
}, runnerDeps(ctx)))(opts, command));
|
|
38765
|
+
declareCommandKind(record.command("auto"), "write").addArgument(new Argument2("<setting>", "Automatic recording setting").choices([
|
|
38766
|
+
"on",
|
|
38767
|
+
"off"
|
|
38768
|
+
])).description("Enable or disable automatic recording for subsequent full runs").option("--runner <id>", runnerFlagDescription).action((setting, opts, command) => withAuthContext(signals, (ctx) => handleRunnerRecord(ctx, {
|
|
38769
|
+
command: { action: "auto", enabled: setting === "on" },
|
|
38770
|
+
runner: opts.runner
|
|
38771
|
+
}, runnerDeps(ctx)))(opts, command));
|
|
38772
|
+
declareCommandKind(runner.command("list-recordings"), "read").description("Read a page of published video recordings, including after the runner terminates. Platform URLs persist; video URLs expire").option("--runner <id>", runnerFlagDescription).option("--recording-id <uuid>", "Look up one recording; an empty result means it is not published or does not exist").option("--page-token <token>", "Continue from nextPageToken returned by the previous page").action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerRecordings(ctx, {
|
|
38773
|
+
runner: opts.runner,
|
|
38774
|
+
...opts.recordingId === undefined ? {} : { recordingId: opts.recordingId },
|
|
38775
|
+
...opts.pageToken === undefined ? {} : { pageToken: opts.pageToken }
|
|
38776
|
+
}, runnerDeps(ctx)))(opts, command));
|
|
38777
|
+
}
|
|
38778
|
+
|
|
38418
38779
|
// src/commands/runner/index.ts
|
|
38419
38780
|
function registerRunnerCommand(program, signals) {
|
|
38420
38781
|
const runner = program.command("runner").description("Drive an interactive runner on the QA Wolf platform");
|
|
@@ -38428,6 +38789,7 @@ function registerRunnerCommand(program, signals) {
|
|
|
38428
38789
|
registerRunnerImportPackageCommand(runner, signals);
|
|
38429
38790
|
registerRunnerHighlightSelectorCommand(runner, signals);
|
|
38430
38791
|
registerRunnerPromoteSnapshotCommand(runner, signals);
|
|
38792
|
+
registerRunnerRecordingCommands(runner, signals);
|
|
38431
38793
|
}
|
|
38432
38794
|
|
|
38433
38795
|
// src/commands/program.ts
|
|
@@ -38458,4 +38820,4 @@ createProgram({ signals }).parseAsync().catch(() => {
|
|
|
38458
38820
|
process.exitCode = 1;
|
|
38459
38821
|
}).finally(() => exitWhenIdle(typeof process.exitCode === "number" ? process.exitCode : 0));
|
|
38460
38822
|
|
|
38461
|
-
//# debugId=
|
|
38823
|
+
//# debugId=DB32E61082C9C27564756E2164756E21
|