@qawolf/cli 1.37.0 → 1.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +217 -18
- package/dist/runner-sdk.js +215 -16
- package/package.json +2 -2
- package/skills/qawolf-cli/SKILL.md +6 -2
- package/skills/qawolf-cli/references/run-results.md +3 -1
package/dist/cli.js
CHANGED
|
@@ -16903,6 +16903,72 @@ var makeCreateRunContract = (ids) => {
|
|
|
16903
16903
|
};
|
|
16904
16904
|
};
|
|
16905
16905
|
|
|
16906
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/listScreenshotComparisons.js
|
|
16907
|
+
var maxListedScreenshotAttempts = 20;
|
|
16908
|
+
var makeListScreenshotComparisonsContract = (ids) => {
|
|
16909
|
+
const input = object({
|
|
16910
|
+
flowIds: array(ids.flow).min(1).max(100).refine((flowIds) => new Set(flowIds).size === flowIds.length, "flowIds must be unique").optional().describe("Only list the comparisons of these flows. Each flow must have a result in the run. Omit to list every failed flow of the run."),
|
|
16911
|
+
runId: ids.run
|
|
16912
|
+
});
|
|
16913
|
+
const imageUrl = (image) => url().describe(`Signed URL of the full-size ${image} PNG. It stops working at expiresAt.`);
|
|
16914
|
+
const previewUrl = (image) => url().optional().describe(`Signed URL of a JPEG copy of the ${image} image, scaled down so its long edge is at most 1568 pixels. ` + "Fetch this one to look at the image: it is small enough to view inline, while the full-size PNG can be too large. " + "It stops working at expiresAt. Absent when QA Wolf could not make the copy, for example because the full-size image is gone.");
|
|
16915
|
+
const passedEnvironment = object({
|
|
16916
|
+
environmentId: ids.environment,
|
|
16917
|
+
lastPassedAt: exports_iso.datetime().describe("When the latest passing comparison in this environment ran.")
|
|
16918
|
+
});
|
|
16919
|
+
const passingElsewhere = discriminatedUnion("status", [
|
|
16920
|
+
object({
|
|
16921
|
+
status: literal("not-tracked").describe("QA Wolf does not record screenshot comparisons for this workspace, so it cannot tell.")
|
|
16922
|
+
}),
|
|
16923
|
+
object({
|
|
16924
|
+
environments: array(passedEnvironment).describe("Other environments where the current baseline passed this screenshot in the last 14 days, most recent first. " + "Empty when it passed nowhere else, or when the baseline no longer exists."),
|
|
16925
|
+
status: literal("tracked")
|
|
16926
|
+
})
|
|
16927
|
+
]).describe("Whether the current baseline still passes in other environments. " + "A baseline that passes elsewhere points to a difference in this environment rather than an outdated baseline.");
|
|
16928
|
+
const comparison = object({
|
|
16929
|
+
actualPreviewUrl: previewUrl("actual"),
|
|
16930
|
+
actualUrl: imageUrl("actual (the screenshot the flow took)"),
|
|
16931
|
+
attemptCompletedAt: exports_iso.datetime().describe("When the run attempt that made the comparison finished."),
|
|
16932
|
+
baselineStatus: _enum([
|
|
16933
|
+
"unchanged",
|
|
16934
|
+
"accepted",
|
|
16935
|
+
"changed-since-run",
|
|
16936
|
+
"deleted",
|
|
16937
|
+
"unknown"
|
|
16938
|
+
]).describe('How the current baseline relates to the expected image this comparison used. "unchanged": the baseline is still the expected image. ' + `"accepted": the baseline is now this comparison's actual screenshot, because someone accepted it, so a reattempt should pass. ` + '"changed-since-run": someone replaced the baseline with another image after this run, so rerun the flow before deciding anything. ' + '"deleted": the baseline no longer exists, and the next run will save its screenshot as the new baseline. ' + `"unknown": the run's copy of the expected image is gone, so QA Wolf cannot compare.`),
|
|
16939
|
+
comparisonId: string2().describe("Identifies this comparison: the run attempt id and the position of the comparison in that attempt."),
|
|
16940
|
+
diffPreviewUrl: previewUrl("diff"),
|
|
16941
|
+
diffUrl: imageUrl("diff (the pixels that differ are highlighted)"),
|
|
16942
|
+
expectedPreviewUrl: previewUrl("expected"),
|
|
16943
|
+
expectedUrl: imageUrl("expected (the baseline as it was when the flow ran)"),
|
|
16944
|
+
filePath: string2().optional().describe("The flow file that made the comparison."),
|
|
16945
|
+
flowId: ids.flow,
|
|
16946
|
+
lineNumber: number2().int().optional().describe("The line of filePath that made the comparison."),
|
|
16947
|
+
name: string2().describe("The screenshot name the flow passed to toHaveScreenshot. The baseline is stored as _screenshots_/<name>.png in team storage."),
|
|
16948
|
+
passingElsewhere,
|
|
16949
|
+
runAttemptId: string2(),
|
|
16950
|
+
similarity: number2().describe("How much of the actual image matches the expected image, in percent."),
|
|
16951
|
+
sizeMismatch: boolean2().describe("The actual and expected images have different dimensions. That fails the comparison whatever the pixels show, and similarity reads 0.")
|
|
16952
|
+
});
|
|
16953
|
+
const output = object({
|
|
16954
|
+
comparisons: array(comparison).describe(`Each screenshot comparison that failed its flow, from the ${maxListedScreenshotAttempts} most recent failed attempts of the selected flows, oldest attempt first.`),
|
|
16955
|
+
expiresAt: exports_iso.datetime().describe("When the image URLs stop working. Call run.listScreenshotComparisons again for fresh ones after it."),
|
|
16956
|
+
truncated: boolean2().describe(`True when the selected flows have more than ${maxListedScreenshotAttempts} failed attempts. Only the ${maxListedScreenshotAttempts} most recent are listed, and the older attempts cannot be listed.`)
|
|
16957
|
+
});
|
|
16958
|
+
return {
|
|
16959
|
+
annotations: {
|
|
16960
|
+
destructiveHint: false,
|
|
16961
|
+
openWorldHint: false,
|
|
16962
|
+
readOnlyHint: false
|
|
16963
|
+
},
|
|
16964
|
+
description: "List the screenshot comparisons that failed a run's flows, with the expected, actual and diff images of each. " + "Only comparisons that caused a failure are listed: a screenshot that differs within tolerance, or that differs on a line that did not fail, is left out. " + "Each comparison tells whether its baseline changed since the run and whether the baseline still passes in other environments. " + "The image URLs expire after a few minutes; fetch the preview URLs to look at the images. " + "The first call for an image makes its missing JPEG preview and keeps it in team storage, so later calls reuse it.",
|
|
16965
|
+
input,
|
|
16966
|
+
kind: "read",
|
|
16967
|
+
name: "run.listScreenshotComparisons",
|
|
16968
|
+
output
|
|
16969
|
+
};
|
|
16970
|
+
};
|
|
16971
|
+
|
|
16906
16972
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/actionSequence.js
|
|
16907
16973
|
var maxActionsPerRequest = 10;
|
|
16908
16974
|
var screenshotModes = ["none", "final", "each"];
|
|
@@ -18968,11 +19034,22 @@ var makeGetIssueContract = (ids) => {
|
|
|
18968
19034
|
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.';
|
|
18969
19035
|
var makeInvestigationFindingSchema = (ids) => object({
|
|
18970
19036
|
actual: string2().optional(),
|
|
18971
|
-
commitHash: string2().optional(),
|
|
19037
|
+
commitHash: string2().optional().describe("The commit of a commit fix. Same as `fix.commitHash`."),
|
|
18972
19038
|
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."),
|
|
18973
19039
|
disputeReason: string2().optional().describe("Why a person disputed what the AI did. Present on a proposal the AI made again after that dispute."),
|
|
18974
19040
|
expected: string2().optional(),
|
|
18975
19041
|
findingId: string2(),
|
|
19042
|
+
fix: discriminatedUnion("type", [
|
|
19043
|
+
object({
|
|
19044
|
+
commitHash: string2().describe("The commit that changes the flow code."),
|
|
19045
|
+
type: literal("commit")
|
|
19046
|
+
}),
|
|
19047
|
+
object({
|
|
19048
|
+
baselineChangeId: string2().optional().describe('The baseline change that made the new screenshot the baseline, on a "fixed" finding. `run restoreScreenshotBaseline` undoes it.'),
|
|
19049
|
+
comparisonId: string2().describe("The failed screenshot comparison whose new screenshot becomes the baseline, as `run listScreenshotComparisons` names it."),
|
|
19050
|
+
type: literal("baseline")
|
|
19051
|
+
})
|
|
19052
|
+
]).optional().describe('The fix of a "fixed" or "fix-proposed" finding: a commit to the flow code, or a new screenshot baseline.'),
|
|
18976
19053
|
flowIds: array(ids.flow),
|
|
18977
19054
|
headline: string2().optional(),
|
|
18978
19055
|
issueId: ids.issue.optional(),
|
|
@@ -18983,8 +19060,9 @@ var makeInvestigationFindingSchema = (ids) => object({
|
|
|
18983
19060
|
answers: array(object({
|
|
18984
19061
|
description: string2().optional(),
|
|
18985
19062
|
title: string2(),
|
|
18986
|
-
value: string2().describe('What choosing this answer means. Known values: "apply", "dismiss", "report", "maintenance" and "
|
|
19063
|
+
value: string2().describe('What choosing this answer means. Known values: "apply", "dismiss", "report", "maintenance", "bug" and "baseline". New values can appear.')
|
|
18987
19064
|
})),
|
|
19065
|
+
comparisonId: string2().optional().describe('The failed screenshot comparison a screenshot question is about, as `run listScreenshotComparisons` names it. Its answers are "bug" (the old screenshot is right), "maintenance" (both are right) and "baseline" (the new screenshot is right).'),
|
|
18988
19066
|
text: string2()
|
|
18989
19067
|
}).optional().describe("The question waiting for a person, with the answers offered."),
|
|
18990
19068
|
response: object({
|
|
@@ -19004,6 +19082,7 @@ var makeInvestigationFindingSchema = (ids) => object({
|
|
|
19004
19082
|
var makeGetInvestigationContract = (ids) => {
|
|
19005
19083
|
const input = object({ runId: ids.run });
|
|
19006
19084
|
const output = resource({
|
|
19085
|
+
baselineChanges: _enum(["automatic", "needs-approval"]).describe('How the investigation can make a new screenshot the baseline. "automatic": it can accept a baseline on its own. ' + '"needs-approval": it proposes the baseline on a fix-proposed finding, and accepts it only after a person applies the proposal.'),
|
|
19007
19086
|
findings: array(makeInvestigationFindingSchema(ids)),
|
|
19008
19087
|
runId: ids.run,
|
|
19009
19088
|
sessionId: ids.chatSession.optional().describe("The investigation session. Absent when the run has none."),
|
|
@@ -19097,7 +19176,8 @@ var makeCreateIssueContract = (ids) => {
|
|
|
19097
19176
|
// node_modules/@qawolf/api-contracts/dist/v1/issue/removeFlows.js
|
|
19098
19177
|
var makeRemoveFlowsFromIssueContract = (ids) => {
|
|
19099
19178
|
const input = object({
|
|
19100
|
-
|
|
19179
|
+
environmentId: ids.environment.optional().describe("The environment whose bug or maintenance reproductions to remove. Required when a selected flow is linked in more than one environment."),
|
|
19180
|
+
flowIds: array(ids.flow).min(1).max(300).refine((flowIds) => new Set(flowIds).size === flowIds.length, "flowIds must be unique").describe("The flows to remove from the issue."),
|
|
19101
19181
|
issueId: ids.issue
|
|
19102
19182
|
});
|
|
19103
19183
|
const output = object({
|
|
@@ -19106,10 +19186,10 @@ var makeRemoveFlowsFromIssueContract = (ids) => {
|
|
|
19106
19186
|
return {
|
|
19107
19187
|
annotations: {
|
|
19108
19188
|
destructiveHint: true,
|
|
19109
|
-
openWorldHint:
|
|
19189
|
+
openWorldHint: true,
|
|
19110
19190
|
readOnlyHint: false
|
|
19111
19191
|
},
|
|
19112
|
-
description: "Remove flows from a coverage request
|
|
19192
|
+
description: "Remove flows from a coverage request or remove bug/maintenance reproductions in an environment. " + "Unlinked flows are left alone. Reports auto-close when no active reproductions remain and auto-close is enabled. " + "For reports, omit environmentId only when each selected flow is linked in at most one environment.",
|
|
19113
19193
|
input,
|
|
19114
19194
|
kind: "write",
|
|
19115
19195
|
name: "issue.removeFlows",
|
|
@@ -19269,6 +19349,49 @@ var makeLegacyTriggerContracts = (ids) => ({
|
|
|
19269
19349
|
resume: makeResumeLegacyTriggerContract(ids)
|
|
19270
19350
|
});
|
|
19271
19351
|
|
|
19352
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/investigationSessionId.js
|
|
19353
|
+
var investigationSessionId = string2().trim().min(1).max(100).optional();
|
|
19354
|
+
|
|
19355
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/acceptScreenshotBaseline.js
|
|
19356
|
+
var makeAcceptScreenshotBaselineContract = (ids) => {
|
|
19357
|
+
const input = object({
|
|
19358
|
+
chatSessionId: investigationSessionId.describe("The chat session of the investigation that accepts the baseline. A workspace or organization API key acts as an automation, so it must send the session that investigates the run. A person's call ignores it."),
|
|
19359
|
+
comparisonId: string2().trim().min(1).max(100).describe("The failed screenshot comparison whose new screenshot becomes the baseline, as `run.listScreenshotComparisons` names it."),
|
|
19360
|
+
runId: ids.run
|
|
19361
|
+
});
|
|
19362
|
+
const output = discriminatedUnion("outcome", [
|
|
19363
|
+
object({
|
|
19364
|
+
baselineChangeId: string2().describe("The baseline change. Record it on the fixed finding, and pass it to `run.restoreScreenshotBaseline` with this runId to put the old baseline back."),
|
|
19365
|
+
name: string2().describe("The screenshot name whose baseline changed."),
|
|
19366
|
+
outcome: literal("accepted").describe("The new screenshot is now the baseline. The old baseline is kept, so the change can be restored.")
|
|
19367
|
+
}),
|
|
19368
|
+
object({
|
|
19369
|
+
outcome: literal("baseline-changed").describe("The baseline is no longer the one the run compared with: someone changed or deleted it since. Nothing changed. List the comparisons again to see the current baseline.")
|
|
19370
|
+
}),
|
|
19371
|
+
object({
|
|
19372
|
+
outcome: literal("needs-approval").describe("Only a person can approve this change. Nothing changed. Propose the new baseline on a fix-proposed finding and wait for a person to apply it.")
|
|
19373
|
+
}),
|
|
19374
|
+
object({
|
|
19375
|
+
outcome: literal("not-investigation-session").describe("An API key that is not a person's can change a baseline only as the investigation of the run, and chatSessionId is missing or is not the session that investigates the run. Nothing changed.")
|
|
19376
|
+
}),
|
|
19377
|
+
object({
|
|
19378
|
+
outcome: literal("nothing-to-accept").describe("The comparison did not fail, or has no new screenshot to accept. Nothing changed.")
|
|
19379
|
+
})
|
|
19380
|
+
]);
|
|
19381
|
+
return {
|
|
19382
|
+
annotations: {
|
|
19383
|
+
destructiveHint: true,
|
|
19384
|
+
openWorldHint: false,
|
|
19385
|
+
readOnlyHint: false
|
|
19386
|
+
},
|
|
19387
|
+
description: "Make the new screenshot of a failed screenshot comparison the baseline, so later runs compare with it. " + "Use it only when the difference is an intended change of the app, not a bug, noise, or a page that was not ready. " + "Every later run of every environment of the workspace compares with the new baseline. " + "Refuses without changing anything when the baseline changed since the run.",
|
|
19388
|
+
input,
|
|
19389
|
+
kind: "write",
|
|
19390
|
+
name: "run.acceptScreenshotBaseline",
|
|
19391
|
+
output
|
|
19392
|
+
};
|
|
19393
|
+
};
|
|
19394
|
+
|
|
19272
19395
|
// node_modules/@qawolf/api-contracts/dist/v1/run/diagnose.js
|
|
19273
19396
|
var makeDiagnoseRunContract = (ids) => {
|
|
19274
19397
|
const input = object({
|
|
@@ -19285,7 +19408,7 @@ var makeDiagnoseRunContract = (ids) => {
|
|
|
19285
19408
|
openWorldHint: true,
|
|
19286
19409
|
readOnlyHint: false
|
|
19287
19410
|
},
|
|
19288
|
-
description: "Diagnose failed flows in a run as reproductions of a bug or maintenance report owned by the caller's team. " + "The issue's type selects the diagnosis. " + "Each flow must have failed in the run. " + "A flow that is already diagnosed moves to this issue. " + "The diagnosis appears on the run, and the reproduction appears under the issue's reproductions. " + "Coverage requests cannot be diagnosed; use issue.addFlows to cover flows instead.",
|
|
19411
|
+
description: "Diagnose failed flows in a run as reproductions of a bug or maintenance report owned by the caller's team. " + "The issue's type selects the diagnosis. " + "Each flow must have failed in the run. " + "A flow that is already diagnosed in the run moves to this issue, and may stay linked to other open reports. " + "The diagnosis appears on the run, and the reproduction appears under the issue's reproductions. " + "Coverage requests cannot be diagnosed; use issue.addFlows to cover flows instead.",
|
|
19289
19412
|
input,
|
|
19290
19413
|
kind: "write",
|
|
19291
19414
|
name: "run.diagnose",
|
|
@@ -19323,6 +19446,22 @@ var makeFindRunsContract = (ids) => {
|
|
|
19323
19446
|
};
|
|
19324
19447
|
};
|
|
19325
19448
|
|
|
19449
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/flowFailure.js
|
|
19450
|
+
var makeFlowFailureSchema = (ids) => {
|
|
19451
|
+
const diagnosis = object({
|
|
19452
|
+
issueId: ids.issue,
|
|
19453
|
+
type: _enum(["bug", "maintenance"])
|
|
19454
|
+
}).optional().describe("QA Wolf's investigation verdict for the failure: `bug` means the " + "application is broken, `maintenance` means the test needed an " + "update and the failure does not indicate an application problem. " + "Absent until the investigation reaches a verdict, and absent when " + "the flow was opted out of investigation. Pass issueId to issue.get " + "for details.");
|
|
19455
|
+
const optedOutOfInvestigation = object({
|
|
19456
|
+
by: _enum(["user", "system"]).describe("Who stopped the investigation: `user` is a person, in the app or " + "with a user API key, or the workspace's automation user for a " + "team or organization API key, for example through " + "run.optOutOfInvestigation; `system` is QA Wolf, when the " + "investigation expired.")
|
|
19457
|
+
}).optional().describe('Present when the failure was marked "do not investigate" (DNI): ' + "it will get no bug or maintenance verdict.");
|
|
19458
|
+
return object({
|
|
19459
|
+
diagnosis,
|
|
19460
|
+
error: string2(),
|
|
19461
|
+
optedOutOfInvestigation
|
|
19462
|
+
});
|
|
19463
|
+
};
|
|
19464
|
+
|
|
19326
19465
|
// node_modules/@qawolf/api-contracts/dist/v1/run/get.js
|
|
19327
19466
|
var makeGetRunContract = (ids) => {
|
|
19328
19467
|
const flowStatus = _enum(publicRunStatusValues);
|
|
@@ -19370,16 +19509,9 @@ var makeGetRunContract = (ids) => {
|
|
|
19370
19509
|
});
|
|
19371
19510
|
const attempt = union([automatedAttempt, manualAttempt]);
|
|
19372
19511
|
const attempts = array(attempt).optional().describe("The flow's finished execution attempts, oldest first, including " + "manual Wolf Browser attempts. Present once at least one attempt has " + "finished, so a flow that passed after retries also lists its failed " + "attempts. Artifact URLs appear only on automated attempts that " + "reached a verdict, stay valid for at least a day (call run.get " + "again for fresh ones), and can return 404 when the attempt did not " + "produce that artifact.");
|
|
19373
|
-
const diagnosis = object({
|
|
19374
|
-
issueId: ids.issue,
|
|
19375
|
-
type: _enum(["bug", "maintenance"])
|
|
19376
|
-
}).optional().describe("QA Wolf's investigation verdict for the failure: `bug` means the " + "application is broken, `maintenance` means the test needed an " + "update and the failure does not indicate an application problem. " + "Absent until the investigation reaches a verdict. Pass issueId to " + "issue.get for details.");
|
|
19377
19512
|
const failedFlow = object({
|
|
19378
19513
|
attempts,
|
|
19379
|
-
failure:
|
|
19380
|
-
diagnosis,
|
|
19381
|
-
error: string2()
|
|
19382
|
-
}),
|
|
19514
|
+
failure: makeFlowFailureSchema(ids),
|
|
19383
19515
|
flowId: ids.flow,
|
|
19384
19516
|
name: string2(),
|
|
19385
19517
|
status: literal("failed")
|
|
@@ -19488,6 +19620,30 @@ var makeGetRunAttemptArtifactsContract = (ids) => {
|
|
|
19488
19620
|
};
|
|
19489
19621
|
};
|
|
19490
19622
|
|
|
19623
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/optOutOfInvestigation.js
|
|
19624
|
+
var makeOptOutOfInvestigationRunContract = (ids) => {
|
|
19625
|
+
const input = object({
|
|
19626
|
+
flowIds: array(ids.flow).min(1).max(100).refine((flowIds) => new Set(flowIds).size === flowIds.length, "flowIds must be unique").describe("The failed flows to stop investigating. Every named flow must be " + "eligible or the whole request is rejected."),
|
|
19627
|
+
runId: ids.run
|
|
19628
|
+
});
|
|
19629
|
+
const output = resource({
|
|
19630
|
+
optedOutFlowIds: array(ids.flow).describe("The flows that no longer need investigation."),
|
|
19631
|
+
runId: ids.run
|
|
19632
|
+
}, { urlFieldDescription: "Absolute URL of the run page." });
|
|
19633
|
+
return {
|
|
19634
|
+
annotations: {
|
|
19635
|
+
destructiveHint: true,
|
|
19636
|
+
openWorldHint: true,
|
|
19637
|
+
readOnlyHint: false
|
|
19638
|
+
},
|
|
19639
|
+
description: 'Mark failed flows in a run as "do not investigate" (DNI): the failures need no bug or maintenance report. ' + "Each flow must have failed in the run and still be under investigation. " + "A flow that already has a diagnosis cannot be opted out. " + "A team or organization key records the opt-out as the workspace's automation user. " + "To link a failure to a report instead, use run.diagnose.",
|
|
19640
|
+
input,
|
|
19641
|
+
kind: "write",
|
|
19642
|
+
name: "run.optOutOfInvestigation",
|
|
19643
|
+
output
|
|
19644
|
+
};
|
|
19645
|
+
};
|
|
19646
|
+
|
|
19491
19647
|
// node_modules/@qawolf/api-contracts/dist/v1/run/reattempt.js
|
|
19492
19648
|
var makeReattemptRunContract = (ids) => {
|
|
19493
19649
|
const input = object({
|
|
@@ -19512,6 +19668,45 @@ var makeReattemptRunContract = (ids) => {
|
|
|
19512
19668
|
};
|
|
19513
19669
|
};
|
|
19514
19670
|
|
|
19671
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/restoreScreenshotBaseline.js
|
|
19672
|
+
var makeRestoreScreenshotBaselineContract = (ids) => {
|
|
19673
|
+
const input = object({
|
|
19674
|
+
baselineChangeId: string2().trim().min(1).max(100).describe("The baseline change to undo, as `run.acceptScreenshotBaseline` returned it for this run."),
|
|
19675
|
+
chatSessionId: investigationSessionId.describe("The chat session of the investigation that restores the baseline. A workspace or organization API key acts as an automation, so it must send the session that investigates the run. A person's call ignores it."),
|
|
19676
|
+
runId: ids.run
|
|
19677
|
+
});
|
|
19678
|
+
const output = discriminatedUnion("outcome", [
|
|
19679
|
+
object({
|
|
19680
|
+
name: string2().describe("The screenshot name whose baseline changed."),
|
|
19681
|
+
outcome: literal("restored").describe("The baseline the change replaced is the baseline again.")
|
|
19682
|
+
}),
|
|
19683
|
+
object({
|
|
19684
|
+
outcome: literal("baseline-changed").describe("The baseline changed again after this change, so restoring it would undo someone else's change. Nothing changed.")
|
|
19685
|
+
}),
|
|
19686
|
+
object({
|
|
19687
|
+
outcome: literal("needs-dispute").describe("Only a person can restore this change, unless a person disputed the finding that made it. Nothing changed.")
|
|
19688
|
+
}),
|
|
19689
|
+
object({
|
|
19690
|
+
outcome: literal("not-investigation-session").describe("An API key that is not a person's can change a baseline only as the investigation of the run, and chatSessionId is missing or is not the session that investigates the run. Nothing changed.")
|
|
19691
|
+
}),
|
|
19692
|
+
object({
|
|
19693
|
+
outcome: literal("nothing-to-restore").describe("The change was already restored, the name had no baseline before it, or the kept copy of the old baseline is missing or is not the baseline the change replaced. Nothing changed.")
|
|
19694
|
+
})
|
|
19695
|
+
]);
|
|
19696
|
+
return {
|
|
19697
|
+
annotations: {
|
|
19698
|
+
destructiveHint: true,
|
|
19699
|
+
openWorldHint: false,
|
|
19700
|
+
readOnlyHint: false
|
|
19701
|
+
},
|
|
19702
|
+
description: "Undo a screenshot baseline change that accepted a failed comparison of the run: put back the baseline it replaced, so later runs compare with the old baseline again. " + "Use it when a person disputes a baseline the AI accepted. " + "Refuses without changing anything when the baseline changed again since.",
|
|
19703
|
+
input,
|
|
19704
|
+
kind: "write",
|
|
19705
|
+
name: "run.restoreScreenshotBaseline",
|
|
19706
|
+
output
|
|
19707
|
+
};
|
|
19708
|
+
};
|
|
19709
|
+
|
|
19515
19710
|
// node_modules/@qawolf/api-contracts/dist/v1/run/stop.js
|
|
19516
19711
|
var makeStopRunContract = (ids) => {
|
|
19517
19712
|
const input = object({ runId: ids.run });
|
|
@@ -19832,7 +20027,7 @@ var makeRunFlowOnRunnerContract = (ids) => {
|
|
|
19832
20027
|
env: runEnvironmentSchema.optional().describe("Environment variables to make available to the run."),
|
|
19833
20028
|
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."),
|
|
19834
20029
|
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."),
|
|
19835
|
-
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.
|
|
20030
|
+
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. Omitted, the flow whose file is at `entryPointPath` on the named environment's flow-code branch stands in; a file not in QA Wolf yet, or a run given no environment, leaves the run with no `QAWOLF_WORKFLOW_ID`."),
|
|
19836
20031
|
id: runnerIdSchema.describe("Id of the runner to run the flow on."),
|
|
19837
20032
|
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."),
|
|
19838
20033
|
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."),
|
|
@@ -20099,12 +20294,16 @@ var makeContractsV1 = (ids) => {
|
|
|
20099
20294
|
},
|
|
20100
20295
|
legacyTrigger: makeLegacyTriggerContracts(resolvedIds),
|
|
20101
20296
|
run: {
|
|
20297
|
+
acceptScreenshotBaseline: makeAcceptScreenshotBaselineContract(resolvedIds),
|
|
20102
20298
|
create: makeCreateRunContract(resolvedIds),
|
|
20103
20299
|
diagnose: makeDiagnoseRunContract(resolvedIds),
|
|
20104
20300
|
find: makeFindRunsContract(resolvedIds),
|
|
20105
20301
|
get: makeGetRunContract(resolvedIds),
|
|
20106
20302
|
getAttemptArtifacts: makeGetRunAttemptArtifactsContract(resolvedIds),
|
|
20303
|
+
listScreenshotComparisons: makeListScreenshotComparisonsContract(resolvedIds),
|
|
20304
|
+
optOutOfInvestigation: makeOptOutOfInvestigationRunContract(resolvedIds),
|
|
20107
20305
|
reattempt: makeReattemptRunContract(resolvedIds),
|
|
20306
|
+
restoreScreenshotBaseline: makeRestoreScreenshotBaselineContract(resolvedIds),
|
|
20108
20307
|
stop: makeStopRunContract(resolvedIds)
|
|
20109
20308
|
},
|
|
20110
20309
|
runner: makeRunnerContracts(resolvedIds),
|
|
@@ -22621,7 +22820,7 @@ function startUpdateCheck(deps) {
|
|
|
22621
22820
|
// package.json
|
|
22622
22821
|
var package_default = {
|
|
22623
22822
|
name: "@qawolf/cli",
|
|
22624
|
-
version: "1.
|
|
22823
|
+
version: "1.38.0",
|
|
22625
22824
|
description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
22626
22825
|
keywords: [
|
|
22627
22826
|
"automation",
|
|
@@ -22692,7 +22891,7 @@ var package_default = {
|
|
|
22692
22891
|
"@clack/prompts": "1.5.1",
|
|
22693
22892
|
"@napi-rs/keyring": "1.3.0",
|
|
22694
22893
|
"@oxc-node/core": "0.1.0",
|
|
22695
|
-
"@qawolf/api-contracts": "0.
|
|
22894
|
+
"@qawolf/api-contracts": "0.77.0",
|
|
22696
22895
|
"@qawolf/emails": "1.1.1",
|
|
22697
22896
|
"@qawolf/flow-targets": "1.0.0",
|
|
22698
22897
|
"@qawolf/flows": "0.1.4",
|
|
@@ -38943,4 +39142,4 @@ createProgram({ signals }).parseAsync().catch(() => {
|
|
|
38943
39142
|
process.exitCode = 1;
|
|
38944
39143
|
}).finally(() => exitWhenIdle(typeof process.exitCode === "number" ? process.exitCode : 0));
|
|
38945
39144
|
|
|
38946
|
-
//# debugId=
|
|
39145
|
+
//# debugId=4535AB8728B9D01A64756E2164756E21
|
package/dist/runner-sdk.js
CHANGED
|
@@ -7265,6 +7265,72 @@ var makeCreateRunContract = (ids) => {
|
|
|
7265
7265
|
};
|
|
7266
7266
|
};
|
|
7267
7267
|
|
|
7268
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/listScreenshotComparisons.js
|
|
7269
|
+
var maxListedScreenshotAttempts = 20;
|
|
7270
|
+
var makeListScreenshotComparisonsContract = (ids) => {
|
|
7271
|
+
const input = object({
|
|
7272
|
+
flowIds: array(ids.flow).min(1).max(100).refine((flowIds) => new Set(flowIds).size === flowIds.length, "flowIds must be unique").optional().describe("Only list the comparisons of these flows. Each flow must have a result in the run. Omit to list every failed flow of the run."),
|
|
7273
|
+
runId: ids.run
|
|
7274
|
+
});
|
|
7275
|
+
const imageUrl = (image) => url().describe(`Signed URL of the full-size ${image} PNG. It stops working at expiresAt.`);
|
|
7276
|
+
const previewUrl = (image) => url().optional().describe(`Signed URL of a JPEG copy of the ${image} image, scaled down so its long edge is at most 1568 pixels. ` + "Fetch this one to look at the image: it is small enough to view inline, while the full-size PNG can be too large. " + "It stops working at expiresAt. Absent when QA Wolf could not make the copy, for example because the full-size image is gone.");
|
|
7277
|
+
const passedEnvironment = object({
|
|
7278
|
+
environmentId: ids.environment,
|
|
7279
|
+
lastPassedAt: exports_iso.datetime().describe("When the latest passing comparison in this environment ran.")
|
|
7280
|
+
});
|
|
7281
|
+
const passingElsewhere = discriminatedUnion("status", [
|
|
7282
|
+
object({
|
|
7283
|
+
status: literal("not-tracked").describe("QA Wolf does not record screenshot comparisons for this workspace, so it cannot tell.")
|
|
7284
|
+
}),
|
|
7285
|
+
object({
|
|
7286
|
+
environments: array(passedEnvironment).describe("Other environments where the current baseline passed this screenshot in the last 14 days, most recent first. " + "Empty when it passed nowhere else, or when the baseline no longer exists."),
|
|
7287
|
+
status: literal("tracked")
|
|
7288
|
+
})
|
|
7289
|
+
]).describe("Whether the current baseline still passes in other environments. " + "A baseline that passes elsewhere points to a difference in this environment rather than an outdated baseline.");
|
|
7290
|
+
const comparison = object({
|
|
7291
|
+
actualPreviewUrl: previewUrl("actual"),
|
|
7292
|
+
actualUrl: imageUrl("actual (the screenshot the flow took)"),
|
|
7293
|
+
attemptCompletedAt: exports_iso.datetime().describe("When the run attempt that made the comparison finished."),
|
|
7294
|
+
baselineStatus: _enum([
|
|
7295
|
+
"unchanged",
|
|
7296
|
+
"accepted",
|
|
7297
|
+
"changed-since-run",
|
|
7298
|
+
"deleted",
|
|
7299
|
+
"unknown"
|
|
7300
|
+
]).describe('How the current baseline relates to the expected image this comparison used. "unchanged": the baseline is still the expected image. ' + `"accepted": the baseline is now this comparison's actual screenshot, because someone accepted it, so a reattempt should pass. ` + '"changed-since-run": someone replaced the baseline with another image after this run, so rerun the flow before deciding anything. ' + '"deleted": the baseline no longer exists, and the next run will save its screenshot as the new baseline. ' + `"unknown": the run's copy of the expected image is gone, so QA Wolf cannot compare.`),
|
|
7301
|
+
comparisonId: string2().describe("Identifies this comparison: the run attempt id and the position of the comparison in that attempt."),
|
|
7302
|
+
diffPreviewUrl: previewUrl("diff"),
|
|
7303
|
+
diffUrl: imageUrl("diff (the pixels that differ are highlighted)"),
|
|
7304
|
+
expectedPreviewUrl: previewUrl("expected"),
|
|
7305
|
+
expectedUrl: imageUrl("expected (the baseline as it was when the flow ran)"),
|
|
7306
|
+
filePath: string2().optional().describe("The flow file that made the comparison."),
|
|
7307
|
+
flowId: ids.flow,
|
|
7308
|
+
lineNumber: number2().int().optional().describe("The line of filePath that made the comparison."),
|
|
7309
|
+
name: string2().describe("The screenshot name the flow passed to toHaveScreenshot. The baseline is stored as _screenshots_/<name>.png in team storage."),
|
|
7310
|
+
passingElsewhere,
|
|
7311
|
+
runAttemptId: string2(),
|
|
7312
|
+
similarity: number2().describe("How much of the actual image matches the expected image, in percent."),
|
|
7313
|
+
sizeMismatch: boolean2().describe("The actual and expected images have different dimensions. That fails the comparison whatever the pixels show, and similarity reads 0.")
|
|
7314
|
+
});
|
|
7315
|
+
const output = object({
|
|
7316
|
+
comparisons: array(comparison).describe(`Each screenshot comparison that failed its flow, from the ${maxListedScreenshotAttempts} most recent failed attempts of the selected flows, oldest attempt first.`),
|
|
7317
|
+
expiresAt: exports_iso.datetime().describe("When the image URLs stop working. Call run.listScreenshotComparisons again for fresh ones after it."),
|
|
7318
|
+
truncated: boolean2().describe(`True when the selected flows have more than ${maxListedScreenshotAttempts} failed attempts. Only the ${maxListedScreenshotAttempts} most recent are listed, and the older attempts cannot be listed.`)
|
|
7319
|
+
});
|
|
7320
|
+
return {
|
|
7321
|
+
annotations: {
|
|
7322
|
+
destructiveHint: false,
|
|
7323
|
+
openWorldHint: false,
|
|
7324
|
+
readOnlyHint: false
|
|
7325
|
+
},
|
|
7326
|
+
description: "List the screenshot comparisons that failed a run's flows, with the expected, actual and diff images of each. " + "Only comparisons that caused a failure are listed: a screenshot that differs within tolerance, or that differs on a line that did not fail, is left out. " + "Each comparison tells whether its baseline changed since the run and whether the baseline still passes in other environments. " + "The image URLs expire after a few minutes; fetch the preview URLs to look at the images. " + "The first call for an image makes its missing JPEG preview and keeps it in team storage, so later calls reuse it.",
|
|
7327
|
+
input,
|
|
7328
|
+
kind: "read",
|
|
7329
|
+
name: "run.listScreenshotComparisons",
|
|
7330
|
+
output
|
|
7331
|
+
};
|
|
7332
|
+
};
|
|
7333
|
+
|
|
7268
7334
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/actionSequence.js
|
|
7269
7335
|
var maxActionsPerRequest = 10;
|
|
7270
7336
|
var screenshotModes = ["none", "final", "each"];
|
|
@@ -9337,11 +9403,22 @@ var makeGetIssueContract = (ids) => {
|
|
|
9337
9403
|
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.';
|
|
9338
9404
|
var makeInvestigationFindingSchema = (ids) => object({
|
|
9339
9405
|
actual: string2().optional(),
|
|
9340
|
-
commitHash: string2().optional(),
|
|
9406
|
+
commitHash: string2().optional().describe("The commit of a commit fix. Same as `fix.commitHash`."),
|
|
9341
9407
|
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."),
|
|
9342
9408
|
disputeReason: string2().optional().describe("Why a person disputed what the AI did. Present on a proposal the AI made again after that dispute."),
|
|
9343
9409
|
expected: string2().optional(),
|
|
9344
9410
|
findingId: string2(),
|
|
9411
|
+
fix: discriminatedUnion("type", [
|
|
9412
|
+
object({
|
|
9413
|
+
commitHash: string2().describe("The commit that changes the flow code."),
|
|
9414
|
+
type: literal("commit")
|
|
9415
|
+
}),
|
|
9416
|
+
object({
|
|
9417
|
+
baselineChangeId: string2().optional().describe('The baseline change that made the new screenshot the baseline, on a "fixed" finding. `run restoreScreenshotBaseline` undoes it.'),
|
|
9418
|
+
comparisonId: string2().describe("The failed screenshot comparison whose new screenshot becomes the baseline, as `run listScreenshotComparisons` names it."),
|
|
9419
|
+
type: literal("baseline")
|
|
9420
|
+
})
|
|
9421
|
+
]).optional().describe('The fix of a "fixed" or "fix-proposed" finding: a commit to the flow code, or a new screenshot baseline.'),
|
|
9345
9422
|
flowIds: array(ids.flow),
|
|
9346
9423
|
headline: string2().optional(),
|
|
9347
9424
|
issueId: ids.issue.optional(),
|
|
@@ -9352,8 +9429,9 @@ var makeInvestigationFindingSchema = (ids) => object({
|
|
|
9352
9429
|
answers: array(object({
|
|
9353
9430
|
description: string2().optional(),
|
|
9354
9431
|
title: string2(),
|
|
9355
|
-
value: string2().describe('What choosing this answer means. Known values: "apply", "dismiss", "report", "maintenance" and "
|
|
9432
|
+
value: string2().describe('What choosing this answer means. Known values: "apply", "dismiss", "report", "maintenance", "bug" and "baseline". New values can appear.')
|
|
9356
9433
|
})),
|
|
9434
|
+
comparisonId: string2().optional().describe('The failed screenshot comparison a screenshot question is about, as `run listScreenshotComparisons` names it. Its answers are "bug" (the old screenshot is right), "maintenance" (both are right) and "baseline" (the new screenshot is right).'),
|
|
9357
9435
|
text: string2()
|
|
9358
9436
|
}).optional().describe("The question waiting for a person, with the answers offered."),
|
|
9359
9437
|
response: object({
|
|
@@ -9373,6 +9451,7 @@ var makeInvestigationFindingSchema = (ids) => object({
|
|
|
9373
9451
|
var makeGetInvestigationContract = (ids) => {
|
|
9374
9452
|
const input = object({ runId: ids.run });
|
|
9375
9453
|
const output = resource({
|
|
9454
|
+
baselineChanges: _enum(["automatic", "needs-approval"]).describe('How the investigation can make a new screenshot the baseline. "automatic": it can accept a baseline on its own. ' + '"needs-approval": it proposes the baseline on a fix-proposed finding, and accepts it only after a person applies the proposal.'),
|
|
9376
9455
|
findings: array(makeInvestigationFindingSchema(ids)),
|
|
9377
9456
|
runId: ids.run,
|
|
9378
9457
|
sessionId: ids.chatSession.optional().describe("The investigation session. Absent when the run has none."),
|
|
@@ -9466,7 +9545,8 @@ var makeCreateIssueContract = (ids) => {
|
|
|
9466
9545
|
// node_modules/@qawolf/api-contracts/dist/v1/issue/removeFlows.js
|
|
9467
9546
|
var makeRemoveFlowsFromIssueContract = (ids) => {
|
|
9468
9547
|
const input = object({
|
|
9469
|
-
|
|
9548
|
+
environmentId: ids.environment.optional().describe("The environment whose bug or maintenance reproductions to remove. Required when a selected flow is linked in more than one environment."),
|
|
9549
|
+
flowIds: array(ids.flow).min(1).max(300).refine((flowIds) => new Set(flowIds).size === flowIds.length, "flowIds must be unique").describe("The flows to remove from the issue."),
|
|
9470
9550
|
issueId: ids.issue
|
|
9471
9551
|
});
|
|
9472
9552
|
const output = object({
|
|
@@ -9475,10 +9555,10 @@ var makeRemoveFlowsFromIssueContract = (ids) => {
|
|
|
9475
9555
|
return {
|
|
9476
9556
|
annotations: {
|
|
9477
9557
|
destructiveHint: true,
|
|
9478
|
-
openWorldHint:
|
|
9558
|
+
openWorldHint: true,
|
|
9479
9559
|
readOnlyHint: false
|
|
9480
9560
|
},
|
|
9481
|
-
description: "Remove flows from a coverage request
|
|
9561
|
+
description: "Remove flows from a coverage request or remove bug/maintenance reproductions in an environment. " + "Unlinked flows are left alone. Reports auto-close when no active reproductions remain and auto-close is enabled. " + "For reports, omit environmentId only when each selected flow is linked in at most one environment.",
|
|
9482
9562
|
input,
|
|
9483
9563
|
kind: "write",
|
|
9484
9564
|
name: "issue.removeFlows",
|
|
@@ -9638,6 +9718,49 @@ var makeLegacyTriggerContracts = (ids) => ({
|
|
|
9638
9718
|
resume: makeResumeLegacyTriggerContract(ids)
|
|
9639
9719
|
});
|
|
9640
9720
|
|
|
9721
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/investigationSessionId.js
|
|
9722
|
+
var investigationSessionId = string2().trim().min(1).max(100).optional();
|
|
9723
|
+
|
|
9724
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/acceptScreenshotBaseline.js
|
|
9725
|
+
var makeAcceptScreenshotBaselineContract = (ids) => {
|
|
9726
|
+
const input = object({
|
|
9727
|
+
chatSessionId: investigationSessionId.describe("The chat session of the investigation that accepts the baseline. A workspace or organization API key acts as an automation, so it must send the session that investigates the run. A person's call ignores it."),
|
|
9728
|
+
comparisonId: string2().trim().min(1).max(100).describe("The failed screenshot comparison whose new screenshot becomes the baseline, as `run.listScreenshotComparisons` names it."),
|
|
9729
|
+
runId: ids.run
|
|
9730
|
+
});
|
|
9731
|
+
const output = discriminatedUnion("outcome", [
|
|
9732
|
+
object({
|
|
9733
|
+
baselineChangeId: string2().describe("The baseline change. Record it on the fixed finding, and pass it to `run.restoreScreenshotBaseline` with this runId to put the old baseline back."),
|
|
9734
|
+
name: string2().describe("The screenshot name whose baseline changed."),
|
|
9735
|
+
outcome: literal("accepted").describe("The new screenshot is now the baseline. The old baseline is kept, so the change can be restored.")
|
|
9736
|
+
}),
|
|
9737
|
+
object({
|
|
9738
|
+
outcome: literal("baseline-changed").describe("The baseline is no longer the one the run compared with: someone changed or deleted it since. Nothing changed. List the comparisons again to see the current baseline.")
|
|
9739
|
+
}),
|
|
9740
|
+
object({
|
|
9741
|
+
outcome: literal("needs-approval").describe("Only a person can approve this change. Nothing changed. Propose the new baseline on a fix-proposed finding and wait for a person to apply it.")
|
|
9742
|
+
}),
|
|
9743
|
+
object({
|
|
9744
|
+
outcome: literal("not-investigation-session").describe("An API key that is not a person's can change a baseline only as the investigation of the run, and chatSessionId is missing or is not the session that investigates the run. Nothing changed.")
|
|
9745
|
+
}),
|
|
9746
|
+
object({
|
|
9747
|
+
outcome: literal("nothing-to-accept").describe("The comparison did not fail, or has no new screenshot to accept. Nothing changed.")
|
|
9748
|
+
})
|
|
9749
|
+
]);
|
|
9750
|
+
return {
|
|
9751
|
+
annotations: {
|
|
9752
|
+
destructiveHint: true,
|
|
9753
|
+
openWorldHint: false,
|
|
9754
|
+
readOnlyHint: false
|
|
9755
|
+
},
|
|
9756
|
+
description: "Make the new screenshot of a failed screenshot comparison the baseline, so later runs compare with it. " + "Use it only when the difference is an intended change of the app, not a bug, noise, or a page that was not ready. " + "Every later run of every environment of the workspace compares with the new baseline. " + "Refuses without changing anything when the baseline changed since the run.",
|
|
9757
|
+
input,
|
|
9758
|
+
kind: "write",
|
|
9759
|
+
name: "run.acceptScreenshotBaseline",
|
|
9760
|
+
output
|
|
9761
|
+
};
|
|
9762
|
+
};
|
|
9763
|
+
|
|
9641
9764
|
// node_modules/@qawolf/api-contracts/dist/v1/run/diagnose.js
|
|
9642
9765
|
var makeDiagnoseRunContract = (ids) => {
|
|
9643
9766
|
const input = object({
|
|
@@ -9654,7 +9777,7 @@ var makeDiagnoseRunContract = (ids) => {
|
|
|
9654
9777
|
openWorldHint: true,
|
|
9655
9778
|
readOnlyHint: false
|
|
9656
9779
|
},
|
|
9657
|
-
description: "Diagnose failed flows in a run as reproductions of a bug or maintenance report owned by the caller's team. " + "The issue's type selects the diagnosis. " + "Each flow must have failed in the run. " + "A flow that is already diagnosed moves to this issue. " + "The diagnosis appears on the run, and the reproduction appears under the issue's reproductions. " + "Coverage requests cannot be diagnosed; use issue.addFlows to cover flows instead.",
|
|
9780
|
+
description: "Diagnose failed flows in a run as reproductions of a bug or maintenance report owned by the caller's team. " + "The issue's type selects the diagnosis. " + "Each flow must have failed in the run. " + "A flow that is already diagnosed in the run moves to this issue, and may stay linked to other open reports. " + "The diagnosis appears on the run, and the reproduction appears under the issue's reproductions. " + "Coverage requests cannot be diagnosed; use issue.addFlows to cover flows instead.",
|
|
9658
9781
|
input,
|
|
9659
9782
|
kind: "write",
|
|
9660
9783
|
name: "run.diagnose",
|
|
@@ -9692,6 +9815,22 @@ var makeFindRunsContract = (ids) => {
|
|
|
9692
9815
|
};
|
|
9693
9816
|
};
|
|
9694
9817
|
|
|
9818
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/flowFailure.js
|
|
9819
|
+
var makeFlowFailureSchema = (ids) => {
|
|
9820
|
+
const diagnosis = object({
|
|
9821
|
+
issueId: ids.issue,
|
|
9822
|
+
type: _enum(["bug", "maintenance"])
|
|
9823
|
+
}).optional().describe("QA Wolf's investigation verdict for the failure: `bug` means the " + "application is broken, `maintenance` means the test needed an " + "update and the failure does not indicate an application problem. " + "Absent until the investigation reaches a verdict, and absent when " + "the flow was opted out of investigation. Pass issueId to issue.get " + "for details.");
|
|
9824
|
+
const optedOutOfInvestigation = object({
|
|
9825
|
+
by: _enum(["user", "system"]).describe("Who stopped the investigation: `user` is a person, in the app or " + "with a user API key, or the workspace's automation user for a " + "team or organization API key, for example through " + "run.optOutOfInvestigation; `system` is QA Wolf, when the " + "investigation expired.")
|
|
9826
|
+
}).optional().describe('Present when the failure was marked "do not investigate" (DNI): ' + "it will get no bug or maintenance verdict.");
|
|
9827
|
+
return object({
|
|
9828
|
+
diagnosis,
|
|
9829
|
+
error: string2(),
|
|
9830
|
+
optedOutOfInvestigation
|
|
9831
|
+
});
|
|
9832
|
+
};
|
|
9833
|
+
|
|
9695
9834
|
// node_modules/@qawolf/api-contracts/dist/v1/run/get.js
|
|
9696
9835
|
var makeGetRunContract = (ids) => {
|
|
9697
9836
|
const flowStatus = _enum(publicRunStatusValues);
|
|
@@ -9739,16 +9878,9 @@ var makeGetRunContract = (ids) => {
|
|
|
9739
9878
|
});
|
|
9740
9879
|
const attempt = union([automatedAttempt, manualAttempt]);
|
|
9741
9880
|
const attempts = array(attempt).optional().describe("The flow's finished execution attempts, oldest first, including " + "manual Wolf Browser attempts. Present once at least one attempt has " + "finished, so a flow that passed after retries also lists its failed " + "attempts. Artifact URLs appear only on automated attempts that " + "reached a verdict, stay valid for at least a day (call run.get " + "again for fresh ones), and can return 404 when the attempt did not " + "produce that artifact.");
|
|
9742
|
-
const diagnosis = object({
|
|
9743
|
-
issueId: ids.issue,
|
|
9744
|
-
type: _enum(["bug", "maintenance"])
|
|
9745
|
-
}).optional().describe("QA Wolf's investigation verdict for the failure: `bug` means the " + "application is broken, `maintenance` means the test needed an " + "update and the failure does not indicate an application problem. " + "Absent until the investigation reaches a verdict. Pass issueId to " + "issue.get for details.");
|
|
9746
9881
|
const failedFlow = object({
|
|
9747
9882
|
attempts,
|
|
9748
|
-
failure:
|
|
9749
|
-
diagnosis,
|
|
9750
|
-
error: string2()
|
|
9751
|
-
}),
|
|
9883
|
+
failure: makeFlowFailureSchema(ids),
|
|
9752
9884
|
flowId: ids.flow,
|
|
9753
9885
|
name: string2(),
|
|
9754
9886
|
status: literal("failed")
|
|
@@ -9857,6 +9989,30 @@ var makeGetRunAttemptArtifactsContract = (ids) => {
|
|
|
9857
9989
|
};
|
|
9858
9990
|
};
|
|
9859
9991
|
|
|
9992
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/optOutOfInvestigation.js
|
|
9993
|
+
var makeOptOutOfInvestigationRunContract = (ids) => {
|
|
9994
|
+
const input = object({
|
|
9995
|
+
flowIds: array(ids.flow).min(1).max(100).refine((flowIds) => new Set(flowIds).size === flowIds.length, "flowIds must be unique").describe("The failed flows to stop investigating. Every named flow must be " + "eligible or the whole request is rejected."),
|
|
9996
|
+
runId: ids.run
|
|
9997
|
+
});
|
|
9998
|
+
const output = resource({
|
|
9999
|
+
optedOutFlowIds: array(ids.flow).describe("The flows that no longer need investigation."),
|
|
10000
|
+
runId: ids.run
|
|
10001
|
+
}, { urlFieldDescription: "Absolute URL of the run page." });
|
|
10002
|
+
return {
|
|
10003
|
+
annotations: {
|
|
10004
|
+
destructiveHint: true,
|
|
10005
|
+
openWorldHint: true,
|
|
10006
|
+
readOnlyHint: false
|
|
10007
|
+
},
|
|
10008
|
+
description: 'Mark failed flows in a run as "do not investigate" (DNI): the failures need no bug or maintenance report. ' + "Each flow must have failed in the run and still be under investigation. " + "A flow that already has a diagnosis cannot be opted out. " + "A team or organization key records the opt-out as the workspace's automation user. " + "To link a failure to a report instead, use run.diagnose.",
|
|
10009
|
+
input,
|
|
10010
|
+
kind: "write",
|
|
10011
|
+
name: "run.optOutOfInvestigation",
|
|
10012
|
+
output
|
|
10013
|
+
};
|
|
10014
|
+
};
|
|
10015
|
+
|
|
9860
10016
|
// node_modules/@qawolf/api-contracts/dist/v1/run/reattempt.js
|
|
9861
10017
|
var makeReattemptRunContract = (ids) => {
|
|
9862
10018
|
const input = object({
|
|
@@ -9881,6 +10037,45 @@ var makeReattemptRunContract = (ids) => {
|
|
|
9881
10037
|
};
|
|
9882
10038
|
};
|
|
9883
10039
|
|
|
10040
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/restoreScreenshotBaseline.js
|
|
10041
|
+
var makeRestoreScreenshotBaselineContract = (ids) => {
|
|
10042
|
+
const input = object({
|
|
10043
|
+
baselineChangeId: string2().trim().min(1).max(100).describe("The baseline change to undo, as `run.acceptScreenshotBaseline` returned it for this run."),
|
|
10044
|
+
chatSessionId: investigationSessionId.describe("The chat session of the investigation that restores the baseline. A workspace or organization API key acts as an automation, so it must send the session that investigates the run. A person's call ignores it."),
|
|
10045
|
+
runId: ids.run
|
|
10046
|
+
});
|
|
10047
|
+
const output = discriminatedUnion("outcome", [
|
|
10048
|
+
object({
|
|
10049
|
+
name: string2().describe("The screenshot name whose baseline changed."),
|
|
10050
|
+
outcome: literal("restored").describe("The baseline the change replaced is the baseline again.")
|
|
10051
|
+
}),
|
|
10052
|
+
object({
|
|
10053
|
+
outcome: literal("baseline-changed").describe("The baseline changed again after this change, so restoring it would undo someone else's change. Nothing changed.")
|
|
10054
|
+
}),
|
|
10055
|
+
object({
|
|
10056
|
+
outcome: literal("needs-dispute").describe("Only a person can restore this change, unless a person disputed the finding that made it. Nothing changed.")
|
|
10057
|
+
}),
|
|
10058
|
+
object({
|
|
10059
|
+
outcome: literal("not-investigation-session").describe("An API key that is not a person's can change a baseline only as the investigation of the run, and chatSessionId is missing or is not the session that investigates the run. Nothing changed.")
|
|
10060
|
+
}),
|
|
10061
|
+
object({
|
|
10062
|
+
outcome: literal("nothing-to-restore").describe("The change was already restored, the name had no baseline before it, or the kept copy of the old baseline is missing or is not the baseline the change replaced. Nothing changed.")
|
|
10063
|
+
})
|
|
10064
|
+
]);
|
|
10065
|
+
return {
|
|
10066
|
+
annotations: {
|
|
10067
|
+
destructiveHint: true,
|
|
10068
|
+
openWorldHint: false,
|
|
10069
|
+
readOnlyHint: false
|
|
10070
|
+
},
|
|
10071
|
+
description: "Undo a screenshot baseline change that accepted a failed comparison of the run: put back the baseline it replaced, so later runs compare with the old baseline again. " + "Use it when a person disputes a baseline the AI accepted. " + "Refuses without changing anything when the baseline changed again since.",
|
|
10072
|
+
input,
|
|
10073
|
+
kind: "write",
|
|
10074
|
+
name: "run.restoreScreenshotBaseline",
|
|
10075
|
+
output
|
|
10076
|
+
};
|
|
10077
|
+
};
|
|
10078
|
+
|
|
9884
10079
|
// node_modules/@qawolf/api-contracts/dist/v1/run/stop.js
|
|
9885
10080
|
var makeStopRunContract = (ids) => {
|
|
9886
10081
|
const input = object({ runId: ids.run });
|
|
@@ -10201,7 +10396,7 @@ var makeRunFlowOnRunnerContract = (ids) => {
|
|
|
10201
10396
|
env: runEnvironmentSchema.optional().describe("Environment variables to make available to the run."),
|
|
10202
10397
|
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."),
|
|
10203
10398
|
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."),
|
|
10204
|
-
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.
|
|
10399
|
+
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. Omitted, the flow whose file is at `entryPointPath` on the named environment's flow-code branch stands in; a file not in QA Wolf yet, or a run given no environment, leaves the run with no `QAWOLF_WORKFLOW_ID`."),
|
|
10205
10400
|
id: runnerIdSchema.describe("Id of the runner to run the flow on."),
|
|
10206
10401
|
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."),
|
|
10207
10402
|
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."),
|
|
@@ -10468,12 +10663,16 @@ var makeContractsV1 = (ids) => {
|
|
|
10468
10663
|
},
|
|
10469
10664
|
legacyTrigger: makeLegacyTriggerContracts(resolvedIds),
|
|
10470
10665
|
run: {
|
|
10666
|
+
acceptScreenshotBaseline: makeAcceptScreenshotBaselineContract(resolvedIds),
|
|
10471
10667
|
create: makeCreateRunContract(resolvedIds),
|
|
10472
10668
|
diagnose: makeDiagnoseRunContract(resolvedIds),
|
|
10473
10669
|
find: makeFindRunsContract(resolvedIds),
|
|
10474
10670
|
get: makeGetRunContract(resolvedIds),
|
|
10475
10671
|
getAttemptArtifacts: makeGetRunAttemptArtifactsContract(resolvedIds),
|
|
10672
|
+
listScreenshotComparisons: makeListScreenshotComparisonsContract(resolvedIds),
|
|
10673
|
+
optOutOfInvestigation: makeOptOutOfInvestigationRunContract(resolvedIds),
|
|
10476
10674
|
reattempt: makeReattemptRunContract(resolvedIds),
|
|
10675
|
+
restoreScreenshotBaseline: makeRestoreScreenshotBaselineContract(resolvedIds),
|
|
10477
10676
|
stop: makeStopRunContract(resolvedIds)
|
|
10478
10677
|
},
|
|
10479
10678
|
runner: makeRunnerContracts(resolvedIds),
|
|
@@ -15108,4 +15307,4 @@ export {
|
|
|
15108
15307
|
createRunnerSdk
|
|
15109
15308
|
};
|
|
15110
15309
|
|
|
15111
|
-
//# debugId=
|
|
15310
|
+
//# debugId=6A1EED78FDDC38F864756E2164756E21
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qawolf/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.38.0",
|
|
4
4
|
"description": "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"automation",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"@clack/prompts": "1.5.1",
|
|
72
72
|
"@napi-rs/keyring": "1.3.0",
|
|
73
73
|
"@oxc-node/core": "0.1.0",
|
|
74
|
-
"@qawolf/api-contracts": "0.
|
|
74
|
+
"@qawolf/api-contracts": "0.77.0",
|
|
75
75
|
"@qawolf/emails": "1.1.1",
|
|
76
76
|
"@qawolf/flow-targets": "1.0.0",
|
|
77
77
|
"@qawolf/flows": "0.1.4",
|
|
@@ -171,17 +171,21 @@ that `url`; never guess a route and never send a repository link in its place.
|
|
|
171
171
|
| `qawolf issue create` | write | Create a bug report, maintenance report, or coverage request issue for the caller's team. A user can create a maintenance report only when they can see the workspace's maintenance reports; a team or organization API key credits it to the workspace's automation user. Link its failed flows afterwards with run.diagnose. |
|
|
172
172
|
| `qawolf issue find` | read | List the team's bug reports, maintenance reports, or coverage requests, newest first. |
|
|
173
173
|
| `qawolf issue get` | read | Get an issue by id. |
|
|
174
|
-
| `qawolf issue removeFlows` | write | Remove flows from a coverage request
|
|
174
|
+
| `qawolf issue removeFlows` | write | Remove flows from a coverage request or remove bug/maintenance reproductions in an environment. Unlinked flows are left alone. Reports auto-close when no active reproductions remain and auto-close is enabled. For reports, omit environmentId only when each selected flow is linked in at most one environment. |
|
|
175
175
|
| `qawolf issue update` | write | Update an issue owned by the caller's team. Omitted fields remain unchanged. |
|
|
176
176
|
| `qawolf legacyTrigger find` | read | List a workspace's legacy per-environment triggers; trigger.find lists the current ones. For migration only; it goes when legacy triggers go. adaptiveFlowSelection becomes a generativeSuite action, which takes no tags. |
|
|
177
177
|
| `qawolf legacyTrigger pause` | write | Pause one legacy trigger, the older per-environment kind, so it stops firing; use trigger.pause for a current one. Its copies in pull request environments pause too. For migration only; it goes when legacy triggers go. |
|
|
178
178
|
| `qawolf legacyTrigger resume` | write | Resume one legacy trigger, the older per-environment kind; use trigger.resume for a current one. Its copies in pull request environments resume too, and a scheduled one jumps to the next upcoming slot even if it was not paused. For migration only; it goes when legacy triggers go. |
|
|
179
|
+
| `qawolf run acceptScreenshotBaseline` | write | Make the new screenshot of a failed screenshot comparison the baseline, so later runs compare with it. Use it only when the difference is an intended change of the app, not a bug, noise, or a page that was not ready. Every later run of every environment of the workspace compares with the new baseline. Refuses without changing anything when the baseline changed since the run. |
|
|
179
180
|
| `qawolf run create` | write | Create a run for the selected flows and/or tags in an environment. |
|
|
180
|
-
| `qawolf run diagnose` | write | Diagnose failed flows in a run as reproductions of a bug or maintenance report owned by the caller's team. The issue's type selects the diagnosis. Each flow must have failed in the run. A flow that is already diagnosed moves to this issue. The diagnosis appears on the run, and the reproduction appears under the issue's reproductions. Coverage requests cannot be diagnosed; use issue.addFlows to cover flows instead. |
|
|
181
|
+
| `qawolf run diagnose` | write | Diagnose failed flows in a run as reproductions of a bug or maintenance report owned by the caller's team. The issue's type selects the diagnosis. Each flow must have failed in the run. A flow that is already diagnosed in the run moves to this issue, and may stay linked to other open reports. The diagnosis appears on the run, and the reproduction appears under the issue's reproductions. Coverage requests cannot be diagnosed; use issue.addFlows to cover flows instead. |
|
|
181
182
|
| `qawolf run find` | read | List an environment's recent runs, newest first, including failed-attempt discovery metadata. |
|
|
182
183
|
| `qawolf run get` | read | Get a run's status, per-flow results, links, and how many of its bugs are blocking. |
|
|
183
184
|
| `qawolf run getAttemptArtifacts` | read | Get metadata and signed artifact URLs for one finished run attempt. |
|
|
185
|
+
| `qawolf run listScreenshotComparisons` | read | List the screenshot comparisons that failed a run's flows, with the expected, actual and diff images of each. Only comparisons that caused a failure are listed: a screenshot that differs within tolerance, or that differs on a line that did not fail, is left out. Each comparison tells whether its baseline changed since the run and whether the baseline still passes in other environments. The image URLs expire after a few minutes; fetch the preview URLs to look at the images. The first call for an image makes its missing JPEG preview and keeps it in team storage, so later calls reuse it. |
|
|
186
|
+
| `qawolf run optOutOfInvestigation` | write | Mark failed flows in a run as "do not investigate" (DNI): the failures need no bug or maintenance report. Each flow must have failed in the run and still be under investigation. A flow that already has a diagnosis cannot be opted out. A team or organization key records the opt-out as the workspace's automation user. To link a failure to a report instead, use run.diagnose. |
|
|
184
187
|
| `qawolf run reattempt` | write | Request new attempts for a run's flows, in the same run. A flow is eligible once its result is failed or canceled and QA Wolf's automatic retries have finished. A fully investigated run no longer accepts reattempts. Attempts run with the latest flow code. Poll run.get for results. |
|
|
188
|
+
| `qawolf run restoreScreenshotBaseline` | write | Undo a screenshot baseline change that accepted a failed comparison of the run: put back the baseline it replaced, so later runs compare with the old baseline again. Use it when a person disputes a baseline the AI accepted. Refuses without changing anything when the baseline changed again since. |
|
|
185
189
|
| `qawolf run stop` | write | Stop a run, including its queued flows and automatic retries. Stopping is asynchronous and can update run-status messages and commit statuses in connected integrations. Repeated requests are safe, and finished runs keep their results. A run that is still being created returns not found; retry once run.get returns the run. If run.get returns a different runId, use that ID. Poll run.get for results. |
|
|
186
190
|
| `qawolf runner act` | write | Perform one raw action on a runner's screen. A browser runner takes click, double_click, scroll, move, drag, keypress, navigate and type; a mobile runner takes tap, swipe, fill and type. A browser runner answers mobile actions with action-not-supported-on-browser; a mobile runner answers the other browser actions with action-not-supported-on-mobile. Use - to read a whole action as JSON from stdin |
|
|
187
191
|
| `qawolf runner actions` | write | Perform a sequence of up to ten raw actions on a runner's screen in one request, as a JSON array of the same actions `runner act` takes. Use - to read the array from stdin. Actions run back to back, so batch only steps whose targets are on the screen you last saw, and end the sequence at the step that changes the page. Each result carries an effect: performed, not-performed, or unknown when the runner stopped answering and the action may have landed |
|
|
@@ -201,9 +201,11 @@ Every documented field of the `run.get` response. `[]` marks an array, so
|
|
|
201
201
|
- `flows[].attempts[].kind` — One of: automated, manual
|
|
202
202
|
- `flows[].attempts[].startedAt` — Absent when the attempt failed before it could start.
|
|
203
203
|
- `flows[].attempts[].status` — One of: passed, failed, canceled
|
|
204
|
-
- `flows[].failure.diagnosis` — QA Wolf's investigation verdict for the failure: `bug` means the application is broken, `maintenance` means the test needed an update and the failure does not indicate an application problem. Absent until the investigation reaches a verdict. Pass issueId to issue.get for details.
|
|
204
|
+
- `flows[].failure.diagnosis` — QA Wolf's investigation verdict for the failure: `bug` means the application is broken, `maintenance` means the test needed an update and the failure does not indicate an application problem. Absent until the investigation reaches a verdict, and absent when the flow was opted out of investigation. Pass issueId to issue.get for details.
|
|
205
205
|
- `flows[].failure.diagnosis.issueId` — The id of the issue.
|
|
206
206
|
- `flows[].failure.diagnosis.type` — One of: bug, maintenance
|
|
207
|
+
- `flows[].failure.optedOutOfInvestigation` — Present when the failure was marked "do not investigate" (DNI): it will get no bug or maintenance verdict.
|
|
208
|
+
- `flows[].failure.optedOutOfInvestigation.by` — One of: user, system
|
|
207
209
|
- `flows[].flowId` — The id of the flow.
|
|
208
210
|
- `flows[].status` — One of: failed, queued, running, passed, canceled
|
|
209
211
|
- `url` — Absolute URL of the run page.
|