@qawolf/cli 1.26.0 → 1.28.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 +1061 -266
- package/dist/runner-sdk.js +715 -29
- package/dist/types/runnerSdk/index.d.ts +1 -1
- package/dist/types/runnerSdk/types.d.ts +7 -0
- package/package.json +2 -2
- package/skills/qawolf-cli/SKILL.md +19 -4
- package/skills/qawolf-cli/references/runner.md +20 -5
package/dist/cli.js
CHANGED
|
@@ -8787,7 +8787,17 @@ var interactMessages = {
|
|
|
8787
8787
|
actionMayHaveHappened: "The runner could not be reached, which does not mean the action was not performed: it may have stopped answering mid-action. Take a screenshot before repeating it.",
|
|
8788
8788
|
actionNotJson: `Stdin did not hold a JSON action. Pipe one object, for example '{"type":"click","button":"left","x":480,"y":260}'.`,
|
|
8789
8789
|
actionNotSupportedOnMobile: (type) => `A mobile runner has a touchscreen, so it cannot perform ${type} as asked. It taps with a left-button click, swipes with drag, and types into whatever the last tap focused.`,
|
|
8790
|
+
actionFailedScreenshotToStdout: "Its screen was written to stdout as a JPEG, so look at that rather than sending the action again.",
|
|
8791
|
+
actionFailedScreenshotUnwritten: (detail) => `Its screen could not be written, because ${detail}, so take one with qawolf runner screenshot rather than sending the action again.`,
|
|
8792
|
+
actionFailedScreenshotWritten: (path) => `Its screen was written to ${path}, so look at that rather than sending the action again.`,
|
|
8793
|
+
actionFailedWithoutScreenshot: "The runner answered without a screen, so nothing was written; take one with qawolf runner screenshot rather than sending the action again.",
|
|
8790
8794
|
actionPerformed: (type) => `Performed ${type}.`,
|
|
8795
|
+
actionPerformedScreenshotNotAnImage: (type) => `Performed ${type}, but the screen that came with the answer was not a JPEG, so nothing was written. The action took effect, so do not repeat it: take the screen with qawolf runner screenshot instead, and report it if it keeps happening.`,
|
|
8796
|
+
actionPerformedScreenshotToStdout: (type) => `Performed ${type} and wrote the runner's screen to stdout as a JPEG. Stdout holds the image bytes alone; this line, and the JSON with --json, is on stderr.`,
|
|
8797
|
+
actionPerformedScreenshotStdoutUnwritable: (type, detail) => `Performed ${type}, but its screen could not be written to stdout: ${detail}. The action took effect, so do not repeat it: keep the pipe reading stdout open, or take the screen with qawolf runner screenshot --out <file>.`,
|
|
8798
|
+
actionPerformedScreenshotUnwritable: (type, path, detail) => `Performed ${type}, but its screen could not be written to "${path}": ${detail}. The action took effect, so do not repeat it: take the screen with qawolf runner screenshot, giving --out a path this process can write to.`,
|
|
8799
|
+
actionPerformedScreenshotWritten: (type, path) => `Performed ${type} and wrote the runner's screen to ${path}.`,
|
|
8800
|
+
actionPerformedWithoutScreenshot: (type) => `Performed ${type}, but the runner answered without the screen it was asked for. The action took effect, so do not repeat it: take the screen with qawolf runner screenshot instead. If this keeps happening, the platform or this CLI is behind the other: upgrade with npm install -g @qawolf/cli.`,
|
|
8791
8801
|
actionAnsweredUnknown: (failureReason) => `The runner answered "${failureReason}", which this version of the CLI does not know how to report. Upgrade with npm install -g @qawolf/cli.`,
|
|
8792
8802
|
screenshotAnsweredUnknown: (failureReason) => `The runner answered "${failureReason}", which this version of the CLI does not know how to report. Upgrade with npm install -g @qawolf/cli.`,
|
|
8793
8803
|
runnerHasNoScreen: "This runner does not run a browser on a virtual desktop, so there is nothing about it to see or drive. Retrying will never help: launch a playwright runner instead.",
|
|
@@ -8795,12 +8805,16 @@ var interactMessages = {
|
|
|
8795
8805
|
nothingToInspect: (errorMessage) => `The runner had nothing to inspect${errorMessage === undefined ? "" : `: ${errorMessage}`}. There is no live page, nothing matched the selector, or no variable has that name; a runner cannot tell those apart. Run a flow on it first, or check the selector or name.`,
|
|
8796
8806
|
inspectAnsweredUnknown: (failureReason) => `The runner answered "${failureReason}", which this version of the CLI does not know how to report. Upgrade with npm install -g @qawolf/cli.`,
|
|
8797
8807
|
inspectMobileAnsweredUnknown: (failureReason) => `The runner answered "${failureReason}", which this version of the CLI does not know how to report. Upgrade with npm install -g @qawolf/cli.`,
|
|
8808
|
+
inspectMobileInvalidSelector: "The runner could not parse that selector under the strategy it was given. Retrying it unchanged will never help: correct the selector, or pick the strategy that matches it.",
|
|
8798
8809
|
runnerIsNotMobile: "This runner is not a mobile device, so there is nothing here to inspect. Retrying will never help: launch an android or ios runner instead.",
|
|
8799
8810
|
screenNeedsARun: "This runner has not run anything yet, so its screen has never started. Waiting will not clear this and there is nothing to retry: run a flow on it with qawolf runner run, then ask again. Evaluating a snippet does not start a screen.",
|
|
8800
8811
|
screenNotReady: "The runner has a screen and cannot serve this yet. Its virtual desktop restarts when a run changes the display size, and it serves one request at a time, so something already in flight is the usual reason. Retry in a second or two.",
|
|
8801
8812
|
screenshotNotAnImage: "The screen was captured but did not arrive as a JPEG, so nothing was written. Nothing about the command needs changing: try it again, and report it if it keeps happening.",
|
|
8813
|
+
stdoutIsATerminal: (flag) => `Stdout is a terminal, so the JPEG bytes would have nowhere to go. Redirect stdout to a file or pipe it into a reader, or give ${flag} a file path instead of "-".`,
|
|
8814
|
+
screenshotStdoutUnwritable: (detail) => `The screen was captured but could not be written to stdout: ${detail}. Keep the pipe reading stdout open, or give --out a file path instead of "-".`,
|
|
8802
8815
|
screenshotUnwritable: (path, detail) => `The screen was captured but could not be written to "${path}": ${detail}. Give --out a path this process can write to.`,
|
|
8803
8816
|
screenshotWritten: (path) => `Wrote the runner's screen to ${path}.`,
|
|
8817
|
+
screenshotWrittenToStdout: "Wrote the runner's screen to stdout as a JPEG. Stdout holds the image bytes alone; this line, and the JSON with --json, is on stderr.",
|
|
8804
8818
|
snippetEmpty: (path) => `"${path}" holds no code to evaluate.`,
|
|
8805
8819
|
snippetErrored: (errorMessage) => errorMessage === undefined ? "The snippet threw and reported no message." : `The snippet threw: ${errorMessage}`,
|
|
8806
8820
|
snippetFileUnreadable: (path) => `Could not read "${path}". Name a readable file, or "-" to read the snippet from stdin.`,
|
|
@@ -16396,6 +16410,72 @@ var makeEmailAddressResourceSchema = () => resource({
|
|
|
16396
16410
|
isDefault: boolean2().describe("Whether this is the workspace's default inbox address.")
|
|
16397
16411
|
}, { urlFieldDescription: emailUrlFieldDescription });
|
|
16398
16412
|
|
|
16413
|
+
// node_modules/@qawolf/api-contracts/dist/v1/file/path.js
|
|
16414
|
+
var uploadedFilePrefix = "uploads";
|
|
16415
|
+
var controlOrBackslash = /[\p{Cc}\\]/u;
|
|
16416
|
+
var shellMetacharacter = /["'`$;|&<>]/u;
|
|
16417
|
+
var segmentsOf = (path) => path.split("/");
|
|
16418
|
+
var teamStoragePathSchema = string2().min(1).max(200).refine((path) => !controlOrBackslash.test(path), "A path cannot carry control characters or a backslash.").refine((path) => !shellMetacharacter.test(path), "A path cannot carry a quote, a backtick, or any of $ ; | & < >. A reader passes the path to a shell, where those change what runs.").refine((path) => segmentsOf(path).every((segment) => segment.length > 0), "A path cannot start or end with a slash, or carry an empty segment.").refine((path) => segmentsOf(path).every((segment) => segment !== "." && segment !== ".."), "A path cannot climb out of the team's storage.").refine((path) => segmentsOf(path).every((segment) => segment.trim() === segment), "A path cannot have leading or trailing whitespace in a segment.");
|
|
16419
|
+
var uploadedFilePath = (fileName) => `${uploadedFilePrefix}/${fileName}`;
|
|
16420
|
+
var uploadedFileNameSchema = teamStoragePathSchema.refine((fileName) => teamStoragePathSchema.safeParse(uploadedFilePath(fileName)).success, `A name has to still be a valid path once it sits under \`${uploadedFilePrefix}/\`, which every other endpoint reads it back as.`);
|
|
16421
|
+
|
|
16422
|
+
// node_modules/@qawolf/api-contracts/dist/v1/file/requestDownload.js
|
|
16423
|
+
var makeRequestFileDownloadContract = (ids) => {
|
|
16424
|
+
const input = object({
|
|
16425
|
+
filePath: teamStoragePathSchema.describe("The file's path in team storage, as file.requestUpload answered with."),
|
|
16426
|
+
workspaceId: ids.workspace.optional().describe("The workspace the file belongs to. Required when authenticating with an organization or user API key.")
|
|
16427
|
+
});
|
|
16428
|
+
const output = object({
|
|
16429
|
+
expiresAt: exports_iso.datetime().describe("When readUrl stops working. Request another one after it."),
|
|
16430
|
+
path: string2().describe("The file's path in team storage."),
|
|
16431
|
+
readUrl: string2().describe('Fetch the file\'s bytes from here, for example `curl -o journeys.csv "<readUrl>"`. Write it to a file and read that file, rather than printing the contents, so a large file costs one line of output instead of all of it.')
|
|
16432
|
+
});
|
|
16433
|
+
return {
|
|
16434
|
+
annotations: {
|
|
16435
|
+
destructiveHint: false,
|
|
16436
|
+
openWorldHint: false,
|
|
16437
|
+
readOnlyHint: true
|
|
16438
|
+
},
|
|
16439
|
+
description: "Get a URL for reading a file out of the caller's team storage. Answers 404 when nothing is stored at that path.",
|
|
16440
|
+
input,
|
|
16441
|
+
kind: "read",
|
|
16442
|
+
name: "file.requestDownload",
|
|
16443
|
+
output
|
|
16444
|
+
};
|
|
16445
|
+
};
|
|
16446
|
+
|
|
16447
|
+
// node_modules/@qawolf/api-contracts/dist/v1/file/requestUpload.js
|
|
16448
|
+
var makeRequestFileUploadContract = (ids) => {
|
|
16449
|
+
const input = object({
|
|
16450
|
+
fileName: uploadedFileNameSchema.describe("What to call the file, such as `journeys.csv` or `q3/journeys.csv`. Spaces and non-ASCII are fine; backslashes, leading slashes and `..` segments are refused. The same name replaces the file."),
|
|
16451
|
+
workspaceId: ids.workspace.optional().describe("The workspace to upload into. Required when authenticating with an organization or user API key.")
|
|
16452
|
+
});
|
|
16453
|
+
const output = object({
|
|
16454
|
+
contentType: string2().describe("Send this as the PUT's `Content-Type` header, exactly. The URL is signed for it and refuses any other value."),
|
|
16455
|
+
expiresAt: exports_iso.datetime().describe("When uploadUrl stops working. Request another one after it."),
|
|
16456
|
+
path: string2().describe("Where the file lives in team storage. Send this to agent.send as a filePath, and the AI fetches it from there."),
|
|
16457
|
+
uploadUrl: string2().describe(`Send the file's bytes here with a single HTTP PUT carrying the returned contentType, for example \`curl -H 'Content-Type: application/octet-stream' --upload-file "journeys.csv" "<uploadUrl>"\`. Upload the bytes from the shell rather than through this API, so a large file never has to be written out as text.`)
|
|
16458
|
+
});
|
|
16459
|
+
return {
|
|
16460
|
+
annotations: {
|
|
16461
|
+
destructiveHint: true,
|
|
16462
|
+
openWorldHint: false,
|
|
16463
|
+
readOnlyHint: false
|
|
16464
|
+
},
|
|
16465
|
+
description: "Get a URL to put a file into team storage: a spreadsheet of journeys, anything too large to paste. PUT with the returned contentType, then name the path in filePaths.",
|
|
16466
|
+
input,
|
|
16467
|
+
kind: "write",
|
|
16468
|
+
name: "file.requestUpload",
|
|
16469
|
+
output
|
|
16470
|
+
};
|
|
16471
|
+
};
|
|
16472
|
+
|
|
16473
|
+
// node_modules/@qawolf/api-contracts/dist/v1/file/index.js
|
|
16474
|
+
var makeFileContracts = (ids) => ({
|
|
16475
|
+
requestDownload: makeRequestFileDownloadContract(ids),
|
|
16476
|
+
requestUpload: makeRequestFileUploadContract(ids)
|
|
16477
|
+
});
|
|
16478
|
+
|
|
16399
16479
|
// node_modules/@qawolf/api-contracts/dist/v1/identity/organization.js
|
|
16400
16480
|
var identityOrganization = object({
|
|
16401
16481
|
id: string2(),
|
|
@@ -16452,6 +16532,7 @@ var defaultIdSchemas = {
|
|
|
16452
16532
|
flow: string2().min(1).describe("The id of the flow."),
|
|
16453
16533
|
issue: string2().min(1).describe("The id of the issue."),
|
|
16454
16534
|
run: string2().min(1).describe("The id of the run."),
|
|
16535
|
+
trigger: string2().min(1).describe("The id of the trigger."),
|
|
16455
16536
|
workspace: string2().min(1).describe("The id of the workspace.")
|
|
16456
16537
|
};
|
|
16457
16538
|
|
|
@@ -16487,6 +16568,11 @@ var makeCreateRunContract = (ids) => {
|
|
|
16487
16568
|
tracking: _enum(["registered", "failed", "not-requested"]).describe('Whether the conversation named by aiTaskId or chatSessionId will receive run status updates: "registered" when it will, "failed" when the run was created but the registration failed, "not-requested" when neither id was given or the AI task has no conversation to notify.')
|
|
16488
16569
|
}, { urlFieldDescription: "Absolute URL of the run page." });
|
|
16489
16570
|
return {
|
|
16571
|
+
annotations: {
|
|
16572
|
+
destructiveHint: true,
|
|
16573
|
+
openWorldHint: true,
|
|
16574
|
+
readOnlyHint: false
|
|
16575
|
+
},
|
|
16490
16576
|
description: "Create a run for the selected flows and/or tags in an environment.",
|
|
16491
16577
|
input,
|
|
16492
16578
|
kind: "write",
|
|
@@ -16624,7 +16710,8 @@ var runnerIdSchema = string2().min(1).max(63).regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9
|
|
|
16624
16710
|
var makeRunnerSchema = () => object({
|
|
16625
16711
|
gpuAccelerated: boolean2().describe("Whether the runner was placed on GPU hardware. Chosen by QA Wolf from the team's configuration and what the fleet can provide, not requested."),
|
|
16626
16712
|
id: runnerIdSchema.describe("The runner's id, as supplied when launching it."),
|
|
16627
|
-
runnerName: runnerNameSchema.describe("The runner family requested when the runner was launched. QA Wolf may place it on faster hardware, reported in gpuAccelerated.")
|
|
16713
|
+
runnerName: runnerNameSchema.describe("The runner family requested when the runner was launched. QA Wolf may place it on faster hardware, reported in gpuAccelerated."),
|
|
16714
|
+
url: 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.")
|
|
16628
16715
|
});
|
|
16629
16716
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/unreachable.js
|
|
16630
16717
|
var runnerUnreachableFailureReason = "runner-unreachable";
|
|
@@ -16669,7 +16756,12 @@ var makeInspectOnRunnerContract = (ids) => {
|
|
|
16669
16756
|
})
|
|
16670
16757
|
]);
|
|
16671
16758
|
return {
|
|
16672
|
-
|
|
16759
|
+
annotations: {
|
|
16760
|
+
destructiveHint: false,
|
|
16761
|
+
openWorldHint: false,
|
|
16762
|
+
readOnlyHint: false
|
|
16763
|
+
},
|
|
16764
|
+
description: "Inspect one thing on an interactive runner: an element's HTML, the page's HTML simplified for a model, or a top-level variable's value as JSON. This refreshes the inactivity timer and can extend the runner's billed lifetime. `nothing-to-inspect` means the runner had nothing to answer with: no live page, no element matching the selector, or no variable under that name. A runner cannot tell those apart, so it reports the one reason and puts whatever it did say in `errorMessage`. `runner-is-not-a-browser` on a mobile runner — call `runner.inspectMobile` for its equivalent surface instead. " + retryableRunnerUnreachableDescription,
|
|
16673
16765
|
input,
|
|
16674
16766
|
kind: "read",
|
|
16675
16767
|
name: "runner.inspect",
|
|
@@ -16729,6 +16821,30 @@ function makeRunnerFailureSchema(reasons) {
|
|
|
16729
16821
|
|
|
16730
16822
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/inspectMobile.js
|
|
16731
16823
|
var maxElementTextLength = 500;
|
|
16824
|
+
var selectorStrategySchema = _enum(["ios-predicate", "shadow", "xpath"]);
|
|
16825
|
+
var elementsRequestSchema = discriminatedUnion("by", [
|
|
16826
|
+
object({
|
|
16827
|
+
by: literal("point"),
|
|
16828
|
+
context: string2().min(1).optional(),
|
|
16829
|
+
what: literal("elements"),
|
|
16830
|
+
x: int().min(0).describe("Whole pixels on the device's own screen."),
|
|
16831
|
+
y: int().min(0).describe("Whole pixels on the device's own screen.")
|
|
16832
|
+
}),
|
|
16833
|
+
object({
|
|
16834
|
+
by: literal("text"),
|
|
16835
|
+
context: string2().min(1).optional(),
|
|
16836
|
+
partial: boolean2().optional().describe("Match text containing this, rather than exactly this."),
|
|
16837
|
+
text: string2().min(1).max(maxElementTextLength),
|
|
16838
|
+
what: literal("elements")
|
|
16839
|
+
}),
|
|
16840
|
+
object({
|
|
16841
|
+
by: literal("selector"),
|
|
16842
|
+
context: string2().min(1).optional(),
|
|
16843
|
+
selector: string2().min(1).max(maxSelectorLength).describe("Resolved the same way a screen object's own selector is."),
|
|
16844
|
+
strategy: selectorStrategySchema.default("xpath"),
|
|
16845
|
+
what: literal("elements")
|
|
16846
|
+
})
|
|
16847
|
+
]).describe("Elements at a screen point, elements carrying some text, or the elements a selector resolves to.");
|
|
16732
16848
|
var inspectMobileRequestSchema = discriminatedUnion("what", [
|
|
16733
16849
|
object({
|
|
16734
16850
|
what: literal("session").describe("The status of the Appium session driving the device: ready and what it is, or why not.")
|
|
@@ -16740,22 +16856,7 @@ var inspectMobileRequestSchema = discriminatedUnion("what", [
|
|
|
16740
16856
|
context: string2().min(1).optional().describe("Read this context instead of whichever is current."),
|
|
16741
16857
|
what: literal("page").describe("The page source of the device's current context, as a tree.")
|
|
16742
16858
|
}),
|
|
16743
|
-
|
|
16744
|
-
object({
|
|
16745
|
-
by: literal("point"),
|
|
16746
|
-
context: string2().min(1).optional(),
|
|
16747
|
-
what: literal("elements"),
|
|
16748
|
-
x: int().min(0).describe("Whole pixels on the device's own screen."),
|
|
16749
|
-
y: int().min(0).describe("Whole pixels on the device's own screen.")
|
|
16750
|
-
}),
|
|
16751
|
-
object({
|
|
16752
|
-
by: literal("text"),
|
|
16753
|
-
context: string2().min(1).optional(),
|
|
16754
|
-
partial: boolean2().optional().describe("Match text containing this, rather than exactly this."),
|
|
16755
|
-
text: string2().min(1).max(maxElementTextLength),
|
|
16756
|
-
what: literal("elements")
|
|
16757
|
-
})
|
|
16758
|
-
]).describe("Elements at a screen point, or elements carrying some text — never both at once.")
|
|
16859
|
+
elementsRequestSchema
|
|
16759
16860
|
]);
|
|
16760
16861
|
var inspectMobileAnswerSchema = discriminatedUnion("what", [
|
|
16761
16862
|
object({
|
|
@@ -16807,6 +16908,7 @@ var makeInspectMobileOnRunnerContract = (ids) => {
|
|
|
16807
16908
|
const output = discriminatedUnion("outcome", [
|
|
16808
16909
|
inspectMobileAnswerSchema,
|
|
16809
16910
|
makeRunnerFailureSchema([
|
|
16911
|
+
"invalid-selector",
|
|
16810
16912
|
"runner-is-not-mobile",
|
|
16811
16913
|
"screen-needs-a-run",
|
|
16812
16914
|
"screen-not-ready",
|
|
@@ -16814,7 +16916,12 @@ var makeInspectMobileOnRunnerContract = (ids) => {
|
|
|
16814
16916
|
])
|
|
16815
16917
|
]);
|
|
16816
16918
|
return {
|
|
16817
|
-
|
|
16919
|
+
annotations: {
|
|
16920
|
+
destructiveHint: false,
|
|
16921
|
+
openWorldHint: false,
|
|
16922
|
+
readOnlyHint: true
|
|
16923
|
+
},
|
|
16924
|
+
description: `Inspect one thing on a mobile interactive runner: the Appium session's status, the WebView contexts available, the current context's page source, or the elements at a point, carrying some text, or matching a selector. This does not refresh the runner's inactivity timer. Mobile only — a browser runner answers \`runner-is-not-mobile\`; call \`runner.inspect\` for a browser's equivalent surface instead. \`what: "session"\` always answers with the session's own status rather than \`screen-needs-a-run\`, since that is the question it exists to answer; the other three request kinds need a live session first and answer the same \`screen-needs-a-run\` or \`screen-not-ready\` outcomes \`runner.performAction\` and \`runner.takeScreenshot\` use, instead of reading anything when there is none. \`screen-needs-a-run\` means no Appium session has started on this runner yet — call \`runner.runFlow\` with a flow that opens one, then inspect again. \`screen-not-ready\` means the runner's Appium session exists but did not answer this instant, or more than one is somehow live — retry once; if it persists, relaunch the runner. \`runner-is-not-mobile\` means there is nothing here to inspect, and retrying will never help — launch an \`android\` or \`ios\` runner instead. \`invalid-selector\` (elements by selector only) means the selector itself could not be parsed under the given \`strategy\` — distinct from a selector that parsed fine but matched nothing, which answers \`matches: []\` instead. ${retryableRunnerUnreachableDescription}`,
|
|
16818
16925
|
input,
|
|
16819
16926
|
kind: "read",
|
|
16820
16927
|
name: "runner.inspectMobile",
|
|
@@ -16922,6 +17029,11 @@ var makePerformActionOnRunnerContract = (ids) => {
|
|
|
16922
17029
|
failure
|
|
16923
17030
|
]);
|
|
16924
17031
|
return {
|
|
17032
|
+
annotations: {
|
|
17033
|
+
destructiveHint: true,
|
|
17034
|
+
openWorldHint: true,
|
|
17035
|
+
readOnlyHint: false
|
|
17036
|
+
},
|
|
16925
17037
|
description: `Perform one raw browser action on an interactive runner: click, double_click, move, drag, scroll, keypress, type, or navigate. Coordinates are whole pixels on the runner's virtual desktop, in the same space as \`runner.takeScreenshot\`. One action per request, and the runner serves one at a time. The action shapes follow the computer-use vocabulary, minus \`screenshot\` (use \`runner.takeScreenshot\`) and \`wait\` (delay on the caller's side). A success means the action took effect. \`action-failed\`, with a reason, if it reached the runner and did not take effect. On a runner image with a browser, the first action on a runner that has never run anything starts its browser and waits for it, so it can take up to a minute to answer — no \`runner.runFlow\` is needed before acting; if the browser is still starting when the wait runs out, the answer is \`screen-not-ready\` and retrying converges. On a mobile runner, the same \`screen-needs-a-run\` and \`screen-not-ready\` outcomes mean no Appium session has started yet, or it did not answer this instant; \`runner.runFlow\` is what starts one, same as a browser. \`screen-needs-a-run\` if the browser could not be started that way — usually a runner whose runs all finished without starting its desktop; call \`runner.runFlow\` with a flow that opens a browser. ${screenNotReadyDescription} A \`navigate\` does not go through the screen, so a screen that is not ready does not stop it. ${runnerHasNoScreenDescription} ${notSupportedOnMobileDescription} ${withScreenshotDescription} ${unreachableDescription}`,
|
|
16926
17038
|
input,
|
|
16927
17039
|
kind: "write",
|
|
@@ -16939,20 +17051,90 @@ var runSelectionSchema = object({
|
|
|
16939
17051
|
error: "A selection's startLine must not be after its endLine.",
|
|
16940
17052
|
path: ["startLine"]
|
|
16941
17053
|
});
|
|
17054
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/configuration.js
|
|
17055
|
+
var isTimezone = (value) => {
|
|
17056
|
+
try {
|
|
17057
|
+
Intl.DateTimeFormat("en-US", { timeZone: value });
|
|
17058
|
+
return true;
|
|
17059
|
+
} catch {
|
|
17060
|
+
return false;
|
|
17061
|
+
}
|
|
17062
|
+
};
|
|
17063
|
+
var triggerCadenceSchema = _enum(["hourly", "daily"]).describe("How often the schedule fires.");
|
|
17064
|
+
var triggerActionKindSchema = _enum(["createSuite", "generativeSuite"]).describe("createSuite runs the flows named by flowIds and tagNames. generativeSuite lets QA Wolf choose the flows from the deployment's changes, and is not available to a schedule.");
|
|
17065
|
+
var makeTriggerActionSchema = (ids) => object({
|
|
17066
|
+
flowIds: array(ids.flow).default([]).describe("Flows to run. Combined with tagNames."),
|
|
17067
|
+
instructions: string2().min(1).optional().describe("Guidance for QA Wolf. Applies to generativeSuite only."),
|
|
17068
|
+
investigateFailures: boolean2().optional().describe("Whether QA Wolf investigates failures in the resulting runs. Defaults to true."),
|
|
17069
|
+
kind: triggerActionKindSchema.default("createSuite"),
|
|
17070
|
+
tagNames: array(string2().min(1)).default([]).describe("Tags naming the flows to run. Combined with flowIds.")
|
|
17071
|
+
}).describe("What the trigger runs when it fires.");
|
|
17072
|
+
var makeTriggerScheduleFields = (ids) => ({
|
|
17073
|
+
action: makeTriggerActionSchema(ids),
|
|
17074
|
+
cadence: triggerCadenceSchema,
|
|
17075
|
+
environmentId: ids.environmentId.describe("The environment the scheduled runs happen in."),
|
|
17076
|
+
minuteOfHour: number2().int().min(0).max(59).optional().describe("Minutes past the hour an hourly schedule fires. Defaults to 0."),
|
|
17077
|
+
timeOfDay: string2().regex(/^([01]\d|2[0-3]):[0-5]\d$/).optional().describe('The wall clock time a daily schedule fires, as "HH:mm". Required when cadence is daily.'),
|
|
17078
|
+
timezoneId: string2().min(1).refine(isTimezone, "Expected an IANA timezone, such as America/New_York.").optional().describe("The IANA timezone that timeOfDay is read in, such as America/New_York. Required when cadence is daily.")
|
|
17079
|
+
});
|
|
17080
|
+
var makeTriggerDeploymentFields = (ids) => ({
|
|
17081
|
+
action: makeTriggerActionSchema(ids),
|
|
17082
|
+
branchPattern: string2().min(1).optional().describe("Only fire for deployments of a branch matching this pattern."),
|
|
17083
|
+
deployTargetPattern: string2().min(1).optional().describe("Only fire for deployments of an app matching this pattern. A deploy target names one app within an environment."),
|
|
17084
|
+
environmentIds: array(ids.environment).default([]).describe("Only fire for deployments to these environments. Mutually exclusive with environmentPattern."),
|
|
17085
|
+
environmentPattern: string2().min(1).optional().describe("Only fire for deployments to environments matching this pattern. Mutually exclusive with environmentIds.")
|
|
17086
|
+
});
|
|
17087
|
+
|
|
17088
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/resource.js
|
|
17089
|
+
var makeTriggerConfigurationSchema = (ids) => discriminatedUnion("kind", [
|
|
17090
|
+
object({
|
|
17091
|
+
...makeTriggerScheduleFields({
|
|
17092
|
+
environmentId: ids.environment,
|
|
17093
|
+
flow: ids.flow
|
|
17094
|
+
}),
|
|
17095
|
+
kind: literal("schedule")
|
|
17096
|
+
}),
|
|
17097
|
+
object({
|
|
17098
|
+
...makeTriggerDeploymentFields(ids),
|
|
17099
|
+
kind: literal("deployment")
|
|
17100
|
+
}),
|
|
17101
|
+
object({
|
|
17102
|
+
actionCount: number2().int().describe("How many actions it runs."),
|
|
17103
|
+
kind: literal("advanced"),
|
|
17104
|
+
whenCount: number2().int().describe("How many separate match conditions it has.")
|
|
17105
|
+
})
|
|
17106
|
+
]);
|
|
17107
|
+
var makeTriggerResourceSchema = (ids) => resource({
|
|
17108
|
+
configuration: makeTriggerConfigurationSchema(ids),
|
|
17109
|
+
createdAt: exports_iso.datetime(),
|
|
17110
|
+
id: ids.trigger,
|
|
17111
|
+
isPaused: boolean2().describe("A paused trigger never fires, on any path."),
|
|
17112
|
+
name: string2(),
|
|
17113
|
+
nextScheduledAt: exports_iso.datetime().optional().describe("When a schedule trigger next fires. Absent for a deployment trigger, and while a schedule trigger is paused."),
|
|
17114
|
+
pausedAt: exports_iso.datetime().optional().describe("When it was paused."),
|
|
17115
|
+
updatedAt: exports_iso.datetime()
|
|
17116
|
+
}, { urlFieldDescription: "Absolute URL of the team's triggers page." });
|
|
16942
17117
|
// node_modules/@qawolf/api-contracts/dist/v1/agent/get.js
|
|
16943
17118
|
var makeAgentGetContract = (ids) => {
|
|
16944
17119
|
const input = object({
|
|
17120
|
+
cursor: string2().min(1).optional().describe("Opaque cursor copied from this same session's previous nextCursor. Omit to read from its first reply."),
|
|
16945
17121
|
sessionId: ids.chatSession.describe("The id agent.send answered with.")
|
|
16946
17122
|
});
|
|
16947
17123
|
const output = resource({
|
|
16948
|
-
|
|
17124
|
+
nextCursor: string2().min(1).describe("Send back as cursor on the next check to read only what follows. Always present, even with no replies. Belongs to this session alone."),
|
|
17125
|
+
replies: array(agentReplySchema).describe("What the AI has said since the cursor, oldest first. A reply still being written returns again, longer. A reply carrying choices is a question the work is blocked on."),
|
|
16949
17126
|
sessionId: ids.chatSession,
|
|
16950
17127
|
status: agentSessionStatusSchema
|
|
16951
17128
|
}, {
|
|
16952
17129
|
urlFieldDescription: "Absolute URL of the live session in the QA Wolf app."
|
|
16953
17130
|
});
|
|
16954
17131
|
return {
|
|
16955
|
-
|
|
17132
|
+
annotations: {
|
|
17133
|
+
destructiveHint: false,
|
|
17134
|
+
openWorldHint: false,
|
|
17135
|
+
readOnlyHint: true
|
|
17136
|
+
},
|
|
17137
|
+
description: 'Monitor a QA Wolf AI session by reading its status and replies. After agent.send, share the returned session URL before monitoring. Wait 30 to 60 seconds between checks; do not call this in a tight loop. Pass the nextCursor from one response as the cursor on the next check; it then reads only what is new, and only for the session that minted it. Continue monitoring silently when the status is unchanged and no replies come back; do not narrate waiting, announce the next check, or ask whether to keep monitoring. Report only substantive new progress, questions, blockers, or the final outcome. A status of "waiting-for-you" means the last reply is a question the work is blocked on, and answering it with agent.send is what unblocks it. Surface an explicit request for user input even if the status still says "working". Include the session URL when reporting a blocker or final outcome. On "completed", stop status checks and verify the requested result before claiming success. For new flows, validation, publication in the target environment, and readiness are separate checks; a Git push or final reply does not prove the flow is active. If every requested result is verified but status remains "working", report the mismatch and stop monitoring. Stop on "failed" or "cancelled" and report any confirmed partial result.',
|
|
16956
17138
|
input,
|
|
16957
17139
|
kind: "read",
|
|
16958
17140
|
name: "agent.get",
|
|
@@ -16964,6 +17146,7 @@ var makeAgentGetContract = (ids) => {
|
|
|
16964
17146
|
var makeAgentSendContract = (ids) => {
|
|
16965
17147
|
const input = object({
|
|
16966
17148
|
environmentId: ids.environmentRef.optional().describe("The environment to work in. Omit to use the team's default environment. Ignored when sessionId is given, because a session keeps the environment it opened in."),
|
|
17149
|
+
filePaths: array(teamStoragePathSchema).max(20).optional().describe("Paths of files this request is about, from file.requestUpload. The AI reads them from storage rather than the message, so a plan of hundreds of journeys costs nothing to send."),
|
|
16967
17150
|
message: string2().trim().min(1).max(1e4).describe("What you want QA Wolf to do, in plain language. Name the journey, the part of the app it covers, and anything the AI cannot discover for itself, such as a test account, a feature flag, or how to reach a staging environment. When answering a question from agent.get, send the answer here."),
|
|
16968
17151
|
sessionId: ids.chatSession.optional().describe("Continue the session with this id instead of opening a new one. Omit to start fresh. Send this to answer a question or to add context to work already running."),
|
|
16969
17152
|
workspaceId: ids.workspace.optional().describe("The workspace to work in. Required when authenticating with an organization or user API key.")
|
|
@@ -16975,6 +17158,11 @@ var makeAgentSendContract = (ids) => {
|
|
|
16975
17158
|
urlFieldDescription: "Absolute URL of the live session in the QA Wolf app."
|
|
16976
17159
|
});
|
|
16977
17160
|
return {
|
|
17161
|
+
annotations: {
|
|
17162
|
+
destructiveHint: true,
|
|
17163
|
+
openWorldHint: true,
|
|
17164
|
+
readOnlyHint: false
|
|
17165
|
+
},
|
|
16978
17166
|
description: "Start or continue work with the QA Wolf AI and return a live session URL to share with the user. Use it to cover a user journey, investigate a failing run, or fix a broken flow. This is the one verb that starts work from nothing: every other write acts on a flow, run or issue that already exists. Returns sessionId, status, and url as soon as the request is accepted; work can take minutes to tens of minutes. After each send, make the next action a normal user-visible assistant message containing the exact returned url, before any tool call or wait. Tool output and internal reasoning do not count as sharing the link. Do not run a timer or monitoring call alongside this send. Acceptance does not mean the work is complete. Then monitor the session with agent.get, reporting new progress, blockers, and the final outcome rather than unchanged status. Send here again to answer a question or add context to the same session.",
|
|
16979
17167
|
input,
|
|
16980
17168
|
kind: "write",
|
|
@@ -16993,6 +17181,11 @@ var makeAutomateContract = (ids) => {
|
|
|
16993
17181
|
});
|
|
16994
17182
|
const output = resource({ automationId: ids.automation }, { urlFieldDescription: "Absolute URL of the automation review page." });
|
|
16995
17183
|
return {
|
|
17184
|
+
annotations: {
|
|
17185
|
+
destructiveHint: true,
|
|
17186
|
+
openWorldHint: true,
|
|
17187
|
+
readOnlyHint: false
|
|
17188
|
+
},
|
|
16996
17189
|
description: "Request automation for draft flows. First create a named local .flow.ts draft for every requested journey that does not already have a matching draft; never reuse a generic starter or placeholder. Each new draft must start with a JSDoc Goal: description, import flow from @qawolf/flows/web, and use export default flow(...); a comment-only file or direct test(...) call is not a valid draft. Commit and push all changes with Git to publish them, then list remote drafts to resolve every selected ID. Do not use patch to create or rename a selected flow. Finally make one automation request containing all requested flow IDs.",
|
|
16997
17190
|
input,
|
|
16998
17191
|
kind: "write",
|
|
@@ -17024,6 +17217,11 @@ var makeFindEmailsContract = (ids) => {
|
|
|
17024
17217
|
nextCursor: nextCursorSchema
|
|
17025
17218
|
});
|
|
17026
17219
|
return {
|
|
17220
|
+
annotations: {
|
|
17221
|
+
destructiveHint: false,
|
|
17222
|
+
openWorldHint: false,
|
|
17223
|
+
readOnlyHint: true
|
|
17224
|
+
},
|
|
17027
17225
|
description: "List the workspace's inbox, or its sent mail, newest first. Read a message body with email.get.",
|
|
17028
17226
|
input,
|
|
17029
17227
|
kind: "read",
|
|
@@ -17040,6 +17238,11 @@ var makeGetEmailContract = (ids) => {
|
|
|
17040
17238
|
});
|
|
17041
17239
|
const output = makeEmailResourceSchema();
|
|
17042
17240
|
return {
|
|
17241
|
+
annotations: {
|
|
17242
|
+
destructiveHint: false,
|
|
17243
|
+
openWorldHint: false,
|
|
17244
|
+
readOnlyHint: true
|
|
17245
|
+
},
|
|
17043
17246
|
description: "Read one email of the workspace, with its plain text and HTML bodies. Use it to pull a sign-in code or a verification link out of a message.",
|
|
17044
17247
|
input,
|
|
17045
17248
|
kind: "read",
|
|
@@ -17063,6 +17266,11 @@ var makeGetEmailAttachmentContract = (ids) => {
|
|
|
17063
17266
|
type: string2().optional().describe("The MIME type.")
|
|
17064
17267
|
}, { urlFieldDescription: emailUrlFieldDescription });
|
|
17065
17268
|
return {
|
|
17269
|
+
annotations: {
|
|
17270
|
+
destructiveHint: false,
|
|
17271
|
+
openWorldHint: false,
|
|
17272
|
+
readOnlyHint: true
|
|
17273
|
+
},
|
|
17066
17274
|
description: "Read one attachment of a workspace email as base64 content, by file name or by position. email.get lists both.",
|
|
17067
17275
|
input,
|
|
17068
17276
|
kind: "read",
|
|
@@ -17082,6 +17290,11 @@ var makeListEmailAddressesContract = (ids) => {
|
|
|
17082
17290
|
nextCursor: nextCursorSchema
|
|
17083
17291
|
});
|
|
17084
17292
|
return {
|
|
17293
|
+
annotations: {
|
|
17294
|
+
destructiveHint: false,
|
|
17295
|
+
openWorldHint: false,
|
|
17296
|
+
readOnlyHint: true
|
|
17297
|
+
},
|
|
17085
17298
|
description: "List the workspace's inbox addresses, alphabetical. A flow can sign up with a plus-suffixed form of any of them, and email.find reads what arrives.",
|
|
17086
17299
|
input,
|
|
17087
17300
|
kind: "read",
|
|
@@ -17104,6 +17317,11 @@ var makeRegisterEmailAddressContract = (ids) => {
|
|
|
17104
17317
|
});
|
|
17105
17318
|
const output = makeEmailAddressResourceSchema();
|
|
17106
17319
|
return {
|
|
17320
|
+
annotations: {
|
|
17321
|
+
destructiveHint: false,
|
|
17322
|
+
openWorldHint: false,
|
|
17323
|
+
readOnlyHint: false
|
|
17324
|
+
},
|
|
17107
17325
|
description: "Register an inbox address for the workspace. Registering an address the workspace already has changes nothing. A refusal names the domains the workspace can use.",
|
|
17108
17326
|
input,
|
|
17109
17327
|
kind: "write",
|
|
@@ -17137,6 +17355,11 @@ var makeSendEmailContract = (ids) => {
|
|
|
17137
17355
|
urlFieldDescription: emailUrlFieldDescription
|
|
17138
17356
|
});
|
|
17139
17357
|
return {
|
|
17358
|
+
annotations: {
|
|
17359
|
+
destructiveHint: true,
|
|
17360
|
+
openWorldHint: true,
|
|
17361
|
+
readOnlyHint: false
|
|
17362
|
+
},
|
|
17140
17363
|
description: "Send an email from one of the workspace's inbox addresses, for example to exercise a flow that reacts to incoming mail. Returns the sent email; read it back with email.get.",
|
|
17141
17364
|
input,
|
|
17142
17365
|
kind: "write",
|
|
@@ -17155,6 +17378,11 @@ var makeDeleteEnvironmentVariableContract = (ids) => {
|
|
|
17155
17378
|
name: string2().describe("The canonicalized name that was removed. Returned even when the environment had no such variable.")
|
|
17156
17379
|
});
|
|
17157
17380
|
return {
|
|
17381
|
+
annotations: {
|
|
17382
|
+
destructiveHint: true,
|
|
17383
|
+
openWorldHint: false,
|
|
17384
|
+
readOnlyHint: false
|
|
17385
|
+
},
|
|
17158
17386
|
description: "Remove one environment variable by name. Succeeds whether or not the variable existed.",
|
|
17159
17387
|
input,
|
|
17160
17388
|
kind: "write",
|
|
@@ -17200,6 +17428,11 @@ var makeFindEnvironmentsContract = (ids) => {
|
|
|
17200
17428
|
nextCursor: nextCursorSchema
|
|
17201
17429
|
});
|
|
17202
17430
|
return {
|
|
17431
|
+
annotations: {
|
|
17432
|
+
destructiveHint: false,
|
|
17433
|
+
openWorldHint: false,
|
|
17434
|
+
readOnlyHint: true
|
|
17435
|
+
},
|
|
17203
17436
|
description: "List the team's environments, newest first.",
|
|
17204
17437
|
input,
|
|
17205
17438
|
kind: "read",
|
|
@@ -17222,6 +17455,11 @@ var makeGetEnvironmentVariableContract = (ids) => {
|
|
|
17222
17455
|
})).describe("The found variables, sorted by name.")
|
|
17223
17456
|
});
|
|
17224
17457
|
return {
|
|
17458
|
+
annotations: {
|
|
17459
|
+
destructiveHint: false,
|
|
17460
|
+
openWorldHint: false,
|
|
17461
|
+
readOnlyHint: true
|
|
17462
|
+
},
|
|
17225
17463
|
description: "Read the values of named environment variables in one call. Values are secrets. Names that do not exist go to missingNames and do not fail the call.",
|
|
17226
17464
|
input,
|
|
17227
17465
|
kind: "read",
|
|
@@ -17237,6 +17475,11 @@ var makeGetEnvironmentContract = (ids) => {
|
|
|
17237
17475
|
});
|
|
17238
17476
|
const output = makeEnvironmentResourceSchema(ids);
|
|
17239
17477
|
return {
|
|
17478
|
+
annotations: {
|
|
17479
|
+
destructiveHint: false,
|
|
17480
|
+
openWorldHint: false,
|
|
17481
|
+
readOnlyHint: true
|
|
17482
|
+
},
|
|
17240
17483
|
description: "Read a single environment's name, kind, standing run health, flow-code branch and reconciliation state, run concurrency limit, and termination state. If flowCodeBranch exists, use its syncStatus for Git reconciliation and read lastSyncedCommitHash only when syncStatus is reconciled.",
|
|
17241
17484
|
input,
|
|
17242
17485
|
kind: "read",
|
|
@@ -17252,6 +17495,11 @@ var makeListEnvironmentVariableNamesContract = (ids) => {
|
|
|
17252
17495
|
variableNames: array(string2()).describe("Names of the environment's variables, sorted alphabetically. Values are never returned.")
|
|
17253
17496
|
});
|
|
17254
17497
|
return {
|
|
17498
|
+
annotations: {
|
|
17499
|
+
destructiveHint: false,
|
|
17500
|
+
openWorldHint: false,
|
|
17501
|
+
readOnlyHint: true
|
|
17502
|
+
},
|
|
17255
17503
|
description: "Use this to answer which QA Wolf environment variables are available to test code. Returns names only; values never leave the server.",
|
|
17256
17504
|
input,
|
|
17257
17505
|
kind: "read",
|
|
@@ -17269,6 +17517,11 @@ var makeSetEnvironmentVariableContract = (ids) => {
|
|
|
17269
17517
|
name: string2().describe("The stored variable name after whitespace is replaced with underscores and letters are uppercased.")
|
|
17270
17518
|
});
|
|
17271
17519
|
return {
|
|
17520
|
+
annotations: {
|
|
17521
|
+
destructiveHint: true,
|
|
17522
|
+
openWorldHint: false,
|
|
17523
|
+
readOnlyHint: false
|
|
17524
|
+
},
|
|
17272
17525
|
description: 'Create or replace an environment variable. If the user asks to create one for "my email" without naming it, use DEFAULT_EMAIL. The value is never returned.',
|
|
17273
17526
|
input,
|
|
17274
17527
|
kind: "write",
|
|
@@ -17283,7 +17536,12 @@ var makeCreateEnvironmentContract = (ids) => {
|
|
|
17283
17536
|
});
|
|
17284
17537
|
const output = makeEnvironmentResourceSchema(ids);
|
|
17285
17538
|
return {
|
|
17286
|
-
|
|
17539
|
+
annotations: {
|
|
17540
|
+
destructiveHint: false,
|
|
17541
|
+
openWorldHint: true,
|
|
17542
|
+
readOnlyHint: false
|
|
17543
|
+
},
|
|
17544
|
+
description: "Create an environment on the caller's team and return it in the environment.get shape. This can also create a branch on the team's connected Git provider.",
|
|
17287
17545
|
input,
|
|
17288
17546
|
kind: "write",
|
|
17289
17547
|
name: "environment.create",
|
|
@@ -17304,6 +17562,11 @@ var makeUpdateEnvironmentContract = (ids) => {
|
|
|
17304
17562
|
});
|
|
17305
17563
|
const output = makeEnvironmentResourceSchema(ids);
|
|
17306
17564
|
return {
|
|
17565
|
+
annotations: {
|
|
17566
|
+
destructiveHint: true,
|
|
17567
|
+
openWorldHint: false,
|
|
17568
|
+
readOnlyHint: false
|
|
17569
|
+
},
|
|
17307
17570
|
description: "Update an environment owned by the caller's team and return it in the environment.get shape. Omitted fields remain unchanged.",
|
|
17308
17571
|
input,
|
|
17309
17572
|
kind: "write",
|
|
@@ -17326,6 +17589,11 @@ var makeCreateTagContract = (ids) => {
|
|
|
17326
17589
|
});
|
|
17327
17590
|
const output = makeTagResourceSchema();
|
|
17328
17591
|
return {
|
|
17592
|
+
annotations: {
|
|
17593
|
+
destructiveHint: false,
|
|
17594
|
+
openWorldHint: false,
|
|
17595
|
+
readOnlyHint: false
|
|
17596
|
+
},
|
|
17329
17597
|
description: "Create a tag on the caller's team. Tags select flows in run.create.",
|
|
17330
17598
|
input,
|
|
17331
17599
|
kind: "write",
|
|
@@ -17354,6 +17622,11 @@ var makeAddTagToFlowsContract = (ids) => {
|
|
|
17354
17622
|
})
|
|
17355
17623
|
});
|
|
17356
17624
|
return {
|
|
17625
|
+
annotations: {
|
|
17626
|
+
destructiveHint: false,
|
|
17627
|
+
openWorldHint: false,
|
|
17628
|
+
readOnlyHint: false
|
|
17629
|
+
},
|
|
17357
17630
|
description: "Assign an existing tag to the selected flows. Create tags with tag.create. Flows that already carry the tag are reported in skippedFlows.",
|
|
17358
17631
|
input,
|
|
17359
17632
|
kind: "write",
|
|
@@ -17386,6 +17659,11 @@ var makeListFlowsContract = (ids) => {
|
|
|
17386
17659
|
}))
|
|
17387
17660
|
});
|
|
17388
17661
|
return {
|
|
17662
|
+
annotations: {
|
|
17663
|
+
destructiveHint: false,
|
|
17664
|
+
openWorldHint: false,
|
|
17665
|
+
readOnlyHint: true
|
|
17666
|
+
},
|
|
17389
17667
|
description: "List the flows of an environment at its latest reconciled commit, or, when an AI task is given, the flows on that task's branch.",
|
|
17390
17668
|
input,
|
|
17391
17669
|
kind: "read",
|
|
@@ -17407,6 +17685,11 @@ var makeRemoveTagFromFlowsContract = (ids) => {
|
|
|
17407
17685
|
})
|
|
17408
17686
|
});
|
|
17409
17687
|
return {
|
|
17688
|
+
annotations: {
|
|
17689
|
+
destructiveHint: true,
|
|
17690
|
+
openWorldHint: false,
|
|
17691
|
+
readOnlyHint: false
|
|
17692
|
+
},
|
|
17410
17693
|
description: "Remove a tag from the selected flows. Succeeds whether or not each flow carried the tag; the flows that did not are reported in skippedFlows.",
|
|
17411
17694
|
input,
|
|
17412
17695
|
kind: "write",
|
|
@@ -17431,6 +17714,11 @@ var makeUpdateFlowContract = (ids) => {
|
|
|
17431
17714
|
})
|
|
17432
17715
|
});
|
|
17433
17716
|
return {
|
|
17717
|
+
annotations: {
|
|
17718
|
+
destructiveHint: true,
|
|
17719
|
+
openWorldHint: false,
|
|
17720
|
+
readOnlyHint: false
|
|
17721
|
+
},
|
|
17434
17722
|
description: "Move a flow between draft and active readiness. The other statuses shown in the app are derived and cannot be set.",
|
|
17435
17723
|
input,
|
|
17436
17724
|
kind: "write",
|
|
@@ -17487,6 +17775,11 @@ var makeFindIssuesContract = (ids) => {
|
|
|
17487
17775
|
nextCursor: nextCursorSchema
|
|
17488
17776
|
});
|
|
17489
17777
|
return {
|
|
17778
|
+
annotations: {
|
|
17779
|
+
destructiveHint: false,
|
|
17780
|
+
openWorldHint: false,
|
|
17781
|
+
readOnlyHint: true
|
|
17782
|
+
},
|
|
17490
17783
|
description: "List the team's bug reports, maintenance reports, or coverage requests, newest first.",
|
|
17491
17784
|
input,
|
|
17492
17785
|
kind: "read",
|
|
@@ -17502,6 +17795,11 @@ var makeGetIssueContract = (ids) => {
|
|
|
17502
17795
|
issue: makeIssueResourceSchema(ids)
|
|
17503
17796
|
});
|
|
17504
17797
|
return {
|
|
17798
|
+
annotations: {
|
|
17799
|
+
destructiveHint: false,
|
|
17800
|
+
openWorldHint: false,
|
|
17801
|
+
readOnlyHint: true
|
|
17802
|
+
},
|
|
17505
17803
|
description: "Get an issue by id.",
|
|
17506
17804
|
input,
|
|
17507
17805
|
kind: "read",
|
|
@@ -17520,6 +17818,11 @@ var makeAddFlowsToIssueContract = (ids) => {
|
|
|
17520
17818
|
issue: makeIssueResourceSchema(ids)
|
|
17521
17819
|
});
|
|
17522
17820
|
return {
|
|
17821
|
+
annotations: {
|
|
17822
|
+
destructiveHint: false,
|
|
17823
|
+
openWorldHint: false,
|
|
17824
|
+
readOnlyHint: false
|
|
17825
|
+
},
|
|
17523
17826
|
description: "Add flows to a coverage request owned by the caller's team. " + "Flows already covered stay covered. " + "Bug and maintenance reports link to flows through the runs that reproduce them; use run.diagnose to record one.",
|
|
17524
17827
|
input,
|
|
17525
17828
|
kind: "write",
|
|
@@ -17551,6 +17854,11 @@ var makeCreateIssueContract = (ids) => {
|
|
|
17551
17854
|
issue: makeIssueResourceSchema(ids)
|
|
17552
17855
|
});
|
|
17553
17856
|
return {
|
|
17857
|
+
annotations: {
|
|
17858
|
+
destructiveHint: true,
|
|
17859
|
+
openWorldHint: true,
|
|
17860
|
+
readOnlyHint: false
|
|
17861
|
+
},
|
|
17554
17862
|
description: "Create a bug or coverage request issue for the caller's team. " + "Maintenance issues cannot be created through the public API.",
|
|
17555
17863
|
input,
|
|
17556
17864
|
kind: "write",
|
|
@@ -17569,6 +17877,11 @@ var makeRemoveFlowsFromIssueContract = (ids) => {
|
|
|
17569
17877
|
issue: makeIssueResourceSchema(ids)
|
|
17570
17878
|
});
|
|
17571
17879
|
return {
|
|
17880
|
+
annotations: {
|
|
17881
|
+
destructiveHint: true,
|
|
17882
|
+
openWorldHint: false,
|
|
17883
|
+
readOnlyHint: false
|
|
17884
|
+
},
|
|
17572
17885
|
description: "Remove flows from a coverage request owned by the caller's team. " + "Flows the request does not cover are left alone. " + "Bug and maintenance reports link to flows through the runs that reproduce them, so their flows cannot be set directly.",
|
|
17573
17886
|
input,
|
|
17574
17887
|
kind: "write",
|
|
@@ -17594,6 +17907,11 @@ var makeUpdateIssueContract = (ids) => {
|
|
|
17594
17907
|
issue: makeIssueResourceSchema(ids)
|
|
17595
17908
|
});
|
|
17596
17909
|
return {
|
|
17910
|
+
annotations: {
|
|
17911
|
+
destructiveHint: true,
|
|
17912
|
+
openWorldHint: true,
|
|
17913
|
+
readOnlyHint: false
|
|
17914
|
+
},
|
|
17597
17915
|
description: "Update an issue owned by the caller's team. Omitted fields remain unchanged.",
|
|
17598
17916
|
input,
|
|
17599
17917
|
kind: "write",
|
|
@@ -17613,6 +17931,11 @@ var makeDiagnoseRunContract = (ids) => {
|
|
|
17613
17931
|
issue: makeIssueResourceSchema(ids)
|
|
17614
17932
|
});
|
|
17615
17933
|
return {
|
|
17934
|
+
annotations: {
|
|
17935
|
+
destructiveHint: true,
|
|
17936
|
+
openWorldHint: true,
|
|
17937
|
+
readOnlyHint: false
|
|
17938
|
+
},
|
|
17616
17939
|
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.",
|
|
17617
17940
|
input,
|
|
17618
17941
|
kind: "write",
|
|
@@ -17649,6 +17972,11 @@ var makeFindRunsContract = (ids) => {
|
|
|
17649
17972
|
})).describe("The environment's runs, newest first. Per-flow results are available via run.get.")
|
|
17650
17973
|
});
|
|
17651
17974
|
return {
|
|
17975
|
+
annotations: {
|
|
17976
|
+
destructiveHint: false,
|
|
17977
|
+
openWorldHint: false,
|
|
17978
|
+
readOnlyHint: true
|
|
17979
|
+
},
|
|
17652
17980
|
description: "List an environment's recent runs, newest first.",
|
|
17653
17981
|
input,
|
|
17654
17982
|
kind: "read",
|
|
@@ -17725,6 +18053,11 @@ var makeGetRunContract = (ids) => {
|
|
|
17725
18053
|
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.")
|
|
17726
18054
|
}, { urlFieldDescription: "Absolute URL of the run page." });
|
|
17727
18055
|
return {
|
|
18056
|
+
annotations: {
|
|
18057
|
+
destructiveHint: false,
|
|
18058
|
+
openWorldHint: false,
|
|
18059
|
+
readOnlyHint: true
|
|
18060
|
+
},
|
|
17728
18061
|
description: "Get a run's status, per-flow results, and links.",
|
|
17729
18062
|
input,
|
|
17730
18063
|
kind: "read",
|
|
@@ -17744,6 +18077,11 @@ var makeReattemptRunContract = (ids) => {
|
|
|
17744
18077
|
runId: ids.run
|
|
17745
18078
|
}, { urlFieldDescription: "Absolute URL of the run page." });
|
|
17746
18079
|
return {
|
|
18080
|
+
annotations: {
|
|
18081
|
+
destructiveHint: true,
|
|
18082
|
+
openWorldHint: true,
|
|
18083
|
+
readOnlyHint: false
|
|
18084
|
+
},
|
|
17747
18085
|
description: "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.",
|
|
17748
18086
|
input,
|
|
17749
18087
|
kind: "write",
|
|
@@ -17760,7 +18098,12 @@ var makeStopRunContract = (ids) => {
|
|
|
17760
18098
|
status: _enum(["accepted", "already-finished", "execution-not-found"]).describe('"accepted" means stopping was requested; poll run.get for the final result. "already-finished" means the run has already finished and its result is unchanged. "execution-not-found" means the run exists but no execution was found to stop; it may still be starting or may no longer be available. No stop request was accepted in that case.')
|
|
17761
18099
|
}, { urlFieldDescription: "Absolute URL of the run page." });
|
|
17762
18100
|
return {
|
|
17763
|
-
|
|
18101
|
+
annotations: {
|
|
18102
|
+
destructiveHint: true,
|
|
18103
|
+
openWorldHint: true,
|
|
18104
|
+
readOnlyHint: false
|
|
18105
|
+
},
|
|
18106
|
+
description: "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.",
|
|
17764
18107
|
input,
|
|
17765
18108
|
kind: "write",
|
|
17766
18109
|
name: "run.stop",
|
|
@@ -17793,6 +18136,11 @@ var makeEvaluateSnippetOnRunnerContract = (ids) => {
|
|
|
17793
18136
|
])
|
|
17794
18137
|
]);
|
|
17795
18138
|
return {
|
|
18139
|
+
annotations: {
|
|
18140
|
+
destructiveHint: true,
|
|
18141
|
+
openWorldHint: true,
|
|
18142
|
+
readOnlyHint: false
|
|
18143
|
+
},
|
|
17796
18144
|
description: "Evaluate a snippet against whatever the runner's browser is showing right now. Answers whether the snippet ran, and its error if it threw — not the value it evaluated to, so read anything you want back out of the runner's journal (a snippet's `console.log` lands in the `console` stream). A snippet starts no run and leaves no run-scoped journal entries of its own. `runner-cannot-evaluate-snippets` on a runner with nothing attached to serve one — which will never clear, so check that the runner you launched is one that runs a browser or app before retrying. `runner-unreachable` if the runner could not be reached: it may still be starting, it may have terminated after inactivity, or it may be too busy to answer. Neither is proof the snippet did not run: a snippet that outlives the answer window is still executing when you read this, so do not blindly resubmit one that mutates what the page is looking at.",
|
|
17797
18145
|
input,
|
|
17798
18146
|
kind: "write",
|
|
@@ -17813,6 +18161,11 @@ var makeGetRunnerContract = (ids) => {
|
|
|
17813
18161
|
running: boolean2().describe("False when no runner is running under this id: it was never launched, it was terminated, or it terminated on its own after inactivity. These are not distinguished, because the run system keeps no record of a runner once it is gone.")
|
|
17814
18162
|
});
|
|
17815
18163
|
return {
|
|
18164
|
+
annotations: {
|
|
18165
|
+
destructiveHint: false,
|
|
18166
|
+
openWorldHint: false,
|
|
18167
|
+
readOnlyHint: true
|
|
18168
|
+
},
|
|
17816
18169
|
description: "Report whether a runner is running under this id on the caller's team. 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. An id that is not running is a success with `running: false` rather than an error, so this is how to check a runner is still there before addressing it.",
|
|
17817
18170
|
input,
|
|
17818
18171
|
kind: "read",
|
|
@@ -17851,6 +18204,11 @@ var makeHighlightSelectorOnRunnerContract = (ids) => {
|
|
|
17851
18204
|
})
|
|
17852
18205
|
]);
|
|
17853
18206
|
return {
|
|
18207
|
+
annotations: {
|
|
18208
|
+
destructiveHint: false,
|
|
18209
|
+
openWorldHint: false,
|
|
18210
|
+
readOnlyHint: false
|
|
18211
|
+
},
|
|
17854
18212
|
description: "Highlight the elements a selector matches on an interactive runner's live page, and answer how many it matched. The highlight stays until it is replaced or cleared, so it is visible in the next `runner.takeScreenshot` — which is the point, since a caller cannot see the runner's screen otherwise. Send an empty selector to clear, which answers `cleared`. `status` tells a selector that matched nothing (`empty`) from one the page could not parse (`invalid`), so a caller can tell a bad locator from a locator pointing at nothing. `no-answer` if the page did not answer in time: the highlight runs inside the page, so a page that is gone or mid-navigation does not answer slowly, it does not answer at all. `runner-cannot-highlight-selectors` on a runner with no browser to draw on. `runner-unreachable` if the runner could not be reached: it may still be starting, or it may have terminated after inactivity.",
|
|
17855
18213
|
input,
|
|
17856
18214
|
kind: "write",
|
|
@@ -17879,6 +18237,11 @@ var makeImportPackageOnRunnerContract = (ids) => {
|
|
|
17879
18237
|
})
|
|
17880
18238
|
]);
|
|
17881
18239
|
return {
|
|
18240
|
+
annotations: {
|
|
18241
|
+
destructiveHint: true,
|
|
18242
|
+
openWorldHint: true,
|
|
18243
|
+
readOnlyHint: false
|
|
18244
|
+
},
|
|
17882
18245
|
description: "Install a package into an interactive runner's live run and import it, so a snippet or a selection can use it without a full run to reinstall dependencies. An `install-failed` failure carries npm's reason in `errorMessage`. `runner-unreachable` if the runner could not be reached: it may still be starting, it may have terminated after inactivity, or it may be too busy to answer. The install may still have landed, but installing the same version again does nothing, so retrying is safe.",
|
|
17883
18246
|
input,
|
|
17884
18247
|
kind: "write",
|
|
@@ -17899,6 +18262,11 @@ var makeLaunchRunnerContract = (ids) => {
|
|
|
17899
18262
|
outcome: literal("success")
|
|
17900
18263
|
});
|
|
17901
18264
|
return {
|
|
18265
|
+
annotations: {
|
|
18266
|
+
destructiveHint: true,
|
|
18267
|
+
openWorldHint: false,
|
|
18268
|
+
readOnlyHint: false
|
|
18269
|
+
},
|
|
17902
18270
|
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 — so read `alreadyRunning` to tell which happened.",
|
|
17903
18271
|
input,
|
|
17904
18272
|
kind: "write",
|
|
@@ -17917,6 +18285,11 @@ var makeTerminateRunnerContract = (ids) => {
|
|
|
17917
18285
|
wasRunning: boolean2().describe("False when no running runner had this id: it was already terminated, was never launched, or terminated on its own after inactivity. These are not distinguished, because the run system keeps no record of a runner once it is gone. Not an error, and a retry needs no special handling.")
|
|
17918
18286
|
});
|
|
17919
18287
|
return {
|
|
18288
|
+
annotations: {
|
|
18289
|
+
destructiveHint: true,
|
|
18290
|
+
openWorldHint: false,
|
|
18291
|
+
readOnlyHint: false
|
|
18292
|
+
},
|
|
17920
18293
|
description: "End an interactive runner on the caller's team, and the pod it runs on with it. Terminating a runner that is not running succeeds and reports `wasRunning: false`. This ends the runner: use `runner.stopRun` to stop what a runner is currently executing while leaving the runner up.",
|
|
17921
18294
|
input,
|
|
17922
18295
|
kind: "write",
|
|
@@ -17935,6 +18308,11 @@ var makeListRunnersContract = (ids) => {
|
|
|
17935
18308
|
runners: array(makeRunnerSchema()).describe("The runners running right now, in no particular order. Each entry carries the same fields a launch reports, so any of them can be addressed like a runner you launched yourself.")
|
|
17936
18309
|
});
|
|
17937
18310
|
return {
|
|
18311
|
+
annotations: {
|
|
18312
|
+
destructiveHint: false,
|
|
18313
|
+
openWorldHint: false,
|
|
18314
|
+
readOnlyHint: true
|
|
18315
|
+
},
|
|
17938
18316
|
description: "List the runners running on the caller's team right now. A team API key already names the team and sends nothing; a credential bound to no single workspace names one in `workspaceId`. 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.",
|
|
17939
18317
|
input,
|
|
17940
18318
|
kind: "read",
|
|
@@ -17964,6 +18342,11 @@ var makePromoteSnapshotOnRunnerContract = (ids) => {
|
|
|
17964
18342
|
})
|
|
17965
18343
|
]);
|
|
17966
18344
|
return {
|
|
18345
|
+
annotations: {
|
|
18346
|
+
destructiveHint: true,
|
|
18347
|
+
openWorldHint: false,
|
|
18348
|
+
readOnlyHint: false
|
|
18349
|
+
},
|
|
17967
18350
|
description: "Accept a run's screenshot as the new baseline for an image diff, on the runner that produced it. The two paths are the ones the diff reported, which reach a caller as an `image-diff-artifact` entry on the runner's `run-events` journal stream. `snapshot-not-found` if the run wrote no screenshot at that path, which usually means the paths were not taken from a diff this run produced; nothing is changed, so correcting the path and repeating is safe. `runner-cannot-promote-snapshots` on a runner that stores no screenshots. `runner-unreachable` if the runner could not be reached: it may still be starting, or it may have terminated after inactivity. A promotion that lands is not undone by promoting again, so a retry after an unreachable answer is safe.",
|
|
17968
18351
|
input,
|
|
17969
18352
|
kind: "write",
|
|
@@ -17984,6 +18367,11 @@ var makeReadRunnerJournalContract = (ids) => {
|
|
|
17984
18367
|
makeRunnerFailureSchema([runnerUnreachableFailureReason])
|
|
17985
18368
|
]);
|
|
17986
18369
|
return {
|
|
18370
|
+
annotations: {
|
|
18371
|
+
destructiveHint: false,
|
|
18372
|
+
openWorldHint: false,
|
|
18373
|
+
readOnlyHint: false
|
|
18374
|
+
},
|
|
17987
18375
|
description: `Read a window of one of an interactive runner's journal streams — the newest few, everything after a cursor, or everything belonging to one run. This is how a flow run's outcome and output are followed: \`run-status\` settles it, \`run-logs\` and \`run-events\` carry what it produced, and \`recorder\` carries the browser actions the runner recorded. A read counts as activity, so working through history does not get the runner reaped underneath you. ${retryableRunnerUnreachableDescription}`,
|
|
17988
18376
|
input,
|
|
17989
18377
|
kind: "read",
|
|
@@ -18056,6 +18444,11 @@ var makeRunFlowOnRunnerContract = (ids) => {
|
|
|
18056
18444
|
failure
|
|
18057
18445
|
]);
|
|
18058
18446
|
return {
|
|
18447
|
+
annotations: {
|
|
18448
|
+
destructiveHint: true,
|
|
18449
|
+
openWorldHint: true,
|
|
18450
|
+
readOnlyHint: false
|
|
18451
|
+
},
|
|
18059
18452
|
description: "Run a flow on an interactive runner. When `selection` is given the lines run against the browser as it stands, so nothing is re-navigated and nothing is signed in again; without it the whole entry point runs from a fresh browser. A runner with no live browser starts one before a selection and says so with `bootstrappedRunner`, which means those lines ran against a fresh page rather than the one an earlier run left. A selection produces no `runStarted` event, so follow it by its run id on `run-status` like any other run. Send `unchangedFiles` to ship only what changed since an earlier run on this runner; a `needs-full-sync` failure names the paths it does not hold, and the way to recover is the same run again with every file in `files`. Answers as soon as the run is accepted, with the id to follow it by — nothing waits for the run to finish. Which browser or device the run needs is read from the flow file's own execution target, so it is not supplied here; when it does not match what the runner is, the call fails with `runner-target-mismatch` rather than failing partway through the run. `runner-unreachable` means the answer did not arrive, which is NOT the same as the run not having started: the runner may have accepted it and been too slow to say so, and resubmitting would start a second run that is billed and journalled alongside the first. Read the runner's `run-status` journal stream before resubmitting, and use the newest run id there if one appeared.",
|
|
18060
18453
|
input,
|
|
18061
18454
|
kind: "write",
|
|
@@ -18078,6 +18471,11 @@ var makeStopRunOnRunnerContract = (ids) => {
|
|
|
18078
18471
|
makeRunnerFailureSchema([runnerUnreachableFailureReason])
|
|
18079
18472
|
]);
|
|
18080
18473
|
return {
|
|
18474
|
+
annotations: {
|
|
18475
|
+
destructiveHint: true,
|
|
18476
|
+
openWorldHint: false,
|
|
18477
|
+
readOnlyHint: false
|
|
18478
|
+
},
|
|
18081
18479
|
description: "Stop what a runner is currently executing, leaving the runner up and its browser on whatever page the run reached. Succeeds whether or not anything was running, and `wasRunning` says which. This is the counterpart of `runner.terminate`, which ends the runner itself. The run stops where it is, so the journal's `run-status` settles it as stopped rather than passed or failed. `runner-unreachable` if the runner could not be reached: it may still be starting, it may have terminated after inactivity, or it may be too busy to answer. The stop may still have landed, but stopping a runner that is already idle does nothing, so retrying is safe.",
|
|
18082
18480
|
input,
|
|
18083
18481
|
kind: "write",
|
|
@@ -18103,7 +18501,12 @@ var makeTakeScreenshotOnRunnerContract = (ids) => {
|
|
|
18103
18501
|
])
|
|
18104
18502
|
]);
|
|
18105
18503
|
return {
|
|
18106
|
-
|
|
18504
|
+
annotations: {
|
|
18505
|
+
destructiveHint: false,
|
|
18506
|
+
openWorldHint: false,
|
|
18507
|
+
readOnlyHint: false
|
|
18508
|
+
},
|
|
18509
|
+
description: `Take one screenshot of an interactive runner's screen. On a runner with a browser the image is the whole virtual desktop, browser window and all; this refreshes the inactivity timer and can extend the runner's billed lifetime. On a mobile runner it is the device's own screen, re-encoded to JPEG on the pod so this contract reads one image format regardless of runner family. ${screenNeedsARunDescription} ${screenNotReadyDescription} ${runnerHasNoScreenDescription} ${retryableRunnerUnreachableDescription}`,
|
|
18107
18510
|
input,
|
|
18108
18511
|
kind: "read",
|
|
18109
18512
|
name: "runner.takeScreenshot",
|
|
@@ -18126,6 +18529,11 @@ var makeListTagsContract = (ids) => {
|
|
|
18126
18529
|
})).describe("The team's tags, alphabetical by name.")
|
|
18127
18530
|
});
|
|
18128
18531
|
return {
|
|
18532
|
+
annotations: {
|
|
18533
|
+
destructiveHint: false,
|
|
18534
|
+
openWorldHint: false,
|
|
18535
|
+
readOnlyHint: true
|
|
18536
|
+
},
|
|
18129
18537
|
description: "List the team's tags, alphabetical by name. Tag names select flows in run.create.",
|
|
18130
18538
|
input,
|
|
18131
18539
|
kind: "read",
|
|
@@ -18134,6 +18542,250 @@ var makeListTagsContract = (ids) => {
|
|
|
18134
18542
|
};
|
|
18135
18543
|
};
|
|
18136
18544
|
|
|
18545
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/configurationRules.js
|
|
18546
|
+
var checkAction = (action, ctx) => {
|
|
18547
|
+
const namesFlows = action.flowIds.length + action.tagNames.length > 0;
|
|
18548
|
+
if (action.kind === "generativeSuite") {
|
|
18549
|
+
if (namesFlows) {
|
|
18550
|
+
ctx.addIssue({
|
|
18551
|
+
code: "custom",
|
|
18552
|
+
message: "flowIds and tagNames do not apply when the action is generativeSuite.",
|
|
18553
|
+
path: ["action", "flowIds"]
|
|
18554
|
+
});
|
|
18555
|
+
}
|
|
18556
|
+
return;
|
|
18557
|
+
}
|
|
18558
|
+
if (action.instructions !== undefined) {
|
|
18559
|
+
ctx.addIssue({
|
|
18560
|
+
code: "custom",
|
|
18561
|
+
message: "instructions apply to a generativeSuite action only.",
|
|
18562
|
+
path: ["action", "instructions"]
|
|
18563
|
+
});
|
|
18564
|
+
}
|
|
18565
|
+
if (!namesFlows) {
|
|
18566
|
+
ctx.addIssue({
|
|
18567
|
+
code: "custom",
|
|
18568
|
+
message: "At least one flow or tag is required.",
|
|
18569
|
+
path: ["action", "flowIds"]
|
|
18570
|
+
});
|
|
18571
|
+
}
|
|
18572
|
+
};
|
|
18573
|
+
var checkSchedule = (value, ctx) => {
|
|
18574
|
+
if (value.cadence === "daily") {
|
|
18575
|
+
for (const field of ["timeOfDay", "timezoneId"]) {
|
|
18576
|
+
if (value[field] === undefined) {
|
|
18577
|
+
ctx.addIssue({
|
|
18578
|
+
code: "custom",
|
|
18579
|
+
message: `${field} is required when cadence is daily.`,
|
|
18580
|
+
path: [field]
|
|
18581
|
+
});
|
|
18582
|
+
}
|
|
18583
|
+
}
|
|
18584
|
+
if (value.minuteOfHour !== undefined) {
|
|
18585
|
+
ctx.addIssue({
|
|
18586
|
+
code: "custom",
|
|
18587
|
+
message: "minuteOfHour applies to an hourly cadence only.",
|
|
18588
|
+
path: ["minuteOfHour"]
|
|
18589
|
+
});
|
|
18590
|
+
}
|
|
18591
|
+
} else {
|
|
18592
|
+
for (const field of ["timeOfDay", "timezoneId"]) {
|
|
18593
|
+
if (value[field] !== undefined) {
|
|
18594
|
+
ctx.addIssue({
|
|
18595
|
+
code: "custom",
|
|
18596
|
+
message: `${field} applies to a daily cadence only.`,
|
|
18597
|
+
path: [field]
|
|
18598
|
+
});
|
|
18599
|
+
}
|
|
18600
|
+
}
|
|
18601
|
+
}
|
|
18602
|
+
if (value.action.kind === "generativeSuite") {
|
|
18603
|
+
ctx.addIssue({
|
|
18604
|
+
code: "custom",
|
|
18605
|
+
message: "A schedule trigger cannot use a generativeSuite action.",
|
|
18606
|
+
path: ["action", "kind"]
|
|
18607
|
+
});
|
|
18608
|
+
return;
|
|
18609
|
+
}
|
|
18610
|
+
checkAction(value.action, ctx);
|
|
18611
|
+
};
|
|
18612
|
+
var checkDeployment = (value, ctx) => {
|
|
18613
|
+
if (value.environmentPattern !== undefined && value.environmentIds.length > 0) {
|
|
18614
|
+
ctx.addIssue({
|
|
18615
|
+
code: "custom",
|
|
18616
|
+
message: "Supply environmentIds or environmentPattern, not both.",
|
|
18617
|
+
path: ["environmentPattern"]
|
|
18618
|
+
});
|
|
18619
|
+
}
|
|
18620
|
+
checkAction(value.action, ctx);
|
|
18621
|
+
};
|
|
18622
|
+
var checkTriggerConfiguration = (value, ctx) => value.kind === "schedule" ? checkSchedule(value, ctx) : checkDeployment(value, ctx);
|
|
18623
|
+
|
|
18624
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/create.js
|
|
18625
|
+
var makeCreateTriggerContract = (ids) => {
|
|
18626
|
+
const commonFields = {
|
|
18627
|
+
name: string2().trim().min(1).max(255),
|
|
18628
|
+
workspaceId: ids.workspace.optional().describe("The workspace to create the trigger in. Required when authenticating with an organization or user API key.")
|
|
18629
|
+
};
|
|
18630
|
+
const input = discriminatedUnion("kind", [
|
|
18631
|
+
object({
|
|
18632
|
+
...commonFields,
|
|
18633
|
+
...makeTriggerScheduleFields({
|
|
18634
|
+
environmentId: ids.environmentRef,
|
|
18635
|
+
flow: ids.flow
|
|
18636
|
+
}),
|
|
18637
|
+
kind: literal("schedule")
|
|
18638
|
+
}),
|
|
18639
|
+
object({
|
|
18640
|
+
...commonFields,
|
|
18641
|
+
...makeTriggerDeploymentFields(ids),
|
|
18642
|
+
kind: literal("deployment")
|
|
18643
|
+
})
|
|
18644
|
+
]).superRefine(checkTriggerConfiguration);
|
|
18645
|
+
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
18646
|
+
return {
|
|
18647
|
+
annotations: {
|
|
18648
|
+
destructiveHint: true,
|
|
18649
|
+
openWorldHint: true,
|
|
18650
|
+
readOnlyHint: false
|
|
18651
|
+
},
|
|
18652
|
+
description: "Create a trigger. A schedule trigger runs a named set of flows on a cadence; " + "a deployment trigger runs when a matching deployment is reported.",
|
|
18653
|
+
input,
|
|
18654
|
+
kind: "write",
|
|
18655
|
+
name: "trigger.create",
|
|
18656
|
+
output
|
|
18657
|
+
};
|
|
18658
|
+
};
|
|
18659
|
+
|
|
18660
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/find.js
|
|
18661
|
+
var makeFindTriggersContract = (ids) => {
|
|
18662
|
+
const input = object({
|
|
18663
|
+
isPaused: boolean2().optional().describe("Only list paused, or only unpaused, triggers. Omit for both."),
|
|
18664
|
+
workspaceId: ids.workspace.optional().describe("The workspace to list triggers from. Required when authenticating with an organization or user API key.")
|
|
18665
|
+
});
|
|
18666
|
+
const output = object({
|
|
18667
|
+
triggers: array(makeTriggerResourceSchema(ids)).describe("The team's triggers, newest first.")
|
|
18668
|
+
});
|
|
18669
|
+
return {
|
|
18670
|
+
annotations: {
|
|
18671
|
+
destructiveHint: false,
|
|
18672
|
+
openWorldHint: false,
|
|
18673
|
+
readOnlyHint: true
|
|
18674
|
+
},
|
|
18675
|
+
description: "List the team's triggers, newest first.",
|
|
18676
|
+
input,
|
|
18677
|
+
kind: "read",
|
|
18678
|
+
name: "trigger.find",
|
|
18679
|
+
output
|
|
18680
|
+
};
|
|
18681
|
+
};
|
|
18682
|
+
|
|
18683
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/get.js
|
|
18684
|
+
var makeGetTriggerContract = (ids) => {
|
|
18685
|
+
const input = object({ triggerId: ids.trigger });
|
|
18686
|
+
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
18687
|
+
return {
|
|
18688
|
+
annotations: {
|
|
18689
|
+
destructiveHint: false,
|
|
18690
|
+
openWorldHint: false,
|
|
18691
|
+
readOnlyHint: true
|
|
18692
|
+
},
|
|
18693
|
+
description: "Get one trigger by id.",
|
|
18694
|
+
input,
|
|
18695
|
+
kind: "read",
|
|
18696
|
+
name: "trigger.get",
|
|
18697
|
+
output
|
|
18698
|
+
};
|
|
18699
|
+
};
|
|
18700
|
+
|
|
18701
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/lifecycle.js
|
|
18702
|
+
var makePauseTriggerContract = (ids) => {
|
|
18703
|
+
const input = object({ triggerId: ids.trigger });
|
|
18704
|
+
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
18705
|
+
return {
|
|
18706
|
+
annotations: {
|
|
18707
|
+
destructiveHint: true,
|
|
18708
|
+
openWorldHint: false,
|
|
18709
|
+
readOnlyHint: false
|
|
18710
|
+
},
|
|
18711
|
+
description: "Pause a trigger so it stops firing. Pausing is idempotent. A team whose " + "triggers are all paused reports no trigger activity at all.",
|
|
18712
|
+
input,
|
|
18713
|
+
kind: "write",
|
|
18714
|
+
name: "trigger.pause",
|
|
18715
|
+
output
|
|
18716
|
+
};
|
|
18717
|
+
};
|
|
18718
|
+
var makeResumeTriggerContract = (ids) => {
|
|
18719
|
+
const input = object({ triggerId: ids.trigger });
|
|
18720
|
+
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
18721
|
+
return {
|
|
18722
|
+
annotations: {
|
|
18723
|
+
destructiveHint: true,
|
|
18724
|
+
openWorldHint: true,
|
|
18725
|
+
readOnlyHint: false
|
|
18726
|
+
},
|
|
18727
|
+
description: "Resume a paused trigger. A schedule trigger restarts from the next " + "upcoming slot: the slots it missed while paused do not run.",
|
|
18728
|
+
input,
|
|
18729
|
+
kind: "write",
|
|
18730
|
+
name: "trigger.resume",
|
|
18731
|
+
output
|
|
18732
|
+
};
|
|
18733
|
+
};
|
|
18734
|
+
var makeDeleteTriggerContract = (ids) => {
|
|
18735
|
+
const input = object({ triggerId: ids.trigger });
|
|
18736
|
+
const output = object({
|
|
18737
|
+
triggerId: ids.trigger.describe("The id of the deleted trigger.")
|
|
18738
|
+
});
|
|
18739
|
+
return {
|
|
18740
|
+
annotations: {
|
|
18741
|
+
destructiveHint: true,
|
|
18742
|
+
openWorldHint: false,
|
|
18743
|
+
readOnlyHint: false
|
|
18744
|
+
},
|
|
18745
|
+
description: "Delete a trigger permanently. The runs it already created are kept. " + "To stop a trigger without losing it, use trigger.pause.",
|
|
18746
|
+
input,
|
|
18747
|
+
kind: "write",
|
|
18748
|
+
name: "trigger.delete",
|
|
18749
|
+
output
|
|
18750
|
+
};
|
|
18751
|
+
};
|
|
18752
|
+
|
|
18753
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/update.js
|
|
18754
|
+
var makeUpdateTriggerContract = (ids) => {
|
|
18755
|
+
const commonFields = {
|
|
18756
|
+
name: string2().trim().min(1).max(255),
|
|
18757
|
+
triggerId: ids.trigger
|
|
18758
|
+
};
|
|
18759
|
+
const input = discriminatedUnion("kind", [
|
|
18760
|
+
object({
|
|
18761
|
+
...commonFields,
|
|
18762
|
+
...makeTriggerScheduleFields({
|
|
18763
|
+
environmentId: ids.environmentRef,
|
|
18764
|
+
flow: ids.flow
|
|
18765
|
+
}),
|
|
18766
|
+
kind: literal("schedule")
|
|
18767
|
+
}),
|
|
18768
|
+
object({
|
|
18769
|
+
...commonFields,
|
|
18770
|
+
...makeTriggerDeploymentFields(ids),
|
|
18771
|
+
kind: literal("deployment")
|
|
18772
|
+
})
|
|
18773
|
+
]).superRefine(checkTriggerConfiguration);
|
|
18774
|
+
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
18775
|
+
return {
|
|
18776
|
+
annotations: {
|
|
18777
|
+
destructiveHint: true,
|
|
18778
|
+
openWorldHint: true,
|
|
18779
|
+
readOnlyHint: false
|
|
18780
|
+
},
|
|
18781
|
+
description: "Replace a trigger's configuration. Every field is written, so read the " + "trigger first and send its configuration back with your changes applied. " + "Pausing is separate: use trigger.pause and trigger.resume.",
|
|
18782
|
+
input,
|
|
18783
|
+
kind: "write",
|
|
18784
|
+
name: "trigger.update",
|
|
18785
|
+
output
|
|
18786
|
+
};
|
|
18787
|
+
};
|
|
18788
|
+
|
|
18137
18789
|
// node_modules/@qawolf/api-contracts/dist/v1/index.js
|
|
18138
18790
|
var makeContractsV1 = (ids) => {
|
|
18139
18791
|
const resolvedIds = { ...defaultIdSchemas, ...ids };
|
|
@@ -18161,6 +18813,7 @@ var makeContractsV1 = (ids) => {
|
|
|
18161
18813
|
setVariable: makeSetEnvironmentVariableContract(resolvedIds),
|
|
18162
18814
|
update: makeUpdateEnvironmentContract(resolvedIds)
|
|
18163
18815
|
},
|
|
18816
|
+
file: makeFileContracts(resolvedIds),
|
|
18164
18817
|
flow: {
|
|
18165
18818
|
addTag: makeAddTagToFlowsContract(resolvedIds),
|
|
18166
18819
|
list: makeListFlowsContract(resolvedIds),
|
|
@@ -18203,6 +18856,15 @@ var makeContractsV1 = (ids) => {
|
|
|
18203
18856
|
tag: {
|
|
18204
18857
|
create: makeCreateTagContract(resolvedIds),
|
|
18205
18858
|
list: makeListTagsContract(resolvedIds)
|
|
18859
|
+
},
|
|
18860
|
+
trigger: {
|
|
18861
|
+
create: makeCreateTriggerContract(resolvedIds),
|
|
18862
|
+
delete: makeDeleteTriggerContract(resolvedIds),
|
|
18863
|
+
find: makeFindTriggersContract(resolvedIds),
|
|
18864
|
+
get: makeGetTriggerContract(resolvedIds),
|
|
18865
|
+
pause: makePauseTriggerContract(resolvedIds),
|
|
18866
|
+
resume: makeResumeTriggerContract(resolvedIds),
|
|
18867
|
+
update: makeUpdateTriggerContract(resolvedIds)
|
|
18206
18868
|
}
|
|
18207
18869
|
};
|
|
18208
18870
|
};
|
|
@@ -20551,7 +21213,7 @@ function startUpdateCheck(deps) {
|
|
|
20551
21213
|
// package.json
|
|
20552
21214
|
var package_default = {
|
|
20553
21215
|
name: "@qawolf/cli",
|
|
20554
|
-
version: "1.
|
|
21216
|
+
version: "1.28.0",
|
|
20555
21217
|
description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
20556
21218
|
keywords: [
|
|
20557
21219
|
"automation",
|
|
@@ -20622,7 +21284,7 @@ var package_default = {
|
|
|
20622
21284
|
"@clack/prompts": "1.5.1",
|
|
20623
21285
|
"@napi-rs/keyring": "1.3.0",
|
|
20624
21286
|
"@oxc-node/core": "0.1.0",
|
|
20625
|
-
"@qawolf/api-contracts": "0.
|
|
21287
|
+
"@qawolf/api-contracts": "0.53.0",
|
|
20626
21288
|
"@qawolf/emails": "1.1.1",
|
|
20627
21289
|
"@qawolf/flow-targets": "1.0.0",
|
|
20628
21290
|
"@qawolf/flows": "0.1.4",
|
|
@@ -32308,6 +32970,28 @@ function registerPublicApiCommands(program, signals, options = {}) {
|
|
|
32308
32970
|
}
|
|
32309
32971
|
}
|
|
32310
32972
|
|
|
32973
|
+
// src/domains/interactiveRunner/evaluateSnippetFailure.ts
|
|
32974
|
+
function describeEvaluateSnippetFailure(failure) {
|
|
32975
|
+
const { failureReason } = failure;
|
|
32976
|
+
switch (failureReason) {
|
|
32977
|
+
case "runner-cannot-evaluate-snippets":
|
|
32978
|
+
return {
|
|
32979
|
+
error: interactiveRunnerMessages.runnerCannotEvaluateSnippets,
|
|
32980
|
+
exitCode: exitCodes.invalidArgs
|
|
32981
|
+
};
|
|
32982
|
+
case "runner-unreachable":
|
|
32983
|
+
return {
|
|
32984
|
+
error: interactiveRunnerMessages.snippetRunnerUnreachable,
|
|
32985
|
+
exitCode: exitCodes.network
|
|
32986
|
+
};
|
|
32987
|
+
default:
|
|
32988
|
+
return {
|
|
32989
|
+
error: interactiveRunnerMessages.snippetAnsweredUnknown(failureReason),
|
|
32990
|
+
exitCode: exitCodes.network
|
|
32991
|
+
};
|
|
32992
|
+
}
|
|
32993
|
+
}
|
|
32994
|
+
|
|
32311
32995
|
// src/domains/interactiveRunner/runnerCallOptions.ts
|
|
32312
32996
|
var runnerCallOptions = { timeoutMs: 90000 };
|
|
32313
32997
|
|
|
@@ -32420,67 +33104,138 @@ function announceRunner(ctx, resolved) {
|
|
|
32420
33104
|
}
|
|
32421
33105
|
}
|
|
32422
33106
|
|
|
32423
|
-
// src/
|
|
32424
|
-
|
|
32425
|
-
|
|
32426
|
-
|
|
32427
|
-
|
|
32428
|
-
|
|
32429
|
-
|
|
32430
|
-
|
|
32431
|
-
|
|
32432
|
-
|
|
32433
|
-
|
|
32434
|
-
|
|
32435
|
-
|
|
32436
|
-
|
|
32437
|
-
|
|
32438
|
-
|
|
32439
|
-
switch (outcome) {
|
|
32440
|
-
case "cleared":
|
|
32441
|
-
ctx.ui.output(result.value, interactiveRunnerMessages.highlightCleared);
|
|
32442
|
-
return;
|
|
32443
|
-
case "success": {
|
|
32444
|
-
const answer = result.value;
|
|
32445
|
-
if (answer.status === "invalid") {
|
|
32446
|
-
return {
|
|
32447
|
-
error: interactiveRunnerMessages.highlightSelectorInvalid(answer.selector),
|
|
32448
|
-
exitCode: exitCodes.invalidArgs
|
|
32449
|
-
};
|
|
32450
|
-
}
|
|
32451
|
-
ctx.ui.output(answer, answer.status === "empty" ? interactiveRunnerMessages.highlightMatchedNothing(answer.selector) : interactiveRunnerMessages.highlightMatched(answer.matchCount, answer.targetPage));
|
|
32452
|
-
return;
|
|
32453
|
-
}
|
|
32454
|
-
case "failure":
|
|
32455
|
-
return describeFailure(result.value.failureReason);
|
|
32456
|
-
default:
|
|
32457
|
-
return {
|
|
32458
|
-
error: interactiveRunnerMessages.highlightAnsweredUnknown(outcome),
|
|
32459
|
-
exitCode: exitCodes.network
|
|
32460
|
-
};
|
|
33107
|
+
// src/core/interactiveRunner/runFiles.ts
|
|
33108
|
+
import { isAbsolute as isAbsolute5, relative as relative10, resolve as resolve11, sep as sep5 } from "node:path";
|
|
33109
|
+
function toCollectedPath(cwd, path) {
|
|
33110
|
+
const absolute = isAbsolute5(path) ? path : resolve11(cwd, path);
|
|
33111
|
+
return relative10(cwd, absolute).split(sep5).join("/");
|
|
33112
|
+
}
|
|
33113
|
+
var namedLargestFileCount = 3;
|
|
33114
|
+
function largestFiles(files) {
|
|
33115
|
+
return Object.entries(files).map(([path, content]) => ({
|
|
33116
|
+
byteLength: runFilesByteLength({ [path]: content }),
|
|
33117
|
+
path
|
|
33118
|
+
})).sort((a, b) => b.byteLength - a.byteLength).slice(0, namedLargestFileCount);
|
|
33119
|
+
}
|
|
33120
|
+
function checkPresenceAndSize(files, requiredPath) {
|
|
33121
|
+
if (!Object.hasOwn(files, requiredPath)) {
|
|
33122
|
+
return { path: requiredPath, type: "missing-file" };
|
|
32461
33123
|
}
|
|
33124
|
+
const byteLength = runFilesByteLength(files);
|
|
33125
|
+
if (byteLength > maxRunFilesByteLength) {
|
|
33126
|
+
return {
|
|
33127
|
+
byteLength,
|
|
33128
|
+
largest: largestFiles(files),
|
|
33129
|
+
maxByteLength: maxRunFilesByteLength,
|
|
33130
|
+
type: "too-large"
|
|
33131
|
+
};
|
|
33132
|
+
}
|
|
33133
|
+
const encodedByteLength = Buffer.byteLength(JSON.stringify({ files, path: requiredPath }), "utf8");
|
|
33134
|
+
if (encodedByteLength > maxRunnerRequestEncodedByteLength) {
|
|
33135
|
+
return {
|
|
33136
|
+
byteLength: encodedByteLength,
|
|
33137
|
+
maxByteLength: maxRunnerRequestEncodedByteLength,
|
|
33138
|
+
type: "request-too-large"
|
|
33139
|
+
};
|
|
33140
|
+
}
|
|
33141
|
+
return { type: "ok" };
|
|
32462
33142
|
}
|
|
32463
|
-
function
|
|
32464
|
-
|
|
32465
|
-
|
|
32466
|
-
|
|
32467
|
-
|
|
32468
|
-
|
|
32469
|
-
|
|
32470
|
-
|
|
32471
|
-
|
|
32472
|
-
|
|
32473
|
-
|
|
32474
|
-
|
|
32475
|
-
|
|
33143
|
+
function checkRunFiles(files, entryPointPath) {
|
|
33144
|
+
const present = checkPresenceAndSize(files, entryPointPath);
|
|
33145
|
+
if (present.type !== "ok")
|
|
33146
|
+
return present;
|
|
33147
|
+
if (!Object.hasOwn(files, runPackageJsonPath)) {
|
|
33148
|
+
return { type: "missing-package-json" };
|
|
33149
|
+
}
|
|
33150
|
+
return { type: "ok" };
|
|
33151
|
+
}
|
|
33152
|
+
function checkSnippetFiles(files, contextFilePath) {
|
|
33153
|
+
return checkPresenceAndSize(files, contextFilePath);
|
|
33154
|
+
}
|
|
33155
|
+
function describeRunFilesCheck(check) {
|
|
33156
|
+
switch (check.type) {
|
|
33157
|
+
case "missing-file":
|
|
33158
|
+
return interactiveRunnerMessages.fileNotCollected(check.path);
|
|
33159
|
+
case "missing-package-json":
|
|
33160
|
+
return interactiveRunnerMessages.missingPackageJson;
|
|
33161
|
+
case "too-large":
|
|
33162
|
+
return interactiveRunnerMessages.filesTooLarge(check.byteLength, check.maxByteLength, check.largest);
|
|
33163
|
+
case "request-too-large":
|
|
33164
|
+
return interactiveRunnerMessages.requestTooLarge(check.byteLength, check.maxByteLength);
|
|
33165
|
+
}
|
|
33166
|
+
}
|
|
33167
|
+
|
|
33168
|
+
// src/domains/interactiveRunner/snippetScope.ts
|
|
33169
|
+
async function resolveSnippetScope(contextFile, deps) {
|
|
33170
|
+
if (contextFile === undefined) {
|
|
33171
|
+
return { filePath: undefined, files: undefined, ok: true };
|
|
33172
|
+
}
|
|
33173
|
+
const filePath = toCollectedPath(deps.cwd, contextFile);
|
|
33174
|
+
const { files } = await deps.collectRunFiles([filePath]);
|
|
33175
|
+
const check = checkSnippetFiles(files, filePath);
|
|
33176
|
+
if (check.type !== "ok") {
|
|
33177
|
+
return { error: describeRunFilesCheck(check), ok: false };
|
|
33178
|
+
}
|
|
33179
|
+
return { filePath, files, ok: true };
|
|
33180
|
+
}
|
|
33181
|
+
|
|
33182
|
+
// src/domains/interactiveRunner/evaluateSnippet.ts
|
|
33183
|
+
var stdinArgument = "-";
|
|
33184
|
+
async function readCode(source, deps) {
|
|
33185
|
+
const code = source === stdinArgument ? await deps.readStdin() : await deps.readFile(source).catch(() => {
|
|
33186
|
+
return;
|
|
33187
|
+
});
|
|
33188
|
+
if (code === undefined) {
|
|
33189
|
+
return {
|
|
33190
|
+
error: interactiveRunnerMessages.snippetFileUnreadable(source),
|
|
33191
|
+
ok: false
|
|
33192
|
+
};
|
|
33193
|
+
}
|
|
33194
|
+
if (code.trim() === "") {
|
|
33195
|
+
return source === stdinArgument ? { error: interactiveRunnerMessages.stdinEmptySnippet, ok: false } : { error: interactiveRunnerMessages.snippetEmpty(source), ok: false };
|
|
33196
|
+
}
|
|
33197
|
+
return { code, ok: true };
|
|
33198
|
+
}
|
|
33199
|
+
async function handleRunnerExec(ctx, options, deps) {
|
|
33200
|
+
const code = await readCode(options.source, deps);
|
|
33201
|
+
if (!code.ok)
|
|
33202
|
+
return { error: code.error, exitCode: exitCodes.invalidArgs };
|
|
33203
|
+
const scope = await resolveSnippetScope(options.contextFile, deps);
|
|
33204
|
+
if (!scope.ok)
|
|
33205
|
+
return { error: scope.error, exitCode: exitCodes.invalidArgs };
|
|
33206
|
+
const resolved = await resolveRunner(ctx, { autoLaunch: true, runner: options.runner }, deps);
|
|
33207
|
+
if (resolved.type === "failed") {
|
|
33208
|
+
return { ...failureFields(resolved), exitCode: resolved.exitCode };
|
|
33209
|
+
}
|
|
33210
|
+
announceRunner(ctx, resolved);
|
|
33211
|
+
const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.evaluateSnippet, {
|
|
33212
|
+
code: code.code,
|
|
33213
|
+
id: resolved.runnerId,
|
|
33214
|
+
...scope.filePath === undefined ? {} : { filePath: scope.filePath },
|
|
33215
|
+
...scope.files === undefined ? {} : { files: scope.files }
|
|
33216
|
+
}, runnerCallOptions);
|
|
33217
|
+
if (!result.ok) {
|
|
33218
|
+
return {
|
|
33219
|
+
...failureFields(result),
|
|
33220
|
+
exitCode: result.exitCode ?? exitCodes.network
|
|
33221
|
+
};
|
|
33222
|
+
}
|
|
33223
|
+
if (result.value.outcome === "failure") {
|
|
33224
|
+
return describeEvaluateSnippetFailure(result.value);
|
|
33225
|
+
}
|
|
33226
|
+
switch (result.value.result) {
|
|
33227
|
+
case "success":
|
|
33228
|
+
ctx.ui.output({ outcome: "evaluated", result: "success" }, interactiveRunnerMessages.snippetRan);
|
|
33229
|
+
return;
|
|
33230
|
+
case "stopped":
|
|
32476
33231
|
return {
|
|
32477
|
-
error: interactiveRunnerMessages.
|
|
32478
|
-
exitCode: exitCodes.
|
|
33232
|
+
error: interactiveRunnerMessages.snippetStopped,
|
|
33233
|
+
exitCode: exitCodes.testFailure
|
|
32479
33234
|
};
|
|
32480
|
-
|
|
33235
|
+
case "error":
|
|
32481
33236
|
return {
|
|
32482
|
-
error: interactiveRunnerMessages.
|
|
32483
|
-
exitCode: exitCodes.
|
|
33237
|
+
error: interactiveRunnerMessages.snippetErrored(result.value.errorMessage),
|
|
33238
|
+
exitCode: exitCodes.testFailure
|
|
32484
33239
|
};
|
|
32485
33240
|
}
|
|
32486
33241
|
}
|
|
@@ -32823,19 +33578,38 @@ function makeRunnerStore(options) {
|
|
|
32823
33578
|
// src/shell/interactiveRunner/writeScreenshot.ts
|
|
32824
33579
|
import { dirname as dirname16 } from "node:path";
|
|
32825
33580
|
var jpegStartOfImage = [255, 216, 255];
|
|
33581
|
+
var stdoutPath = "-";
|
|
32826
33582
|
async function writeScreenshot(options) {
|
|
32827
33583
|
const bytes = Buffer.from(options.imageJpegBase64, "base64");
|
|
32828
33584
|
if (jpegStartOfImage.some((byte, index) => bytes[index] !== byte)) {
|
|
32829
33585
|
return { ok: false, reason: "not-a-jpeg" };
|
|
32830
33586
|
}
|
|
32831
33587
|
try {
|
|
32832
|
-
|
|
32833
|
-
|
|
33588
|
+
if (options.path === stdoutPath) {
|
|
33589
|
+
await writeToStdout(options.stdout ?? process.stdout, bytes);
|
|
33590
|
+
} else {
|
|
33591
|
+
await options.fs.mkdir(dirname16(options.path), { recursive: true });
|
|
33592
|
+
await options.fs.writeFile(options.path, bytes);
|
|
33593
|
+
}
|
|
32834
33594
|
} catch (error) {
|
|
32835
33595
|
return { detail: errorMessage(error), ok: false, reason: "unwritable" };
|
|
32836
33596
|
}
|
|
32837
33597
|
return { ok: true };
|
|
32838
33598
|
}
|
|
33599
|
+
function writeToStdout(stdout, bytes) {
|
|
33600
|
+
return new Promise((resolve, reject) => {
|
|
33601
|
+
const onError = (error) => reject(error);
|
|
33602
|
+
stdout.once("error", onError);
|
|
33603
|
+
stdout.write(bytes, (error) => {
|
|
33604
|
+
if (error) {
|
|
33605
|
+
reject(error);
|
|
33606
|
+
return;
|
|
33607
|
+
}
|
|
33608
|
+
stdout.off("error", onError);
|
|
33609
|
+
resolve();
|
|
33610
|
+
});
|
|
33611
|
+
});
|
|
33612
|
+
}
|
|
32839
33613
|
|
|
32840
33614
|
// src/shell/stdin.ts
|
|
32841
33615
|
async function readStdin2() {
|
|
@@ -32876,6 +33650,81 @@ function runnerDeps(ctx) {
|
|
|
32876
33650
|
});
|
|
32877
33651
|
}
|
|
32878
33652
|
|
|
33653
|
+
// src/commands/runner/exec.register.ts
|
|
33654
|
+
var execExamples = `
|
|
33655
|
+
Examples:
|
|
33656
|
+
$ qawolf runner exec snippet.ts
|
|
33657
|
+
$ echo 'console.log(await page.title())' | qawolf runner exec -
|
|
33658
|
+
$ qawolf runner exec snippet.ts --file flows/checkout.flow.ts`;
|
|
33659
|
+
function registerRunnerExecCommand(runner, signals) {
|
|
33660
|
+
declareCommandKind(runner.command("exec <file>"), "write").description("Evaluate a snippet against a runner's live page. Use - to read the snippet from stdin").option("--file <path>", "File whose scope the snippet is evaluated in; it and the directory's other files travel with it").option("--runner <id>", runnerFlagDescription).addHelpText("after", execExamples).action((file, opts, command) => withAuthContext(signals, (ctx) => handleRunnerExec(ctx, { contextFile: opts.file, runner: opts.runner, source: file }, runnerDeps(ctx)))(opts, command));
|
|
33661
|
+
}
|
|
33662
|
+
|
|
33663
|
+
// src/domains/interactiveRunner/highlightSelector.ts
|
|
33664
|
+
async function handleRunnerHighlightSelector(ctx, options, deps) {
|
|
33665
|
+
const resolved = await resolveRunner(ctx, {
|
|
33666
|
+
autoLaunch: false,
|
|
33667
|
+
noRunnerIdMessage: interactiveRunnerMessages.noRunnerIdForHighlight,
|
|
33668
|
+
runner: options.runner
|
|
33669
|
+
}, deps);
|
|
33670
|
+
if (resolved.type === "failed") {
|
|
33671
|
+
return { ...failureFields(resolved), exitCode: resolved.exitCode };
|
|
33672
|
+
}
|
|
33673
|
+
const selector = options.selector ?? "";
|
|
33674
|
+
const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.highlightSelector, { id: resolved.runnerId, selector }, runnerCallOptions);
|
|
33675
|
+
if (!result.ok) {
|
|
33676
|
+
return { ...failureFields(result), exitCode: exitCodes.network };
|
|
33677
|
+
}
|
|
33678
|
+
const { outcome } = result.value;
|
|
33679
|
+
switch (outcome) {
|
|
33680
|
+
case "cleared":
|
|
33681
|
+
ctx.ui.output(result.value, interactiveRunnerMessages.highlightCleared);
|
|
33682
|
+
return;
|
|
33683
|
+
case "success": {
|
|
33684
|
+
const answer = result.value;
|
|
33685
|
+
if (answer.status === "invalid") {
|
|
33686
|
+
return {
|
|
33687
|
+
error: interactiveRunnerMessages.highlightSelectorInvalid(answer.selector),
|
|
33688
|
+
exitCode: exitCodes.invalidArgs
|
|
33689
|
+
};
|
|
33690
|
+
}
|
|
33691
|
+
ctx.ui.output(answer, answer.status === "empty" ? interactiveRunnerMessages.highlightMatchedNothing(answer.selector) : interactiveRunnerMessages.highlightMatched(answer.matchCount, answer.targetPage));
|
|
33692
|
+
return;
|
|
33693
|
+
}
|
|
33694
|
+
case "failure":
|
|
33695
|
+
return describeFailure(result.value.failureReason);
|
|
33696
|
+
default:
|
|
33697
|
+
return {
|
|
33698
|
+
error: interactiveRunnerMessages.highlightAnsweredUnknown(outcome),
|
|
33699
|
+
exitCode: exitCodes.network
|
|
33700
|
+
};
|
|
33701
|
+
}
|
|
33702
|
+
}
|
|
33703
|
+
function describeFailure(failureReason) {
|
|
33704
|
+
switch (failureReason) {
|
|
33705
|
+
case "no-answer":
|
|
33706
|
+
return {
|
|
33707
|
+
error: interactiveRunnerMessages.highlightNoAnswer,
|
|
33708
|
+
exitCode: exitCodes.network
|
|
33709
|
+
};
|
|
33710
|
+
case "runner-cannot-highlight-selectors":
|
|
33711
|
+
return {
|
|
33712
|
+
error: interactiveRunnerMessages.runnerCannotHighlightSelectors,
|
|
33713
|
+
exitCode: exitCodes.invalidArgs
|
|
33714
|
+
};
|
|
33715
|
+
case "runner-unreachable":
|
|
33716
|
+
return {
|
|
33717
|
+
error: interactiveRunnerMessages.runnerUnreachable,
|
|
33718
|
+
exitCode: exitCodes.network
|
|
33719
|
+
};
|
|
33720
|
+
default:
|
|
33721
|
+
return {
|
|
33722
|
+
error: interactiveRunnerMessages.highlightAnsweredUnknown(failureReason),
|
|
33723
|
+
exitCode: exitCodes.network
|
|
33724
|
+
};
|
|
33725
|
+
}
|
|
33726
|
+
}
|
|
33727
|
+
|
|
32879
33728
|
// src/commands/runner/highlightSelector.register.ts
|
|
32880
33729
|
var highlightExamples = `
|
|
32881
33730
|
Examples:
|
|
@@ -33096,7 +33945,7 @@ function buildInspectMobileRequest(what, flags) {
|
|
|
33096
33945
|
return { ok: true, request: parsed.data };
|
|
33097
33946
|
}
|
|
33098
33947
|
|
|
33099
|
-
// src/domains/interactiveRunner/
|
|
33948
|
+
// src/domains/interactiveRunner/inspectMobileAnswer.ts
|
|
33100
33949
|
function describeSession(session) {
|
|
33101
33950
|
switch (session.type) {
|
|
33102
33951
|
case "ready":
|
|
@@ -33126,6 +33975,8 @@ function streamLine(value) {
|
|
|
33126
33975
|
return JSON.stringify({ matches: value.matches });
|
|
33127
33976
|
}
|
|
33128
33977
|
}
|
|
33978
|
+
|
|
33979
|
+
// src/domains/interactiveRunner/inspectMobile.ts
|
|
33129
33980
|
async function handleRunnerInspectMobile(ctx, options, deps) {
|
|
33130
33981
|
const built = buildInspectMobileRequest(options.what, options.flags);
|
|
33131
33982
|
if (!built.ok)
|
|
@@ -33153,6 +34004,11 @@ async function handleRunnerInspectMobile(ctx, options, deps) {
|
|
|
33153
34004
|
}
|
|
33154
34005
|
const { failureReason } = result.value;
|
|
33155
34006
|
switch (failureReason) {
|
|
34007
|
+
case "invalid-selector":
|
|
34008
|
+
return {
|
|
34009
|
+
error: interactiveRunnerMessages.inspectMobileInvalidSelector,
|
|
34010
|
+
exitCode: exitCodes.invalidArgs
|
|
34011
|
+
};
|
|
33156
34012
|
case "runner-is-not-mobile":
|
|
33157
34013
|
return {
|
|
33158
34014
|
error: interactiveRunnerMessages.runnerIsNotMobile,
|
|
@@ -33265,162 +34121,10 @@ function registerRunnerInspectCommands(runner, signals) {
|
|
|
33265
34121
|
registerRunnerInspectMobileCommands(inspect, signals);
|
|
33266
34122
|
}
|
|
33267
34123
|
|
|
33268
|
-
// src/
|
|
33269
|
-
function
|
|
33270
|
-
const
|
|
33271
|
-
|
|
33272
|
-
case "runner-cannot-evaluate-snippets":
|
|
33273
|
-
return {
|
|
33274
|
-
error: interactiveRunnerMessages.runnerCannotEvaluateSnippets,
|
|
33275
|
-
exitCode: exitCodes.invalidArgs
|
|
33276
|
-
};
|
|
33277
|
-
case "runner-unreachable":
|
|
33278
|
-
return {
|
|
33279
|
-
error: interactiveRunnerMessages.snippetRunnerUnreachable,
|
|
33280
|
-
exitCode: exitCodes.network
|
|
33281
|
-
};
|
|
33282
|
-
default:
|
|
33283
|
-
return {
|
|
33284
|
-
error: interactiveRunnerMessages.snippetAnsweredUnknown(failureReason),
|
|
33285
|
-
exitCode: exitCodes.network
|
|
33286
|
-
};
|
|
33287
|
-
}
|
|
33288
|
-
}
|
|
33289
|
-
|
|
33290
|
-
// src/core/interactiveRunner/runFiles.ts
|
|
33291
|
-
import { isAbsolute as isAbsolute5, relative as relative10, resolve as resolve11, sep as sep5 } from "node:path";
|
|
33292
|
-
function toCollectedPath(cwd, path) {
|
|
33293
|
-
const absolute = isAbsolute5(path) ? path : resolve11(cwd, path);
|
|
33294
|
-
return relative10(cwd, absolute).split(sep5).join("/");
|
|
33295
|
-
}
|
|
33296
|
-
var namedLargestFileCount = 3;
|
|
33297
|
-
function largestFiles(files) {
|
|
33298
|
-
return Object.entries(files).map(([path, content]) => ({
|
|
33299
|
-
byteLength: runFilesByteLength({ [path]: content }),
|
|
33300
|
-
path
|
|
33301
|
-
})).sort((a, b) => b.byteLength - a.byteLength).slice(0, namedLargestFileCount);
|
|
33302
|
-
}
|
|
33303
|
-
function checkPresenceAndSize(files, requiredPath) {
|
|
33304
|
-
if (!Object.hasOwn(files, requiredPath)) {
|
|
33305
|
-
return { path: requiredPath, type: "missing-file" };
|
|
33306
|
-
}
|
|
33307
|
-
const byteLength = runFilesByteLength(files);
|
|
33308
|
-
if (byteLength > maxRunFilesByteLength) {
|
|
33309
|
-
return {
|
|
33310
|
-
byteLength,
|
|
33311
|
-
largest: largestFiles(files),
|
|
33312
|
-
maxByteLength: maxRunFilesByteLength,
|
|
33313
|
-
type: "too-large"
|
|
33314
|
-
};
|
|
33315
|
-
}
|
|
33316
|
-
const encodedByteLength = Buffer.byteLength(JSON.stringify({ files, path: requiredPath }), "utf8");
|
|
33317
|
-
if (encodedByteLength > maxRunnerRequestEncodedByteLength) {
|
|
33318
|
-
return {
|
|
33319
|
-
byteLength: encodedByteLength,
|
|
33320
|
-
maxByteLength: maxRunnerRequestEncodedByteLength,
|
|
33321
|
-
type: "request-too-large"
|
|
33322
|
-
};
|
|
33323
|
-
}
|
|
33324
|
-
return { type: "ok" };
|
|
33325
|
-
}
|
|
33326
|
-
function checkRunFiles(files, entryPointPath) {
|
|
33327
|
-
const present = checkPresenceAndSize(files, entryPointPath);
|
|
33328
|
-
if (present.type !== "ok")
|
|
33329
|
-
return present;
|
|
33330
|
-
if (!Object.hasOwn(files, runPackageJsonPath)) {
|
|
33331
|
-
return { type: "missing-package-json" };
|
|
33332
|
-
}
|
|
33333
|
-
return { type: "ok" };
|
|
33334
|
-
}
|
|
33335
|
-
function checkSnippetFiles(files, contextFilePath) {
|
|
33336
|
-
return checkPresenceAndSize(files, contextFilePath);
|
|
33337
|
-
}
|
|
33338
|
-
function describeRunFilesCheck(check) {
|
|
33339
|
-
switch (check.type) {
|
|
33340
|
-
case "missing-file":
|
|
33341
|
-
return interactiveRunnerMessages.fileNotCollected(check.path);
|
|
33342
|
-
case "missing-package-json":
|
|
33343
|
-
return interactiveRunnerMessages.missingPackageJson;
|
|
33344
|
-
case "too-large":
|
|
33345
|
-
return interactiveRunnerMessages.filesTooLarge(check.byteLength, check.maxByteLength, check.largest);
|
|
33346
|
-
case "request-too-large":
|
|
33347
|
-
return interactiveRunnerMessages.requestTooLarge(check.byteLength, check.maxByteLength);
|
|
33348
|
-
}
|
|
33349
|
-
}
|
|
33350
|
-
|
|
33351
|
-
// src/domains/interactiveRunner/snippetScope.ts
|
|
33352
|
-
async function resolveSnippetScope(contextFile, deps) {
|
|
33353
|
-
if (contextFile === undefined) {
|
|
33354
|
-
return { filePath: undefined, files: undefined, ok: true };
|
|
33355
|
-
}
|
|
33356
|
-
const filePath = toCollectedPath(deps.cwd, contextFile);
|
|
33357
|
-
const { files } = await deps.collectRunFiles([filePath]);
|
|
33358
|
-
const check = checkSnippetFiles(files, filePath);
|
|
33359
|
-
if (check.type !== "ok") {
|
|
33360
|
-
return { error: describeRunFilesCheck(check), ok: false };
|
|
33361
|
-
}
|
|
33362
|
-
return { filePath, files, ok: true };
|
|
33363
|
-
}
|
|
33364
|
-
|
|
33365
|
-
// src/domains/interactiveRunner/evaluateSnippet.ts
|
|
33366
|
-
var stdinArgument = "-";
|
|
33367
|
-
async function readCode(source, deps) {
|
|
33368
|
-
const code = source === stdinArgument ? await deps.readStdin() : await deps.readFile(source).catch(() => {
|
|
33369
|
-
return;
|
|
33370
|
-
});
|
|
33371
|
-
if (code === undefined) {
|
|
33372
|
-
return {
|
|
33373
|
-
error: interactiveRunnerMessages.snippetFileUnreadable(source),
|
|
33374
|
-
ok: false
|
|
33375
|
-
};
|
|
33376
|
-
}
|
|
33377
|
-
if (code.trim() === "") {
|
|
33378
|
-
return source === stdinArgument ? { error: interactiveRunnerMessages.stdinEmptySnippet, ok: false } : { error: interactiveRunnerMessages.snippetEmpty(source), ok: false };
|
|
33379
|
-
}
|
|
33380
|
-
return { code, ok: true };
|
|
33381
|
-
}
|
|
33382
|
-
async function handleRunnerExec(ctx, options, deps) {
|
|
33383
|
-
const code = await readCode(options.source, deps);
|
|
33384
|
-
if (!code.ok)
|
|
33385
|
-
return { error: code.error, exitCode: exitCodes.invalidArgs };
|
|
33386
|
-
const scope = await resolveSnippetScope(options.contextFile, deps);
|
|
33387
|
-
if (!scope.ok)
|
|
33388
|
-
return { error: scope.error, exitCode: exitCodes.invalidArgs };
|
|
33389
|
-
const resolved = await resolveRunner(ctx, { autoLaunch: true, runner: options.runner }, deps);
|
|
33390
|
-
if (resolved.type === "failed") {
|
|
33391
|
-
return { ...failureFields(resolved), exitCode: resolved.exitCode };
|
|
33392
|
-
}
|
|
33393
|
-
announceRunner(ctx, resolved);
|
|
33394
|
-
const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.evaluateSnippet, {
|
|
33395
|
-
code: code.code,
|
|
33396
|
-
id: resolved.runnerId,
|
|
33397
|
-
...scope.filePath === undefined ? {} : { filePath: scope.filePath },
|
|
33398
|
-
...scope.files === undefined ? {} : { files: scope.files }
|
|
33399
|
-
}, runnerCallOptions);
|
|
33400
|
-
if (!result.ok) {
|
|
33401
|
-
return {
|
|
33402
|
-
...failureFields(result),
|
|
33403
|
-
exitCode: result.exitCode ?? exitCodes.network
|
|
33404
|
-
};
|
|
33405
|
-
}
|
|
33406
|
-
if (result.value.outcome === "failure") {
|
|
33407
|
-
return describeEvaluateSnippetFailure(result.value);
|
|
33408
|
-
}
|
|
33409
|
-
switch (result.value.result) {
|
|
33410
|
-
case "success":
|
|
33411
|
-
ctx.ui.output({ outcome: "evaluated", result: "success" }, interactiveRunnerMessages.snippetRan);
|
|
33412
|
-
return;
|
|
33413
|
-
case "stopped":
|
|
33414
|
-
return {
|
|
33415
|
-
error: interactiveRunnerMessages.snippetStopped,
|
|
33416
|
-
exitCode: exitCodes.testFailure
|
|
33417
|
-
};
|
|
33418
|
-
case "error":
|
|
33419
|
-
return {
|
|
33420
|
-
error: interactiveRunnerMessages.snippetErrored(result.value.errorMessage),
|
|
33421
|
-
exitCode: exitCodes.testFailure
|
|
33422
|
-
};
|
|
33423
|
-
}
|
|
34124
|
+
// src/core/sentences.ts
|
|
34125
|
+
function appendSentence(text, sentence) {
|
|
34126
|
+
const punctuated = /[.!?]$/.test(text.trimEnd()) ? text.trimEnd() : `${text.trimEnd()}.`;
|
|
34127
|
+
return `${punctuated} ${sentence}`;
|
|
33424
34128
|
}
|
|
33425
34129
|
|
|
33426
34130
|
// src/domains/interactiveRunner/performActionFailure.ts
|
|
@@ -33467,6 +34171,56 @@ function describePerformActionFailure(options) {
|
|
|
33467
34171
|
}
|
|
33468
34172
|
}
|
|
33469
34173
|
|
|
34174
|
+
// src/domains/interactiveRunner/performActionScreenshot.ts
|
|
34175
|
+
async function writeActionScreenshot(ctx, options, deps) {
|
|
34176
|
+
const { type } = options.action;
|
|
34177
|
+
if (options.imageJpegBase64 === undefined) {
|
|
34178
|
+
return {
|
|
34179
|
+
error: interactiveRunnerMessages.actionPerformedWithoutScreenshot(type),
|
|
34180
|
+
exitCode: exitCodes.network
|
|
34181
|
+
};
|
|
34182
|
+
}
|
|
34183
|
+
const written = await deps.writeScreenshot({
|
|
34184
|
+
imageJpegBase64: options.imageJpegBase64,
|
|
34185
|
+
path: options.out
|
|
34186
|
+
});
|
|
34187
|
+
if (!written.ok) {
|
|
34188
|
+
return written.reason === "not-a-jpeg" ? {
|
|
34189
|
+
error: interactiveRunnerMessages.actionPerformedScreenshotNotAnImage(type),
|
|
34190
|
+
exitCode: exitCodes.network
|
|
34191
|
+
} : {
|
|
34192
|
+
error: options.out === stdoutPath ? interactiveRunnerMessages.actionPerformedScreenshotStdoutUnwritable(type, written.detail) : interactiveRunnerMessages.actionPerformedScreenshotUnwritable(type, options.out, written.detail),
|
|
34193
|
+
exitCode: exitCodes.network
|
|
34194
|
+
};
|
|
34195
|
+
}
|
|
34196
|
+
if (options.out === stdoutPath) {
|
|
34197
|
+
ctx.ui.success(interactiveRunnerMessages.actionPerformedScreenshotToStdout(type));
|
|
34198
|
+
return;
|
|
34199
|
+
}
|
|
34200
|
+
ctx.ui.output({ action: options.action, outcome: "success", screenshotPath: options.out }, interactiveRunnerMessages.actionPerformedScreenshotWritten(type, options.out));
|
|
34201
|
+
return;
|
|
34202
|
+
}
|
|
34203
|
+
async function addFailureScreenshot(options, deps) {
|
|
34204
|
+
const note = await describeFailureScreenshot(options, deps);
|
|
34205
|
+
return {
|
|
34206
|
+
...options.failure,
|
|
34207
|
+
error: appendSentence(options.failure.error, note)
|
|
34208
|
+
};
|
|
34209
|
+
}
|
|
34210
|
+
async function describeFailureScreenshot(options, deps) {
|
|
34211
|
+
if (options.imageJpegBase64 === undefined) {
|
|
34212
|
+
return interactiveRunnerMessages.actionFailedWithoutScreenshot;
|
|
34213
|
+
}
|
|
34214
|
+
const written = await deps.writeScreenshot({
|
|
34215
|
+
imageJpegBase64: options.imageJpegBase64,
|
|
34216
|
+
path: options.out
|
|
34217
|
+
});
|
|
34218
|
+
if (!written.ok) {
|
|
34219
|
+
return interactiveRunnerMessages.actionFailedScreenshotUnwritten(written.reason === "not-a-jpeg" ? "it did not arrive as a JPEG" : written.detail);
|
|
34220
|
+
}
|
|
34221
|
+
return options.out === stdoutPath ? interactiveRunnerMessages.actionFailedScreenshotToStdout : interactiveRunnerMessages.actionFailedScreenshotWritten(options.out);
|
|
34222
|
+
}
|
|
34223
|
+
|
|
33470
34224
|
// src/core/interactiveRunner/browserAction.ts
|
|
33471
34225
|
function parseJsonPath(path) {
|
|
33472
34226
|
try {
|
|
@@ -33531,30 +34285,56 @@ async function handleRunnerAct(ctx, options, deps) {
|
|
|
33531
34285
|
const built = await readAction(options.type, options.flags, deps);
|
|
33532
34286
|
if (!built.ok)
|
|
33533
34287
|
return { error: built.error, exitCode: exitCodes.invalidArgs };
|
|
34288
|
+
if (options.screenshot === stdoutPath && ctx.outputMode === "human") {
|
|
34289
|
+
return {
|
|
34290
|
+
error: interactiveRunnerMessages.stdoutIsATerminal("--screenshot"),
|
|
34291
|
+
exitCode: exitCodes.invalidArgs
|
|
34292
|
+
};
|
|
34293
|
+
}
|
|
33534
34294
|
const resolved = await resolveRunner(ctx, { autoLaunch: true, runner: options.runner }, deps);
|
|
33535
34295
|
if (resolved.type === "failed") {
|
|
33536
34296
|
return { ...failureFields(resolved), exitCode: resolved.exitCode };
|
|
33537
34297
|
}
|
|
33538
34298
|
announceRunner(ctx, resolved);
|
|
33539
|
-
const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.performAction, {
|
|
34299
|
+
const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.performAction, {
|
|
34300
|
+
action: built.action,
|
|
34301
|
+
id: resolved.runnerId,
|
|
34302
|
+
...options.screenshot === undefined ? {} : { withScreenshot: true }
|
|
34303
|
+
}, runnerCallOptions);
|
|
33540
34304
|
if (!result.ok) {
|
|
33541
34305
|
const fields = failureFields(result);
|
|
33542
34306
|
return {
|
|
33543
34307
|
...fields,
|
|
33544
34308
|
...result.mayHaveArrived ? {
|
|
33545
|
-
error:
|
|
34309
|
+
error: appendSentence(fields.error, interactiveRunnerMessages.actionMayHaveHappened)
|
|
33546
34310
|
} : {},
|
|
33547
34311
|
exitCode: exitCodes.network
|
|
33548
34312
|
};
|
|
33549
34313
|
}
|
|
33550
|
-
|
|
34314
|
+
const answer = result.value;
|
|
34315
|
+
if (answer.outcome === "success") {
|
|
34316
|
+
if (options.screenshot !== undefined) {
|
|
34317
|
+
return writeActionScreenshot(ctx, {
|
|
34318
|
+
action: built.action,
|
|
34319
|
+
imageJpegBase64: answer.imageJpegBase64,
|
|
34320
|
+
out: options.screenshot
|
|
34321
|
+
}, deps);
|
|
34322
|
+
}
|
|
33551
34323
|
ctx.ui.output({ action: built.action, outcome: "success" }, interactiveRunnerMessages.actionPerformed(built.action.type));
|
|
33552
34324
|
return;
|
|
33553
34325
|
}
|
|
33554
|
-
|
|
34326
|
+
const failure = describePerformActionFailure({
|
|
33555
34327
|
actionType: built.action.type,
|
|
33556
|
-
failure:
|
|
34328
|
+
failure: answer
|
|
33557
34329
|
});
|
|
34330
|
+
if (options.screenshot === undefined || answer.failureReason !== "action-failed") {
|
|
34331
|
+
return failure;
|
|
34332
|
+
}
|
|
34333
|
+
return addFailureScreenshot({
|
|
34334
|
+
failure,
|
|
34335
|
+
imageJpegBase64: answer.imageJpegBase64,
|
|
34336
|
+
out: options.screenshot
|
|
34337
|
+
}, deps);
|
|
33558
34338
|
}
|
|
33559
34339
|
|
|
33560
34340
|
// src/domains/interactiveRunner/takeScreenshot.ts
|
|
@@ -33567,6 +34347,12 @@ async function handleRunnerScreenshot(ctx, options, deps) {
|
|
|
33567
34347
|
if (resolved.type === "failed") {
|
|
33568
34348
|
return { ...failureFields(resolved), exitCode: resolved.exitCode };
|
|
33569
34349
|
}
|
|
34350
|
+
if (options.out === stdoutPath && ctx.outputMode === "human") {
|
|
34351
|
+
return {
|
|
34352
|
+
error: interactiveRunnerMessages.stdoutIsATerminal("--out"),
|
|
34353
|
+
exitCode: exitCodes.invalidArgs
|
|
34354
|
+
};
|
|
34355
|
+
}
|
|
33570
34356
|
const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.takeScreenshot, { id: resolved.runnerId }, runnerCallOptions);
|
|
33571
34357
|
if (!result.ok) {
|
|
33572
34358
|
return { ...failureFields(result), exitCode: exitCodes.network };
|
|
@@ -33576,16 +34362,13 @@ async function handleRunnerScreenshot(ctx, options, deps) {
|
|
|
33576
34362
|
imageJpegBase64: result.value.imageJpegBase64,
|
|
33577
34363
|
path: options.out
|
|
33578
34364
|
});
|
|
33579
|
-
if (!written.ok)
|
|
33580
|
-
return written.
|
|
33581
|
-
|
|
33582
|
-
|
|
33583
|
-
|
|
33584
|
-
|
|
33585
|
-
exitCode: exitCodes.invalidArgs
|
|
33586
|
-
};
|
|
34365
|
+
if (!written.ok)
|
|
34366
|
+
return describeUnwritten(written, options.out);
|
|
34367
|
+
if (options.out === stdoutPath) {
|
|
34368
|
+
ctx.ui.success(interactiveRunnerMessages.screenshotWrittenToStdout);
|
|
34369
|
+
} else {
|
|
34370
|
+
ctx.ui.output({ outcome: "success", path: options.out }, interactiveRunnerMessages.screenshotWritten(options.out));
|
|
33587
34371
|
}
|
|
33588
|
-
ctx.ui.output({ outcome: "success", path: options.out }, interactiveRunnerMessages.screenshotWritten(options.out));
|
|
33589
34372
|
return;
|
|
33590
34373
|
}
|
|
33591
34374
|
const { failureReason } = result.value;
|
|
@@ -33618,13 +34401,27 @@ async function handleRunnerScreenshot(ctx, options, deps) {
|
|
|
33618
34401
|
}
|
|
33619
34402
|
}
|
|
33620
34403
|
}
|
|
34404
|
+
function describeUnwritten(written, out) {
|
|
34405
|
+
if (written.reason === "not-a-jpeg") {
|
|
34406
|
+
return {
|
|
34407
|
+
error: interactiveRunnerMessages.screenshotNotAnImage,
|
|
34408
|
+
exitCode: exitCodes.network
|
|
34409
|
+
};
|
|
34410
|
+
}
|
|
34411
|
+
return {
|
|
34412
|
+
error: out === stdoutPath ? interactiveRunnerMessages.screenshotStdoutUnwritable(written.detail) : interactiveRunnerMessages.screenshotUnwritable(out, written.detail),
|
|
34413
|
+
exitCode: exitCodes.invalidArgs
|
|
34414
|
+
};
|
|
34415
|
+
}
|
|
33621
34416
|
|
|
33622
34417
|
// src/commands/runner/interact.register.ts
|
|
33623
34418
|
var defaultScreenshotPath = "screenshot.jpg";
|
|
33624
34419
|
var screenshotExamples = `
|
|
33625
34420
|
Examples:
|
|
33626
34421
|
$ qawolf runner screenshot
|
|
33627
|
-
$ qawolf runner screenshot --out screens/step-3.jpg
|
|
34422
|
+
$ qawolf runner screenshot --out screens/step-3.jpg
|
|
34423
|
+
$ qawolf runner screenshot --out - > step-3.jpg
|
|
34424
|
+
$ qawolf runner screenshot --out - | my-vision-tool`;
|
|
33628
34425
|
var actExamples = `
|
|
33629
34426
|
Examples:
|
|
33630
34427
|
$ qawolf runner act click --button left --x 480 --y 260
|
|
@@ -33632,15 +34429,12 @@ Examples:
|
|
|
33632
34429
|
$ qawolf runner act keypress --keys Control a
|
|
33633
34430
|
$ qawolf runner act navigate --url https://example.com
|
|
33634
34431
|
$ qawolf runner act drag --path '[{"x":10,"y":20},{"x":80,"y":90}]'
|
|
33635
|
-
$
|
|
33636
|
-
|
|
33637
|
-
|
|
33638
|
-
$ qawolf runner exec snippet.ts
|
|
33639
|
-
$ echo 'console.log(await page.title())' | qawolf runner exec -
|
|
33640
|
-
$ qawolf runner exec snippet.ts --file flows/checkout.flow.ts`;
|
|
34432
|
+
$ qawolf runner act click --button left --x 480 --y 260 --screenshot step-4.jpg
|
|
34433
|
+
$ echo '{"type":"click","button":"left","x":1,"y":2}' | qawolf runner act -
|
|
34434
|
+
$ echo '{"type":"click","button":"left","x":1,"y":2}' | qawolf runner act - --screenshot - > step-5.jpg`;
|
|
33641
34435
|
function registerRunnerInteractCommands(runner, signals) {
|
|
33642
|
-
declareCommandKind(runner.command("screenshot"), "read").description("Save a JPEG of an interactive runner's screen to a file").option("--out <path>", "File to write the image to", defaultScreenshotPath).option("--runner <id>", runnerFlagDescription).addHelpText("after", screenshotExamples).action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerScreenshot(ctx, { out: opts.out, runner: opts.runner }, runnerDeps(ctx)))(opts, command));
|
|
33643
|
-
declareCommandKind(runner.command("act <action>"), "write").description("Perform one raw action on a runner's screen: click, double_click, scroll, move, drag, keypress, navigate or type. Use - to read a whole action as JSON from stdin. On a mobile runner only click (button left), drag and type have a touchscreen equivalent; the rest answer action-not-supported-on-mobile").option("--button <button>", "click: left, right, wheel, back or forward (mobile: left only)").option("--keys <keys...>", "keypress: modifiers and the key, e.g. Control a").option("--path <json>", "drag: JSON array of points to drag through (mobile: only the first and last are used)").option("--runner <id>", runnerFlagDescription).option("--scroll-x <delta>", "scroll: horizontal wheel delta").option("--scroll-y <delta>", "scroll: vertical wheel delta").option("--text <text>", "type: the text to type").option("--url <url>", "navigate: the http or https URL to go to").option("--x <pixels>", "pointer x, in screenshot pixels").option("--y <pixels>", "pointer y, in screenshot pixels").addHelpText("after", actExamples).action((action, opts, command) => withAuthContext(signals, (ctx) => handleRunnerAct(ctx, {
|
|
34436
|
+
declareCommandKind(runner.command("screenshot"), "read").description("Save a JPEG of an interactive runner's screen to a file, or write it to stdout with --out -").option("--out <path>", "File to write the image to. - writes the JPEG bytes to stdout on their own and moves the confirmation, JSON included, to stderr", defaultScreenshotPath).option("--runner <id>", runnerFlagDescription).addHelpText("after", screenshotExamples).action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerScreenshot(ctx, { out: opts.out, runner: opts.runner }, runnerDeps(ctx)))(opts, command));
|
|
34437
|
+
declareCommandKind(runner.command("act <action>"), "write").description("Perform one raw action on a runner's screen: click, double_click, scroll, move, drag, keypress, navigate or type. Use - to read a whole action as JSON from stdin. On a mobile runner only click (button left), drag and type have a touchscreen equivalent; the rest answer action-not-supported-on-mobile").option("--button <button>", "click: left, right, wheel, back or forward (mobile: left only)").option("--keys <keys...>", "keypress: modifiers and the key, e.g. Control a").option("--path <json>", "drag: JSON array of points to drag through (mobile: only the first and last are used)").option("--runner <id>", runnerFlagDescription).option("--screenshot <path>", "Also save a JPEG of the screen, taken after the action, to this file, in place of a separate screenshot. An action that did not take effect answers with one too. - writes it to stdout and moves the confirmation, JSON included, to stderr").option("--scroll-x <delta>", "scroll: horizontal wheel delta").option("--scroll-y <delta>", "scroll: vertical wheel delta").option("--text <text>", "type: the text to type").option("--url <url>", "navigate: the http or https URL to go to").option("--x <pixels>", "pointer x, in screenshot pixels").option("--y <pixels>", "pointer y, in screenshot pixels").addHelpText("after", actExamples).action((action, opts, command) => withAuthContext(signals, (ctx) => handleRunnerAct(ctx, {
|
|
33644
34438
|
flags: {
|
|
33645
34439
|
button: opts.button,
|
|
33646
34440
|
keys: opts.keys,
|
|
@@ -33653,9 +34447,9 @@ function registerRunnerInteractCommands(runner, signals) {
|
|
|
33653
34447
|
y: opts.y
|
|
33654
34448
|
},
|
|
33655
34449
|
runner: opts.runner,
|
|
34450
|
+
screenshot: opts.screenshot,
|
|
33656
34451
|
type: action
|
|
33657
34452
|
}, runnerDeps(ctx)))(opts, command));
|
|
33658
|
-
declareCommandKind(runner.command("exec <file>"), "write").description("Evaluate a snippet against a runner's live page. Use - to read the snippet from stdin").option("--file <path>", "File whose scope the snippet is evaluated in; it and the directory's other files travel with it").option("--runner <id>", runnerFlagDescription).addHelpText("after", execExamples).action((file, opts, command) => withAuthContext(signals, (ctx) => handleRunnerExec(ctx, { contextFile: opts.file, runner: opts.runner, source: file }, runnerDeps(ctx)))(opts, command));
|
|
33659
34453
|
}
|
|
33660
34454
|
|
|
33661
34455
|
// src/domains/interactiveRunner/readJournal.ts
|
|
@@ -34610,6 +35404,7 @@ function registerRunnerCommand(program, signals) {
|
|
|
34610
35404
|
registerRunCommand(runner, signals);
|
|
34611
35405
|
registerRunnerEventsCommand(runner, signals);
|
|
34612
35406
|
registerRunnerInteractCommands(runner, signals);
|
|
35407
|
+
registerRunnerExecCommand(runner, signals);
|
|
34613
35408
|
registerRunnerInspectCommands(runner, signals);
|
|
34614
35409
|
registerRunnerImportPackageCommand(runner, signals);
|
|
34615
35410
|
registerRunnerHighlightSelectorCommand(runner, signals);
|
|
@@ -34643,4 +35438,4 @@ createProgram({ signals }).parseAsync().catch(() => {
|
|
|
34643
35438
|
process.exitCode = 1;
|
|
34644
35439
|
}).finally(() => exitWhenIdle(typeof process.exitCode === "number" ? process.exitCode : 0));
|
|
34645
35440
|
|
|
34646
|
-
//# debugId=
|
|
35441
|
+
//# debugId=AAB38BDE68A61B2564756E2164756E21
|