@qawolf/cli 1.35.0 → 1.36.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.
@@ -2462,7 +2462,6 @@ var bigint = /^-?\d+n?$/;
2462
2462
  var integer = /^-?\d+$/;
2463
2463
  var number = /^-?\d+(?:\.\d+)?$/;
2464
2464
  var boolean = /^(?:true|false)$/i;
2465
- var _null = /^null$/i;
2466
2465
  var lowercase = /^[^A-Z]*$/;
2467
2466
  var uppercase = /^[^a-z]*$/;
2468
2467
 
@@ -3383,23 +3382,6 @@ var $ZodBigInt = /* @__PURE__ */ $constructor("$ZodBigInt", (inst, def) => {
3383
3382
  return payload;
3384
3383
  };
3385
3384
  });
3386
- var $ZodNull = /* @__PURE__ */ $constructor("$ZodNull", (inst, def) => {
3387
- $ZodType.init(inst, def);
3388
- inst._zod.pattern = _null;
3389
- inst._zod.values = new Set([null]);
3390
- inst._zod.parse = (payload, _ctx) => {
3391
- const input = payload.value;
3392
- if (input === null)
3393
- return payload;
3394
- payload.issues.push({
3395
- expected: "null",
3396
- code: "invalid_type",
3397
- input,
3398
- inst
3399
- });
3400
- return payload;
3401
- };
3402
- });
3403
3385
  var $ZodUnknown = /* @__PURE__ */ $constructor("$ZodUnknown", (inst, def) => {
3404
3386
  $ZodType.init(inst, def);
3405
3387
  inst._zod.parse = (payload) => payload;
@@ -4884,12 +4866,6 @@ function _coercedBigint(Class, params) {
4884
4866
  ...normalizeParams(params)
4885
4867
  });
4886
4868
  }
4887
- function _null2(Class, params) {
4888
- return new Class({
4889
- type: "null",
4890
- ...normalizeParams(params)
4891
- });
4892
- }
4893
4869
  function _unknown(Class) {
4894
4870
  return new Class({
4895
4871
  type: "unknown"
@@ -6519,14 +6495,6 @@ var ZodBigInt = /* @__PURE__ */ $constructor("ZodBigInt", (inst, def) => {
6519
6495
  inst.maxValue = bag.maximum ?? null;
6520
6496
  inst.format = bag.format ?? null;
6521
6497
  });
6522
- var ZodNull = /* @__PURE__ */ $constructor("ZodNull", (inst, def) => {
6523
- $ZodNull.init(inst, def);
6524
- ZodType.init(inst, def);
6525
- inst._zod.processJSONSchema = (ctx, json, params) => nullProcessor(inst, ctx, json, params);
6526
- });
6527
- function _null3(params) {
6528
- return _null2(ZodNull, params);
6529
- }
6530
6498
  var ZodUnknown = /* @__PURE__ */ $constructor("ZodUnknown", (inst, def) => {
6531
6499
  $ZodUnknown.init(inst, def);
6532
6500
  ZodType.init(inst, def);
@@ -6957,12 +6925,6 @@ function refine(fn, _params = {}) {
6957
6925
  function superRefine(fn, params) {
6958
6926
  return _superRefine(fn, params);
6959
6927
  }
6960
- function json(params) {
6961
- const jsonSchema = lazy(() => {
6962
- return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record(string2(), jsonSchema)]);
6963
- });
6964
- return jsonSchema;
6965
- }
6966
6928
  // node_modules/zod/v4/classic/coerce.js
6967
6929
  var exports_coerce = {};
6968
6930
  __export(exports_coerce, {
@@ -7009,6 +6971,75 @@ var agentReplySchema = object({
7009
6971
  var maxAgentWaitSeconds = 45;
7010
6972
  var agentWakeReasonSchema = _enum(["reply", "status", "timeout"]).describe('Why a held check answered. "reply" means the AI said something new, "status" means the session settled or needs you, "timeout" means the hold ran out with nothing new.');
7011
6973
 
6974
+ // node_modules/@qawolf/api-contracts/dist/v1/run/summary.js
6975
+ var publicRunStatusValues = [
6976
+ "queued",
6977
+ "running",
6978
+ "passed",
6979
+ "failed",
6980
+ "canceled"
6981
+ ];
6982
+ var makeRunSummaryFields = (ids) => ({
6983
+ completedAt: exports_iso.datetime().optional().describe("When the run finished executing. Absent while queued or running, and also absent for a terminal run that never completed execution (e.g. every flow was canceled or skipped)."),
6984
+ createdAt: exports_iso.datetime(),
6985
+ git: object({
6986
+ branch: string2().optional(),
6987
+ commitMessage: string2().optional(),
6988
+ commitSha: string2().optional(),
6989
+ commitUrl: string2().optional().describe("Link to the commit on the code host.")
6990
+ }).describe("The branch and commit under test. The fields are present when a deploy notification started the run, and absent for runs started another way, for example manually or with run.create."),
6991
+ needsReview: boolean2().optional().describe("True once every flow has finished its attempts and at least one failed flow still has no diagnosis, including one whose investigation carried over to a later run. `status` stays `running` until that failure is diagnosed, so a caller that is not waiting for a diagnosis can stop polling here and read the run's failed flows."),
6992
+ runId: ids.run,
6993
+ status: _enum(publicRunStatusValues).describe("Whole-run status, and the value to gate a pipeline on. Terminal statuses are passed, failed, and canceled; poll until one is reached. `failed` means the run found a bug that is still open and serious enough to block — a lower-priority bug, or one since resolved, reads `passed`, and the per-flow diagnoses are still in `flows`. The run stays `running` while any investigation is unresolved, including one that carried over to a later run, so a terminal status always means a settled answer. `needsReview` tells such a run apart from one that is still executing.")
6994
+ });
6995
+
6996
+ // node_modules/@qawolf/api-contracts/dist/v1/deployment/didNotRunReason.js
6997
+ var didNotRunReasonSchema = _enum([
6998
+ "billing-prevented",
6999
+ "branch-still-syncing",
7000
+ "deployment-no-longer-live",
7001
+ "duplicate-run",
7002
+ "environment-not-linked-to-branch",
7003
+ "environment-not-ready",
7004
+ "environment-terminated",
7005
+ "internal-error",
7006
+ "low-risk-change",
7007
+ "no-flows-to-run",
7008
+ "no-matching-trigger",
7009
+ "no-pull-request",
7010
+ "no-relevant-flows",
7011
+ "rate-limited",
7012
+ "run-not-created",
7013
+ "skipped",
7014
+ "test-configuration-error",
7015
+ "trigger-not-found",
7016
+ "workspace-inactive",
7017
+ "workspace-not-ready"
7018
+ ]).describe([
7019
+ "Why the matched trigger created no run, as a stable value safe to branch on in a CI pipeline. The accompanying `message` is the wording for a person to read and may be reworded at any time; this value will not be.",
7020
+ "billing-prevented: a billing restriction on the workspace stopped the run.",
7021
+ "branch-still-syncing: the environment was still syncing its flows.",
7022
+ "deployment-no-longer-live: the deployment was torn down or replaced by a newer one before the run started.",
7023
+ "duplicate-run: another run already covered this same deployment.",
7024
+ "environment-not-linked-to-branch: the environment has no branch to take flows from yet.",
7025
+ "environment-not-ready: the environment was still being prepared; retrying later can succeed.",
7026
+ "environment-terminated: the environment for the run is terminated.",
7027
+ "internal-error: QA Wolf could not start the run and the cause is on our side.",
7028
+ "low-risk-change: adaptive flow selection assessed the change as low risk, so nothing needed to run.",
7029
+ "no-flows-to-run: no flow was eligible, because the trigger's flows are in maintenance, still drafts, or missing on this branch.",
7030
+ "no-matching-trigger: no deployment trigger matched.",
7031
+ "no-pull-request: the trigger uses adaptive flow selection, which needs an open pull request, and this deployment had none.",
7032
+ "no-relevant-flows: adaptive flow selection found no existing flow covering this change.",
7033
+ "rate-limited: too many runs were requested at once; retrying later can succeed.",
7034
+ "run-not-created: the run never reached a decision.",
7035
+ "skipped: the run was skipped for a reason with no more specific value.",
7036
+ "test-configuration-error: the workspace's flows or dependency rules need fixing before this can run.",
7037
+ "trigger-not-found: the trigger no longer exists.",
7038
+ "workspace-inactive: the workspace is not active.",
7039
+ "workspace-not-ready: the workspace has not finished setting up."
7040
+ ].join(`
7041
+ `));
7042
+
7012
7043
  // node_modules/@qawolf/api-contracts/dist/v1/resource.js
7013
7044
  function resource(fields, { urlFieldDescription = "Absolute URL." } = {}) {
7014
7045
  return object({
@@ -7351,7 +7382,8 @@ var runnerNameSchema = _enum([
7351
7382
  "basic",
7352
7383
  "playwright",
7353
7384
  "android",
7354
- "ios"
7385
+ "ios",
7386
+ "windows"
7355
7387
  ]);
7356
7388
  var runnerWorkspaceRequirement = "Read it from whoami.";
7357
7389
  var runnerWorkspaceIdDescription = `The workspace the runner belongs to. ${runnerWorkspaceRequirement}`;
@@ -7473,7 +7505,11 @@ function makeRunnerFailureSchema(reasons) {
7473
7505
 
7474
7506
  // node_modules/@qawolf/api-contracts/dist/v1/runner/inspectMobile.js
7475
7507
  var maxElementTextLength = 500;
7476
- var selectorStrategySchema = _enum(["ios-predicate", "shadow", "xpath"]);
7508
+ var selectorStrategySchema = _enum([
7509
+ "ios-predicate",
7510
+ "shadow",
7511
+ "xpath"
7512
+ ]);
7477
7513
  var elementsRequestSchema = discriminatedUnion("by", [
7478
7514
  object({
7479
7515
  by: literal("point"),
@@ -7638,6 +7674,65 @@ var runFilesSchema = record(string2(), string2()).superRefine((files, refinement
7638
7674
  }).refine((files) => runFilesByteLength(files) <= maxRunFilesByteLength, {
7639
7675
  error: `The files in one request may carry at most ${maxRunFilesByteLength} bytes in total.`
7640
7676
  });
7677
+ // node_modules/@qawolf/api-contracts/dist/v1/runner/runnerAction.js
7678
+ var maxSwipeDurationMs = 1e4;
7679
+ var selectorSchema2 = string2().min(1).max(maxSelectorLength).describe("The element to act on, resolved the way a screen object's own selector is.");
7680
+ var strategySchema = selectorStrategySchema.optional().describe("How `selector` is resolved. Defaults to `xpath`.");
7681
+ function refuseStrategyWithoutSelector(action, context) {
7682
+ if (action.strategy !== undefined && action.selector === undefined) {
7683
+ context.addIssue({
7684
+ code: "custom",
7685
+ message: "`strategy` says how to resolve a `selector`, so it needs one.",
7686
+ path: ["strategy"]
7687
+ });
7688
+ }
7689
+ }
7690
+ var tapActionSchema = strictObject({
7691
+ selector: selectorSchema2.optional(),
7692
+ strategy: strategySchema,
7693
+ type: literal("tap"),
7694
+ x: screenCoordinateSchema.optional(),
7695
+ y: screenCoordinateSchema.optional()
7696
+ }).superRefine((tap, context) => {
7697
+ refuseStrategyWithoutSelector(tap, context);
7698
+ const hasPoint = tap.x !== undefined || tap.y !== undefined;
7699
+ if (tap.selector !== undefined && hasPoint) {
7700
+ context.addIssue({
7701
+ code: "custom",
7702
+ message: "A tap aims at `x` and `y` or at a `selector`, not both.",
7703
+ path: ["selector"]
7704
+ });
7705
+ }
7706
+ if (tap.selector === undefined && (tap.x === undefined || tap.y === undefined)) {
7707
+ context.addIssue({
7708
+ code: "custom",
7709
+ message: "A tap needs both `x` and `y`, or a `selector`.",
7710
+ path: [tap.x === undefined ? "x" : "y"]
7711
+ });
7712
+ }
7713
+ });
7714
+ var swipeActionSchema = strictObject({
7715
+ duration_ms: int().positive().max(maxSwipeDurationMs).optional().describe("How long the swipe takes, in milliseconds. A slow one scrolls, a fast one flings. The device's default is 300."),
7716
+ from: screenPointSchema,
7717
+ to: screenPointSchema,
7718
+ type: literal("swipe")
7719
+ }).refine(({ from, to }) => from.x !== to.x || from.y !== to.y, {
7720
+ message: "A swipe whose `from` and `to` are the same travels nowhere.",
7721
+ path: ["to"]
7722
+ });
7723
+ var fillActionSchema = strictObject({
7724
+ selector: selectorSchema2,
7725
+ strategy: strategySchema,
7726
+ text: string2().max(maxTypedTextLength).describe("What the field holds afterwards, in place of its old value. Empty clears it."),
7727
+ type: literal("fill")
7728
+ });
7729
+ var runnerActionSchema = discriminatedUnion("type", [
7730
+ ...browserActionSchema.options,
7731
+ tapActionSchema,
7732
+ swipeActionSchema,
7733
+ fillActionSchema
7734
+ ]);
7735
+
7641
7736
  // node_modules/@qawolf/api-contracts/dist/v1/runner/screen.js
7642
7737
  var screenFailureReasons = [
7643
7738
  "runner-has-no-screen",
@@ -7646,17 +7741,18 @@ var screenFailureReasons = [
7646
7741
  ];
7647
7742
  var screenNotReadyDescription = "`screen-not-ready` if the runner has a screen that cannot serve this instant. Retry in a second or two: the desktop restarts when a run changes the display size, and it serves one see-or-act request at a time, so a screenshot or action already in flight is the usual reason. Do not submit a run to clear this — a run may restart the display and discard what is on it.";
7648
7743
  var screenNeedsARunDescription = "`screen-needs-a-run` if the runner's virtual desktop has never started. Waiting will not change this and retrying is pointless — call `runner.runFlow` with a flow that opens a browser, then ask for the screen again. On a runner image with a browser that has never run anything, any `runner.performAction` also starts the browser itself. Evaluating a snippet does not start the desktop.";
7649
- var runnerHasNoScreenDescription = "`runner-has-no-screen` if this runner is not one that runs a browser on a virtual desktop. Nothing about it can be seen or driven, and retrying will never help — launch a `playwright` runner instead.";
7744
+ var runnerHasNoScreenDescription = "`runner-has-no-screen` if this runner has no browser desktop to see or drive, and retrying will never help. Launch a `playwright` runner, or on `windows` use `runner.runFlow`.";
7650
7745
  var screenshotImageSchema = string2().min(1).describe("The screenshot, as a base64-encoded JPEG. Decode it and write the bytes to a `.jpg` file — writing this string to the file leaves you with base64 text rather than an image.");
7651
7746
 
7652
7747
  // node_modules/@qawolf/api-contracts/dist/v1/runner/performAction.js
7653
7748
  var unreachableDescription = "`runner-unreachable` if the runner could not be reached: it may still be starting, it may have terminated after inactivity, or it may have stopped answering mid-action. This does not mean the action was not performed — take a screenshot before repeating it.";
7654
- var notSupportedOnMobileDescription = "`action-not-supported-on-mobile` if the runner is a mobile device and this action has no touchscreen equivalent: `double_click`, `scroll`, `move`, `keypress` and `navigate`, and a `click` whose `button` is not `left`, are all pointer-device concepts a touchscreen has nothing to offer for. `click` taps, `drag` swipes between its path's first and last point, and `type` types into whatever the last tap focused.";
7749
+ var notSupportedOnMobileDescription = "`action-not-supported-on-mobile` if the runner is a mobile device and this action has no touchscreen equivalent: `double_click`, `scroll`, `move`, `keypress` and `navigate`, and a `click` whose `button` is not `left`, are all pointer-device concepts a touchscreen has nothing to offer for. `type` types into whatever the last tap focused. Deprecated on mobile and to be removed: `click` taps and `drag` swipes between its path's first and last point; send `tap` and `swipe` instead.";
7750
+ var mobileActionsDescription = "Only a mobile runner performs `tap`, `swipe` and `fill`: `tap` touches a point or the element a `selector` names, `swipe` moves from one point to another over `duration_ms`, and `fill` replaces the value of the field a `selector` names. A browser runner answers them with `action-not-supported-on-browser`.";
7655
7751
  var withScreenshotDescription = "With `withScreenshot: true`, the answer also carries `imageJpegBase64`: a screenshot taken after the action. One call instead of `runner.performAction` followed by `runner.takeScreenshot`. The action itself is performed exactly as without the option; if the screen could serve no frame after it — a display restart, or a `navigate` on a desktop that is still starting — the action's result comes back without `imageJpegBase64`, so take a screenshot separately rather than repeating the action. On a mobile runner the screenshot is the device's own, taken right after a performed action.";
7656
7752
  var maxActionErrorMessageLength = 1000;
7657
7753
  var makePerformActionOnRunnerContract = (ids) => {
7658
7754
  const input = object({
7659
- action: browserActionSchema.describe(`The action to perform. Coordinates are whole pixels on the runner's virtual desktop, in the same space as \`runner.takeScreenshot\`. The action shapes follow the computer-use vocabulary, minus \`screenshot\` (use \`runner.takeScreenshot\`) and \`wait\` (delay on the caller's side). A \`navigate\` does not go through the screen, so a screen that is not ready does not stop it. ${notSupportedOnMobileDescription}`),
7755
+ action: runnerActionSchema.describe(`The action to perform. Coordinates are whole pixels on the runner's virtual desktop, in the same space as \`runner.takeScreenshot\`. The browser action shapes follow the computer-use vocabulary, minus \`screenshot\` (use \`runner.takeScreenshot\`) and \`wait\` (delay on the caller's side). A \`navigate\` does not go through the screen, so a screen that is not ready does not stop it. ${notSupportedOnMobileDescription} ${mobileActionsDescription}`),
7660
7756
  id: runnerIdSchema.describe("Id of the runner to act on."),
7661
7757
  withScreenshot: boolean2().optional().describe(withScreenshotDescription),
7662
7758
  workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
@@ -7672,6 +7768,7 @@ var makePerformActionOnRunnerContract = (ids) => {
7672
7768
  makeRunnerFailureSchema([
7673
7769
  ...screenFailureReasons,
7674
7770
  "action-not-supported-on-mobile",
7771
+ "action-not-supported-on-browser",
7675
7772
  runnerUnreachableFailureReason
7676
7773
  ])
7677
7774
  ]);
@@ -7688,7 +7785,7 @@ var makePerformActionOnRunnerContract = (ids) => {
7688
7785
  openWorldHint: true,
7689
7786
  readOnlyHint: false
7690
7787
  },
7691
- description: `Perform one raw browser action on an interactive runner: click, double_click, move, drag, scroll, keypress, type, or navigate. One action per request, and the runner serves one at a time. 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} ${runnerHasNoScreenDescription} ${unreachableDescription}`,
7788
+ description: `Perform one raw action on an interactive runner: click, double_click, move, drag, scroll, keypress, type, or navigate; on mobile, tap, swipe, fill, or type. One action per request, and the runner serves one at a time. 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} ${runnerHasNoScreenDescription} ${unreachableDescription}`,
7692
7789
  input,
7693
7790
  kind: "write",
7694
7791
  name: "runner.performAction",
@@ -7951,7 +8048,7 @@ var makeTriggerScheduleFields = (ids) => ({
7951
8048
  environmentId: ids.environmentId.describe("The environment the scheduled runs happen in."),
7952
8049
  minuteOfHour: number2().int().min(0).max(59).optional().describe("Minutes past the hour an hourly schedule fires. Defaults to 0."),
7953
8050
  timeOfDay: string2().regex(/^([01]\d|2[0-3]):[0-5]\d$/).optional().describe('The wall clock time a daily or weekly schedule fires, as "HH:mm". Required when cadence is daily or weekly.'),
7954
- timezoneId: string2().min(1).refine(isTimezone, "Expected an IANA timezone, such as America/New_York.").optional().describe("The IANA timezone the schedule is read in, such as America/New_York. Required when cadence is daily or weekly, and when an hourly schedule names daysOfWeek, whose day boundaries it sets. An hourly schedule firing on every day needs none, and is read in America/Los_Angeles.")
8051
+ timezoneId: string2().min(1).refine(isTimezone, "Expected an IANA timezone, such as America/New_York.").optional().describe("The IANA timezone the schedule is read in, such as America/New_York. Required for every cadence: it sets the day boundaries daysOfWeek is read against, and the wall clock timeOfDay is read against.")
7955
8052
  });
7956
8053
  var makeTriggerDeploymentFields = (ids) => ({
7957
8054
  action: makeTriggerActionSchema(ids),
@@ -7992,12 +8089,12 @@ var checkAction = (action, ctx) => {
7992
8089
  };
7993
8090
  var requiredScheduleFields = {
7994
8091
  daily: ["timeOfDay", "timezoneId"],
7995
- hourly: [],
8092
+ hourly: ["timezoneId"],
7996
8093
  weekly: ["dayOfWeek", "timeOfDay", "timezoneId"]
7997
8094
  };
7998
8095
  var optionalScheduleFields = {
7999
8096
  daily: ["daysOfWeek"],
8000
- hourly: ["daysOfWeek", "minuteOfHour", "timezoneId"],
8097
+ hourly: ["daysOfWeek", "minuteOfHour"],
8001
8098
  weekly: []
8002
8099
  };
8003
8100
  var cadencesUsingField = {
@@ -8038,18 +8135,8 @@ var checkScheduleFields = (value, ctx) => {
8038
8135
  }
8039
8136
  }
8040
8137
  };
8041
- var checkHourlyZone = (value, ctx) => {
8042
- if (value.cadence !== "hourly" || value.daysOfWeek === undefined || value.timezoneId !== undefined)
8043
- return;
8044
- ctx.addIssue({
8045
- code: "custom",
8046
- message: "timezoneId is required when an hourly cadence names daysOfWeek.",
8047
- path: ["timezoneId"]
8048
- });
8049
- };
8050
8138
  var checkSchedule = (value, ctx) => {
8051
8139
  checkScheduleFields(value, ctx);
8052
- checkHourlyZone(value, ctx);
8053
8140
  if (value.action.kind === "generativeSuite") {
8054
8141
  ctx.addIssue({
8055
8142
  code: "custom",
@@ -8452,53 +8539,6 @@ var makeFindDeploymentsContract = (ids) => {
8452
8539
  };
8453
8540
  };
8454
8541
 
8455
- // node_modules/@qawolf/api-contracts/dist/v1/deployment/didNotRunReason.js
8456
- var didNotRunReasonSchema = _enum([
8457
- "billing-prevented",
8458
- "branch-still-syncing",
8459
- "deployment-no-longer-live",
8460
- "duplicate-run",
8461
- "environment-not-linked-to-branch",
8462
- "environment-not-ready",
8463
- "environment-terminated",
8464
- "internal-error",
8465
- "low-risk-change",
8466
- "no-flows-to-run",
8467
- "no-matching-trigger",
8468
- "no-pull-request",
8469
- "no-relevant-flows",
8470
- "rate-limited",
8471
- "run-not-created",
8472
- "skipped",
8473
- "test-configuration-error",
8474
- "trigger-not-found",
8475
- "workspace-inactive",
8476
- "workspace-not-ready"
8477
- ]).describe([
8478
- "Why the matched trigger created no run, as a stable value safe to branch on in a CI pipeline. The accompanying `message` is the wording for a person to read and may be reworded at any time; this value will not be.",
8479
- "billing-prevented: a billing restriction on the workspace stopped the run.",
8480
- "branch-still-syncing: the environment was still syncing its flows.",
8481
- "deployment-no-longer-live: the deployment was torn down or replaced by a newer one before the run started.",
8482
- "duplicate-run: another run already covered this same deployment.",
8483
- "environment-not-linked-to-branch: the environment has no branch to take flows from yet.",
8484
- "environment-not-ready: the environment was still being prepared; retrying later can succeed.",
8485
- "environment-terminated: the environment for the run is terminated.",
8486
- "internal-error: QA Wolf could not start the run and the cause is on our side.",
8487
- "low-risk-change: adaptive flow selection assessed the change as low risk, so nothing needed to run.",
8488
- "no-flows-to-run: no flow was eligible, because the trigger's flows are in maintenance, still drafts, or missing on this branch.",
8489
- "no-matching-trigger: no deployment trigger matched.",
8490
- "no-pull-request: the trigger uses adaptive flow selection, which needs an open pull request, and this deployment had none.",
8491
- "no-relevant-flows: adaptive flow selection found no existing flow covering this change.",
8492
- "rate-limited: too many runs were requested at once; retrying later can succeed.",
8493
- "run-not-created: the run never reached a decision.",
8494
- "skipped: the run was skipped for a reason with no more specific value.",
8495
- "test-configuration-error: the workspace's flows or dependency rules need fixing before this can run.",
8496
- "trigger-not-found: the trigger no longer exists.",
8497
- "workspace-inactive: the workspace is not active.",
8498
- "workspace-not-ready: the workspace has not finished setting up."
8499
- ].join(`
8500
- `));
8501
-
8502
8542
  // node_modules/@qawolf/api-contracts/dist/v1/deployment/listTriggerEvaluations.js
8503
8543
  var conditionKindSchema = _enum(["environment", "deployTarget", "branch", "service"]).describe("What the condition checks on the deployment: the environment it reported into, the deploy target it serves, the git branch of the deployed commit, or the service it deployed.");
8504
8544
  var conditionVerdictSchema = discriminatedUnion("outcome", [
@@ -9376,7 +9416,7 @@ var makeCreateIssueContract = (ids) => {
9376
9416
  const commonFields = {
9377
9417
  description: string2().optional().describe("The issue description as plain text; each line becomes a paragraph. Markdown is not parsed."),
9378
9418
  name: string2().trim().min(1).max(255),
9379
- priority: issuePrioritySchema.optional().describe('Defaults to "unprioritized".'),
9419
+ priority: issuePrioritySchema.optional().describe('Defaults to "unprioritized" for bug reports and coverage requests.'),
9380
9420
  workspaceId: ids.workspace.optional().describe("The workspace to create the issue in. Required when authenticating with an organization or user API key.")
9381
9421
  };
9382
9422
  const input = discriminatedUnion("type", [
@@ -9388,6 +9428,13 @@ var makeCreateIssueContract = (ids) => {
9388
9428
  ...commonFields,
9389
9429
  estimatedDueDate: exports_iso.date().optional().describe("The calendar date the coverage is estimated to be complete, as YYYY-MM-DD."),
9390
9430
  type: literal("coverageRequest")
9431
+ }),
9432
+ object({
9433
+ ...commonFields,
9434
+ effort: _enum(["extraSmall", "small", "medium", "large", "extraLarge"]).describe("The effort to repair the flows."),
9435
+ priority: _enum(["low", "medium", "high", "urgent"]),
9436
+ reason: _enum(["dataChanged", "flowNeedsWork", "other", "uiChanged"]).describe("Why the flows need maintenance."),
9437
+ type: literal("maintenance")
9391
9438
  })
9392
9439
  ]);
9393
9440
  const output = object({
@@ -9399,7 +9446,7 @@ var makeCreateIssueContract = (ids) => {
9399
9446
  openWorldHint: true,
9400
9447
  readOnlyHint: false
9401
9448
  },
9402
- description: "Create a bug or coverage request issue for the caller's team. " + "Maintenance issues cannot be created through the public API.",
9449
+ description: "Create a bug report, maintenance report, or coverage request issue for the caller's team. " + "A user can create a maintenance report only when they can see the workspace's maintenance reports; " + "a team or organization API key credits it to the workspace's automation user. " + "Link its failed flows afterwards with run.diagnose.",
9403
9450
  input,
9404
9451
  kind: "write",
9405
9452
  name: "issue.create",
@@ -9488,6 +9535,8 @@ var makeLegacyTriggerSchema = (ids) => {
9488
9535
  isPaused: boolean2(),
9489
9536
  name: string2(),
9490
9537
  pausedAt: exports_iso.datetime().optional(),
9538
+ runConcurrencyLimit: string2().describe(`How many of the trigger's runs may execute at once: a whole number, "unlimited", or a concurrency expression. A trigger that stores no limit reads as "unlimited", which leaves the environment's limit in charge. Copy a number or an expression onto the current trigger action's runConcurrencyLimit, and omit that field when this reads "unlimited".`),
9539
+ shouldTriage: boolean2().describe("Whether QA Wolf investigates the failures of the runs this trigger creates. It becomes the current trigger action's investigateFailures, which is on unless the trigger says otherwise."),
9491
9540
  tags: array(namedIdentity)
9492
9541
  };
9493
9542
  return union([
@@ -9604,27 +9653,6 @@ var makeDiagnoseRunContract = (ids) => {
9604
9653
  };
9605
9654
  };
9606
9655
 
9607
- // node_modules/@qawolf/api-contracts/dist/v1/run/summary.js
9608
- var publicRunStatusValues = [
9609
- "queued",
9610
- "running",
9611
- "passed",
9612
- "failed",
9613
- "canceled"
9614
- ];
9615
- var makeRunSummaryFields = (ids) => ({
9616
- completedAt: exports_iso.datetime().optional().describe("When the run finished executing. Absent while queued or running, and also absent for a terminal run that never completed execution (e.g. every flow was canceled or skipped)."),
9617
- createdAt: exports_iso.datetime(),
9618
- git: object({
9619
- branch: string2().optional(),
9620
- commitMessage: string2().optional(),
9621
- commitSha: string2().optional(),
9622
- commitUrl: string2().optional().describe("Link to the commit on the code host.")
9623
- }).describe("The branch and commit under test. The fields are present when a deploy notification started the run, and absent for runs started another way, for example manually or with run.create."),
9624
- runId: ids.run,
9625
- status: _enum(publicRunStatusValues).describe("Whole-run status, and the value to gate a pipeline on. Terminal statuses are passed, failed, and canceled; poll until one is reached. `failed` means the run found a bug that is still open and serious enough to block — a lower-priority bug, or one since resolved, reads `passed`, and the per-flow diagnoses are still in `flows`. The run stays `running` while any investigation is unresolved, including one that carried over to a later run, so a terminal status always means a settled answer.")
9626
- });
9627
-
9628
9656
  // node_modules/@qawolf/api-contracts/dist/v1/run/find.js
9629
9657
  var makeFindRunsContract = (ids) => {
9630
9658
  const input = object({
@@ -9782,54 +9810,6 @@ var makeStopRunContract = (ids) => {
9782
9810
  };
9783
9811
  };
9784
9812
 
9785
- // node_modules/@qawolf/api-contracts/dist/v1/run/triage.js
9786
- var probability = number2().min(0).max(1);
9787
- var verdictAnswer = object({
9788
- choice: _enum(["bug", "maintenance"]),
9789
- confidence: probability,
9790
- probabilities: record(string2(), probability),
9791
- type: literal("choice")
9792
- });
9793
- var makeTriageRunContract = (ids) => {
9794
- const input = object({
9795
- flowId: ids.flow.describe("The flow that failed in the run."),
9796
- runId: ids.run
9797
- });
9798
- const decision = discriminatedUnion("type", [
9799
- object({
9800
- diagnosis: _enum(["bug", "maintenance"]),
9801
- type: literal("diagnose")
9802
- }),
9803
- object({
9804
- diagnosis: _enum(["bug", "maintenance"]),
9805
- type: literal("suggest")
9806
- }),
9807
- object({
9808
- type: literal("needsHuman")
9809
- })
9810
- ]);
9811
- const output = object({
9812
- answers: object({
9813
- verdict: verdictAnswer
9814
- }),
9815
- decision: decision.describe("What the triage would do with the verdict. The prototype records nothing; it only reports."),
9816
- model: string2(),
9817
- state: json().describe("The context that was sent to the model, for inspection.")
9818
- });
9819
- return {
9820
- annotations: {
9821
- destructiveHint: false,
9822
- openWorldHint: false,
9823
- readOnlyHint: true
9824
- },
9825
- description: "Prototype. Ask Jev, a TypeSafe System One model, whether a flow failure in a run is a bug in the product " + "or a test that needs maintenance. Compares the failing attempt with the flow's last passing run in the " + "same environment. Records nothing.",
9826
- input,
9827
- kind: "read",
9828
- name: "run.triage",
9829
- output
9830
- };
9831
- };
9832
-
9833
9813
  // node_modules/@qawolf/api-contracts/dist/v1/runner/evaluateSnippet.js
9834
9814
  var maxSnippetCodeLength = 64 * 1024;
9835
9815
  var makeEvaluateSnippetOnRunnerContract = (ids) => {
@@ -10397,8 +10377,7 @@ var makeContractsV1 = (ids) => {
10397
10377
  find: makeFindRunsContract(resolvedIds),
10398
10378
  get: makeGetRunContract(resolvedIds),
10399
10379
  reattempt: makeReattemptRunContract(resolvedIds),
10400
- stop: makeStopRunContract(resolvedIds),
10401
- triage: makeTriageRunContract(resolvedIds)
10380
+ stop: makeStopRunContract(resolvedIds)
10402
10381
  },
10403
10382
  runner: makeRunnerContracts(resolvedIds),
10404
10383
  skill: makeSkillContracts(),
@@ -10711,7 +10690,8 @@ var interactMessages = {
10711
10690
  actionFlagsWithStdin: '"-" reads the whole action from stdin, so an action flag passed alongside it would be ignored. Pipe the action in on its own, or name the action type and use flags.',
10712
10691
  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.",
10713
10692
  actionNotJson: `Stdin did not hold a JSON action. Pipe one object, for example '{"type":"click","button":"left","x":480,"y":260}'.`,
10714
- 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.`,
10693
+ actionNotSupportedOnBrowser: (type) => `Only a mobile runner performs ${type}. On a browser runner, click at --x and --y, drag along a --path, and type into whatever has focus.`,
10694
+ actionNotSupportedOnMobile: (type) => `A mobile runner has a touchscreen, so it cannot perform ${type} as asked. Use tap, swipe, fill, or type into whatever the last tap focused.`,
10715
10695
  actionFailedScreenshotToStdout: "Its screen was written to stdout as a JPEG, so look at that rather than sending the action again.",
10716
10696
  actionFailedScreenshotUnwritten: (detail) => `Its screen could not be written, because ${detail}, so take one with qawolf runner screenshot rather than sending the action again.`,
10717
10697
  actionFailedScreenshotWritten: (path) => `Its screen was written to ${path}, so look at that rather than sending the action again.`,
@@ -15031,4 +15011,4 @@ export {
15031
15011
  createRunnerSdk
15032
15012
  };
15033
15013
 
15034
- //# debugId=CF6139275CB5B8DC64756E2164756E21
15014
+ //# debugId=CBFC365D381BD6E864756E2164756E21
@@ -1,4 +1,4 @@
1
- import type { BrowserAction, InspectOnRunnerRequest, JournalStream, PublicApiInput, PublicApiOutput, ReadJournalResponse, RunnerNameForPublicApi, publicContractsV1 } from "@qawolf/api-contracts/v1";
1
+ import type { InspectOnRunnerRequest, JournalStream, PublicApiInput, PublicApiOutput, ReadJournalResponse, RunnerAction, RunnerNameForPublicApi, publicContractsV1 } from "@qawolf/api-contracts/v1";
2
2
  type Runner = typeof publicContractsV1.runner;
3
3
  export type RunnerSdkOptions = {
4
4
  /** A QA Wolf team API key. The SDK never reads the CLI's stored credentials. */
@@ -77,7 +77,7 @@ export type EventsRequest = RunnerRequest & {
77
77
  window: JournalWindow;
78
78
  };
79
79
  export type ActRequest = RunnerRequest & {
80
- action: BrowserAction;
80
+ action: RunnerAction;
81
81
  /**
82
82
  * Ask the runner to answer with a screenshot taken after the action, on
83
83
  * `imageJpegBase64`. One call instead of an act and a screenshot, with no
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qawolf/cli",
3
- "version": "1.35.0",
3
+ "version": "1.36.0",
4
4
  "description": "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
5
5
  "keywords": [
6
6
  "automation",
@@ -71,7 +71,7 @@
71
71
  "@clack/prompts": "1.5.1",
72
72
  "@napi-rs/keyring": "1.3.0",
73
73
  "@oxc-node/core": "0.1.0",
74
- "@qawolf/api-contracts": "0.70.0",
74
+ "@qawolf/api-contracts": "0.73.0",
75
75
  "@qawolf/emails": "1.1.1",
76
76
  "@qawolf/flow-targets": "1.0.0",
77
77
  "@qawolf/flows": "0.1.4",
@@ -168,7 +168,7 @@ that `url`; never guess a route and never send a repository link in its place.
168
168
  | `qawolf install clear` | local | Remove the managed runtime cache (all installed runtime versions) |
169
169
  | `qawolf investigation get` | read | Read what the QA Wolf AI investigating a run has concluded: one finding per cause of its failures, with the question waiting for a person and any answer or dispute the investigation hasn't acted on yet. |
170
170
  | `qawolf issue addFlows` | write | 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. |
171
- | `qawolf issue create` | write | Create a bug or coverage request issue for the caller's team. Maintenance issues cannot be created through the public API. |
171
+ | `qawolf issue create` | write | Create a bug report, maintenance report, or coverage request issue for the caller's team. A user can create a maintenance report only when they can see the workspace's maintenance reports; a team or organization API key credits it to the workspace's automation user. Link its failed flows afterwards with run.diagnose. |
172
172
  | `qawolf issue find` | read | List the team's bug reports, maintenance reports, or coverage requests, newest first. |
173
173
  | `qawolf issue get` | read | Get an issue by id. |
174
174
  | `qawolf issue removeFlows` | write | 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. |
@@ -182,8 +182,7 @@ that `url`; never guess a route and never send a repository link in its place.
182
182
  | `qawolf run get` | read | Get a run's status, per-flow results, links, and how many of its bugs are blocking. |
183
183
  | `qawolf run reattempt` | write | Request new attempts for a run's flows, in the same run. A flow is eligible once its result is failed or canceled and QA Wolf's automatic retries have finished. A fully investigated run no longer accepts reattempts. Attempts run with the latest flow code. Poll run.get for results. |
184
184
  | `qawolf run stop` | write | Stop a run, including its queued flows and automatic retries. Stopping is asynchronous and can update run-status messages and commit statuses in connected integrations. Repeated requests are safe, and finished runs keep their results. A run that is still being created returns not found; retry once run.get returns the run. If run.get returns a different runId, use that ID. Poll run.get for results. |
185
- | `qawolf run triage` | read | Prototype. Ask Jev, a TypeSafe System One model, whether a flow failure in a run is a bug in the product or a test that needs maintenance. Compares the failing attempt with the flow's last passing run in the same environment. Records nothing. |
186
- | `qawolf runner act` | write | 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 |
185
+ | `qawolf runner act` | write | Perform one raw action on a runner's screen. A browser runner takes click, double_click, scroll, move, drag, keypress, navigate and type; a mobile runner takes tap, swipe, fill and type. A browser runner answers mobile actions with action-not-supported-on-browser; a mobile runner answers the other browser actions with action-not-supported-on-mobile. Use - to read a whole action as JSON from stdin |
187
186
  | `qawolf runner actions` | write | Perform a sequence of up to ten raw actions on a runner's screen in one request, as a JSON array of the same actions `runner act` takes. Use - to read the array from stdin. Actions run back to back, so batch only steps whose targets are on the screen you last saw, and end the sequence at the step that changes the page. Each result carries an effect: performed, not-performed, or unknown when the runner stopped answering and the action may have landed |
188
187
  | `qawolf runner events` | read | Print a runner's journal, one entry per line. QA Wolf writes console, recorder, run-events, run-logs, run-status |
189
188
  | `qawolf runner exec` | write | Evaluate a snippet against a runner's live page. Use - to read the snippet from stdin |
@@ -183,6 +183,7 @@ Every documented field of the `run.get` response. `[]` marks an array, so
183
183
  - `completedAt` — When the run finished executing. Absent while queued or running, and also absent for a terminal run that never completed execution (e.g. every flow was canceled or skipped).
184
184
  - `git` — The branch and commit under test. The fields are present when a deploy notification started the run, and absent for runs started another way, for example manually or with run.create.
185
185
  - `git.commitUrl` — Link to the commit on the code host.
186
+ - `needsReview` — True once every flow has finished its attempts and at least one failed flow still has no diagnosis, including one whose investigation carried over to a later run. `status` stays `running` until that failure is diagnosed, so a caller that is not waiting for a diagnosis can stop polling here and read the run's failed flows.
186
187
  - `runId` — 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.
187
188
  - `status` — One of: queued, running, passed, failed, canceled
188
189
  - `blockingBugCount` — How many bugs this run found are blocking: still open, and priority urgent, high, or unprioritized — unprioritized counts because nobody has ruled it out yet. This is what `failed` is derived from, so it explains a failure rather than adding a second verdict: gate on `status`, then read this to say how many bugs are holding the build. It counts a bug filed against a later run by an investigation that carried over from this one, and it drops a bug once that bug is resolved.
@@ -126,7 +126,7 @@ Retry on the exit code, not on the message text:
126
126
  launch the id or name one that is running. The message says which runner was
127
127
  meant and whether `--runner`, `QAWOLF_RUNNER_ID` or this directory's stored
128
128
  default chose it — read that line before you pick an id to launch.
129
- - `2` will not clear on its own. Nothing has run on this runner yet, so run a flow; or the runner has no browser at all, so launch with `--name playwright` instead; or the action has no touchscreen equivalent on a mobile runner, so reach for one that has. A `runner actions` sequence also exits `2` before it sends anything, for an argument that is not a JSON array, an array of more than ten actions, and `--screenshot` flags that contradict each other. The message says which.
129
+ - `2` will not clear on its own. Nothing has run on this runner yet, so run a flow; or the runner has no browser at all, so launch with `--name playwright` instead; or the action belongs to the other runner family, so send `tap`, `swipe` or `fill` to a mobile runner and the browser actions to a browser runner. A `runner actions` sequence also exits `2` before it sends anything, for an argument that is not a JSON array, an array of more than ten actions, and `--screenshot` flags that contradict each other. The message says which.
130
130
 
131
131
  ## Seeing and acting: the loop is yours
132
132
 
@@ -167,17 +167,18 @@ Coordinates are pixels on the same screenshot you just read. The runner serves o
167
167
 
168
168
  `act`, `actions`, `run` and `exec` are the commands whose lost answer may still have taken effect. On a `4` from `act` or `actions`, take a screenshot before repeating a click. `exec`'s message says the snippet could not be evaluated, but a lost answer looks the same from outside, so treat a `4` from a snippet that changes something as "may have run" rather than "did not run".
169
169
 
170
- A mobile runner has a touchscreen, not a mouse, so only three of the eight
171
- actions have a touchscreen equivalent and go through: `click` with
172
- `button: "left"` taps, `drag` swipes, and `type` types into whatever the last
173
- tap focused. The rest — `double_click`, `scroll`, `move`, `keypress`,
174
- `navigate` — answer `action-not-supported-on-mobile` rather than doing
175
- something approximate. `navigate` is the one to watch for, since it works on a
176
- browser runner without a run first but has no meaning on mobile at all.
170
+ A mobile runner has a touchscreen, not a mouse, so it has actions of its own:
171
+
172
+ - `tap` touches a point (`--x`, `--y`) or the element `--selector` names. Check the selector first with `inspect elements --selector`, and pass `--strategy ios-predicate` or `shadow` when it is not XPath.
173
+ - `swipe --from x,y --to x,y` moves in a straight line between two points. `--duration-ms` sets how long it takes, up to 10000: a slow swipe scrolls, a fast one flings.
174
+ - `fill --selector ... --text ...` replaces the value of that field, and `--text ""` clears it. To add to what a field already holds, `tap` it and then `type`.
175
+ - `type` types into whatever the last tap focused, the same as on a browser.
176
+
177
+ The browser actions `double_click`, `scroll`, `move`, `keypress` and `navigate` answer `action-not-supported-on-mobile` rather than doing something approximate. `navigate` is the one to watch for, since it works on a browser runner without a run first but has no meaning on mobile at all. `click` with `button: "left"` and `drag` still tap and swipe on mobile, but they are deprecated there, so send `tap` and `swipe`. A browser runner answers `tap`, `swipe` and `fill` with `action-not-supported-on-browser`.
177
178
 
178
179
  ### Several steps in one request: `runner actions`
179
180
 
180
- `qawolf runner actions '<json array>'` performs up to ten of those same actions back to back in one request, for the steps you already know: click the field, type into it, press Enter. One round trip instead of three, with no delay to guess at between them, and `-` reads the array from stdin the way `act -` reads one action.
181
+ `qawolf runner actions '<json array>'` performs up to ten of the browser actions back to back in one request (not `tap`, `swipe` or `fill`), for the steps you already know: click the field, type into it, press Enter. One round trip instead of three, with no delay to guess at between them, and `-` reads the array from stdin the way `act -` reads one action.
181
182
 
182
183
  ```sh
183
184
  qawolf runner actions '[{"type":"click","button":"left","x":480,"y":260},{"type":"type","text":"me@example.com"},{"type":"keypress","keys":["Enter"]}]' --screenshot after-login.jpg