@qawolf/cli 1.8.1 → 1.9.2

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 CHANGED
@@ -20363,7 +20363,7 @@ var require_readdir_glob = __commonJS((exports, module) => {
20363
20363
  readdirGlob.ReaddirGlob = ReaddirGlob;
20364
20364
  });
20365
20365
 
20366
- // node_modules/async/dist/async.js
20366
+ // node_modules/archiver/node_modules/async/dist/async.js
20367
20367
  var require_async = __commonJS((exports, module) => {
20368
20368
  (function(global2, factory) {
20369
20369
  typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global2 = typeof globalThis !== "undefined" ? globalThis : global2 || self, factory(global2.async = {}));
@@ -87848,7 +87848,7 @@ var require_src2 = __commonJS((exports, module) => {
87848
87848
  }
87849
87849
  });
87850
87850
 
87851
- // node_modules/agent-base/dist/helpers.js
87851
+ // node_modules/webdriver/node_modules/https-proxy-agent/node_modules/agent-base/dist/helpers.js
87852
87852
  var require_helpers = __commonJS((exports) => {
87853
87853
  var __createBinding = exports && exports.__createBinding || (Object.create ? function(o2, m4, k2, k22) {
87854
87854
  if (k22 === undefined)
@@ -87920,7 +87920,7 @@ var require_helpers = __commonJS((exports) => {
87920
87920
  exports.req = req;
87921
87921
  });
87922
87922
 
87923
- // node_modules/agent-base/dist/index.js
87923
+ // node_modules/webdriver/node_modules/https-proxy-agent/node_modules/agent-base/dist/index.js
87924
87924
  var require_dist3 = __commonJS((exports) => {
87925
87925
  var __createBinding = exports && exports.__createBinding || (Object.create ? function(o2, m4, k2, k22) {
87926
87926
  if (k22 === undefined)
@@ -88071,7 +88071,7 @@ var require_dist3 = __commonJS((exports) => {
88071
88071
  exports.Agent = Agent2;
88072
88072
  });
88073
88073
 
88074
- // node_modules/https-proxy-agent/dist/parse-proxy-response.js
88074
+ // node_modules/webdriver/node_modules/https-proxy-agent/dist/parse-proxy-response.js
88075
88075
  var require_parse_proxy_response = __commonJS((exports) => {
88076
88076
  var __importDefault = exports && exports.__importDefault || function(mod) {
88077
88077
  return mod && mod.__esModule ? mod : { default: mod };
@@ -88167,7 +88167,7 @@ var require_parse_proxy_response = __commonJS((exports) => {
88167
88167
  exports.parseProxyResponse = parseProxyResponse;
88168
88168
  });
88169
88169
 
88170
- // node_modules/https-proxy-agent/dist/index.js
88170
+ // node_modules/webdriver/node_modules/https-proxy-agent/dist/index.js
88171
88171
  var require_dist4 = __commonJS((exports) => {
88172
88172
  var __createBinding = exports && exports.__createBinding || (Object.create ? function(o2, m4, k2, k22) {
88173
88173
  if (k22 === undefined)
@@ -156391,6 +156391,60 @@ var installMessages = {
156391
156391
  appiumNotFound: (appiumPath) => toolNotInstalled("Appium", appiumPath)
156392
156392
  }
156393
156393
  };
156394
+ // src/core/messages/interactiveRunner.ts
156395
+ var noRunnerId = "No runner id. Pass --runner, set QAWOLF_RUNNER_ID, or run qawolf runner launch first.";
156396
+ var interactiveRunnerMessages = {
156397
+ actionFailed: (errorMessage2) => `The action reached the runner and did not take effect: ${errorMessage2}`,
156398
+ 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.',
156399
+ 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.",
156400
+ actionNotJson: `Stdin did not hold a JSON action. Pipe one object, for example '{"type":"click","button":"left","x":480,"y":260}'.`,
156401
+ actionPerformed: (type) => `Performed ${type}.`,
156402
+ alreadyRunning: (id) => `Runner ${id} was already running.`,
156403
+ defaultNotRemembered: (id) => `Runner ${id} could not be written to .qawolf as this directory's default, so later commands will not find it on their own. Pass --runner ${id}, or set QAWOLF_RUNNER_ID=${id}.`,
156404
+ defaultNotForgotten: (id) => `Runner ${id} was stopped, but it could not be removed from .qawolf as this directory's default, so later commands will still be sent to it. Pass --runner, or launch a new runner.`,
156405
+ fileNotCollected: (path) => `"${path}" is not one of the files that travel to a runner. It must be inside the current directory and end in .ts, .tsx, .js, .mjs, .cjs or .json.`,
156406
+ fileUnreadable: (path, reason) => `${path} is one of the files that travel to a runner, but it could not be read: ${reason}. Move it out of the current directory, or make it readable.`,
156407
+ filesUnreadable: (reason) => `One of the files that travel to a runner could not be read: ${reason}.`,
156408
+ filesTooLarge: (byteLength, maxByteLength, largest) => `The collected files carry ${String(byteLength)} bytes, over the ${String(maxByteLength)} a single run may ship. The largest are ${largest.map((file) => `${file.path} (${String(file.byteLength)} bytes)`).join(", ")}. Every .ts, .tsx, .js, .mjs, .cjs and .json file under the current directory travels, build output included, so run from a directory holding only the flow and what it imports.`,
156409
+ keptAlive: (id) => `Runner ${id} is alive, and its inactivity clock has been reset.`,
156410
+ launchFailed: (id, error) => `Launching runner ${id} failed. ${error}`,
156411
+ launchLost: (id, error) => `Launching runner ${id} failed. ${error}. The request may still have reached QA Wolf and started a runner, so relaunch the same id with qawolf runner launch --id ${id} to attach to it rather than start a second one, or stop it with qawolf runner stop --runner ${id}.`,
156412
+ launched: (id) => `Launched runner ${id}.`,
156413
+ launchedForCommand: (id) => `No runner was given, so launched ${id} for this command. Its browser is fresh: nothing has been run on it and nothing is signed in. It bills until it is stopped or idles out, so stop it with qawolf runner stop --runner ${id} when you are done.`,
156414
+ followEventsTimedOut: (stream, seconds) => `Stopped following ${stream} after ${formatSeconds(seconds * 1000)}: reading keeps the runner alive and billing, so a follow does not run unbounded. Pass --timeout to wait longer, or follow again to continue.`,
156415
+ followTimedOut: (runId, runnerId, seconds) => `Stopped following run ${runId} after ${formatSeconds(seconds * 1000)}. The run may still be going: read it with qawolf runner events run-status --run ${runId}, and stop the runner with qawolf runner stop --runner ${runnerId} when you are done. Pass --timeout to wait longer.`,
156416
+ missingPackageJson: "No package.json in the current directory. A run reads its npm dependencies from one, so it has to travel with the flow.",
156417
+ noRunnerIdForScreenshot: `${noRunnerId} A screenshot also needs a screen, which a runner gets from its first run: run a flow on it with qawolf runner run.`,
156418
+ noRunnerId,
156419
+ notRunning: (id) => `Runner ${id} was not running.`,
156420
+ runFailed: (errorMessage2) => errorMessage2 === undefined ? "The run failed and reported no reason." : `The run failed: ${errorMessage2}`,
156421
+ runPassed: "The run passed.",
156422
+ runSettledUnknown: (status) => `The run settled as "${status}", which this version of the CLI does not recognize. Upgrade to read it.`,
156423
+ runSubmitAnsweredUnknown: (outcome) => `The runner answered the submission with "${outcome}", which this version of the CLI does not recognize. Upgrade to read it.`,
156424
+ runSubmitted: (runId) => `Submitted run ${runId}.`,
156425
+ 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 node20WithPlaywright runner instead.",
156426
+ runnerHasNoScreenToEvaluate: "The runner could not evaluate the snippet. This covers a runner that is still starting or busy, and also one with no live page to evaluate against: a freshly launched runner has no page until a run opens one, so run a flow on it with qawolf runner run first. If it is not a runner that runs a browser at all, this will never clear. It is not proof the snippet did not run, so do not resubmit one that mutates the page without reading qawolf runner events console first.",
156427
+ runnerUnreachable: "The runner could not be reached. It may still be starting, or it may have terminated after inactivity. Retry, or launch it again.",
156428
+ 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.",
156429
+ 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.",
156430
+ 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.",
156431
+ screenshotUnwritable: (path, detail) => `The screen was captured but could not be written to "${path}": ${detail}. Give --out a path this process can write to.`,
156432
+ screenshotWritten: (path) => `Wrote the runner's screen to ${path}.`,
156433
+ skippedEntries: (stream, count) => `${pluralize(count, "entry", "entries")} of ${stream} were dropped before this read: the runner keeps a bounded history and this follow has been overtaken.`,
156434
+ snippetEmpty: (path) => `"${path}" holds no code to evaluate.`,
156435
+ snippetErrored: (errorMessage2) => errorMessage2 === undefined ? "The snippet threw and reported no message." : `The snippet threw: ${errorMessage2}`,
156436
+ snippetFileUnreadable: (path) => `Could not read "${path}". Name a readable file, or "-" to read the snippet from stdin.`,
156437
+ snippetRan: "The snippet ran. Its value is not returned: read anything it printed with qawolf runner events console.",
156438
+ snippetStopped: "The snippet was interrupted before it finished. Anything it printed first is in qawolf runner events console.",
156439
+ stdinEmptyAction: 'Nothing arrived on stdin. Pipe the action in, or name the action type instead of "-".',
156440
+ stdinEmptySnippet: 'Nothing arrived on stdin. Pipe the snippet in, or name a file instead of "-".',
156441
+ stopped: (id) => `Stopped runner ${id}.`,
156442
+ requestTooLarge: (byteLength, maxByteLength) => `The run request encodes to ${String(byteLength)} bytes, over the ${String(maxByteLength)} a runner accepts. Escaping inflates file content, so a set of files inside the content cap can still encode to more than this. Run from a directory holding only the flow and what it imports.`,
156443
+ unknownStream: (stream, known) => `"${stream}" is not a stream QA Wolf writes, so this read has nothing to return. QA Wolf writes ${known.join(", ")}.`,
156444
+ unsearchedHistory: (stream) => `This read of ${stream} stopped before searching all of the runner's history, so older entries may exist that it did not look at. Page forwards with --since to reach them.`,
156445
+ submitMayHaveStarted: "The runner did not answer, which does not mean the run did not start. Read qawolf runner events run-status before running the flow again, and use the newest run id there if one appeared.",
156446
+ targetMismatch: (runnerName, requiredRunnerName) => `This flow needs a ${requiredRunnerName} runner and ${runnerName} is running. Launch one with --name ${requiredRunnerName}.`
156447
+ };
156394
156448
  // src/core/messages/runner.ts
156395
156449
  var runnerMessages = {
156396
156450
  playwrightLoadFailed: (envDir, detail) => packageLoadFailed("Playwright", envDir, detail),
@@ -156543,7 +156597,8 @@ var exitCodes = {
156543
156597
  invalidArgs: 2,
156544
156598
  auth: 3,
156545
156599
  network: 4,
156546
- config: 5
156600
+ config: 5,
156601
+ timeout: 6
156547
156602
  };
156548
156603
  function exit(code, message, proc = process) {
156549
156604
  if (message !== undefined && message.length > 0) {
@@ -171279,7 +171334,12 @@ async function requestWithRetry(args) {
171279
171334
  const backoff = args.backoffMs[attempt];
171280
171335
  const retryable = result.error.kind === "network" || result.error.kind === "timeout";
171281
171336
  if (backoff === undefined || !retryable) {
171282
- return { ok: false, ...args.describe(result.error) };
171337
+ const mayHaveArrived = result.error.kind !== "http";
171338
+ return {
171339
+ ok: false,
171340
+ ...args.describe(result.error),
171341
+ ...mayHaveArrived ? { mayHaveArrived } : {}
171342
+ };
171283
171343
  }
171284
171344
  await sleep2(backoff);
171285
171345
  }
@@ -172427,7 +172487,6 @@ var browserActionSchema = exports_external.discriminatedUnion("type", [
172427
172487
  type: exports_external.literal("type")
172428
172488
  })
172429
172489
  ]);
172430
-
172431
172490
  // node_modules/@qawolf/api-contracts/dist/v1/runner/environment.js
172432
172491
  var reservedRunEnvironmentVariablePrefix = "QAWOLF_";
172433
172492
  function refusalForRunEnvironmentVariableName(name) {
@@ -172488,7 +172547,6 @@ var runFilePathSchema = exports_external.string().refine(isShippableRunFilePath,
172488
172547
  error: runFilePathRefusal
172489
172548
  });
172490
172549
  var runPackageJsonPath = "package.json";
172491
-
172492
172550
  // node_modules/@qawolf/api-contracts/dist/v1/runner/identity.js
172493
172551
  var runnerNameSchema = exports_external.enum([
172494
172552
  "node20Basic",
@@ -172502,7 +172560,6 @@ var makeRunnerSchema = () => exports_external.object({
172502
172560
  id: runnerIdSchema.describe("The runner's id, as supplied when launching it."),
172503
172561
  runnerName: runnerNameSchema.describe("The image requested when the runner was launched. QA Wolf may place it on faster hardware, reported in gpuAccelerated.")
172504
172562
  });
172505
-
172506
172563
  // node_modules/@qawolf/api-contracts/dist/v1/runner/journal.js
172507
172564
  var journalEntrySchema = exports_external.object({
172508
172565
  payload: exports_external.unknown(),
@@ -172537,7 +172594,6 @@ var readJournalResponseSchema = exports_external.object({
172537
172594
  nextSequence: exports_external.number().int().nonnegative(),
172538
172595
  oldestAvailableSequence: exports_external.number().int().nonnegative()
172539
172596
  });
172540
-
172541
172597
  // node_modules/@qawolf/api-contracts/dist/v1/runner/payloadSize.js
172542
172598
  var maxRunFilesByteLength = 4 * 1024 * 1024;
172543
172599
  var maxRunnerRequestEncodedByteLength = 9 * 1024 * 1024;
@@ -172560,7 +172616,6 @@ var runFilesSchema = exports_external.record(exports_external.string(), exports_
172560
172616
  }).refine((files) => runFilesByteLength(files) <= maxRunFilesByteLength, {
172561
172617
  error: `The files in one request may carry at most ${maxRunFilesByteLength} bytes in total.`
172562
172618
  });
172563
-
172564
172619
  // node_modules/@qawolf/api-contracts/dist/v1/runner/screen.js
172565
172620
  var runnerScreenNotReadySchema = exports_external.object({
172566
172621
  outcome: exports_external.literal("screen-not-ready")
@@ -172666,6 +172721,7 @@ var makeEnvironmentResourceSchema = (ids) => resource({
172666
172721
  })
172667
172722
  ]).optional().describe("The environment's flow code branch and its git reconciliation state, if it has one."),
172668
172723
  id: ids.environment,
172724
+ isDefault: exports_external.boolean().describe("Whether this is the workspace's default environment."),
172669
172725
  kind: exports_external.enum(["static", "preview"]).describe("Whether the environment is static or a preview."),
172670
172726
  name: exports_external.string(),
172671
172727
  runConcurrencyLimit: exports_external.string().describe('Maximum number of concurrent runs: a whole number, "unlimited", or a concurrency expression.'),
@@ -172681,6 +172737,7 @@ var makeFindEnvironmentsContract = (ids) => {
172681
172737
  ...makePaginationInputFields({ defaultLimit: 20, maxLimit: 100 })
172682
172738
  });
172683
172739
  const output = exports_external.object({
172740
+ defaultEnvironmentId: ids.environment.describe("The id of the workspace's default environment."),
172684
172741
  environments: exports_external.array(makeEnvironmentResourceSchema(ids)).describe("The team's environments, newest first."),
172685
172742
  nextCursor: nextCursorSchema
172686
172743
  });
@@ -172754,7 +172811,7 @@ var makeSetEnvironmentVariableContract = (ids) => {
172754
172811
  name: exports_external.string().describe("The stored variable name after whitespace is replaced with underscores and letters are uppercased.")
172755
172812
  });
172756
172813
  return {
172757
- description: 'Create or replace an environment variable. If the user asks to create one for "my email" without naming it, use DEFAULT_ENVIRONMENT_EMAIL. The value is never returned.',
172814
+ 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.',
172758
172815
  input,
172759
172816
  kind: "write",
172760
172817
  name: "environment.setVariable",
@@ -173056,8 +173113,50 @@ var makeGetRunContract = (ids) => {
173056
173113
  const input = exports_external.object({
173057
173114
  runId: ids.run
173058
173115
  });
173116
+ const artifactUrls = {
173117
+ logsUrl: exports_external.url().optional().describe("Signed URL for the attempt's execution logs."),
173118
+ traceUrl: exports_external.url().optional().describe("Signed URL for the attempt's Playwright trace (a trace.zip; open it with `npx playwright show-trace`)."),
173119
+ videoUrl: exports_external.url().optional().describe("Signed URL for the attempt's screen recording.")
173120
+ };
173121
+ const automatedKind = exports_external.literal("automated").describe("The attempt ran on a QA Wolf runner.");
173122
+ const automatedAttempt = exports_external.discriminatedUnion("status", [
173123
+ exports_external.object({
173124
+ ...artifactUrls,
173125
+ completedAt: exports_external.iso.datetime(),
173126
+ kind: automatedKind,
173127
+ startedAt: exports_external.iso.datetime(),
173128
+ status: exports_external.literal("passed")
173129
+ }),
173130
+ exports_external.object({
173131
+ ...artifactUrls,
173132
+ completedAt: exports_external.iso.datetime(),
173133
+ kind: automatedKind,
173134
+ startedAt: exports_external.iso.datetime().optional().describe("Absent when the attempt failed before it could start."),
173135
+ status: exports_external.literal("failed")
173136
+ }),
173137
+ exports_external.object({
173138
+ completedAt: exports_external.iso.datetime().optional(),
173139
+ kind: automatedKind,
173140
+ startedAt: exports_external.iso.datetime().optional(),
173141
+ status: exports_external.literal("canceled").describe("The attempt was terminated without reaching a verdict.")
173142
+ })
173143
+ ]);
173144
+ const manualAttempt = exports_external.object({
173145
+ completedAt: exports_external.iso.datetime(),
173146
+ kind: exports_external.literal("manual").describe("The attempt ran by hand in the Wolf Browser."),
173147
+ startedAt: exports_external.iso.datetime(),
173148
+ status: exports_external.literal("passed")
173149
+ });
173150
+ const attempt = exports_external.union([automatedAttempt, manualAttempt]);
173151
+ const attempts = exports_external.array(attempt).optional().describe("The flow's finished execution attempts, oldest first, including " + "manual Wolf Browser attempts. Present once at least one attempt has " + "finished, so a flow that passed after retries also lists its failed " + "attempts. Artifact URLs appear only on automated attempts that " + "reached a verdict, stay valid for at least a day (call run.get " + "again for fresh ones), and can return 404 when the attempt did not " + "produce that artifact.");
173152
+ const diagnosis = exports_external.object({
173153
+ issueId: ids.issue,
173154
+ type: exports_external.enum(["bug", "maintenance"])
173155
+ }).optional().describe("QA Wolf's investigation verdict for the failure: `bug` means the " + "application is broken, `maintenance` means the test needed an " + "update and the failure does not indicate an application problem. " + "Absent until the investigation reaches a verdict. Pass issueId to " + "issue.get for details.");
173059
173156
  const failedFlow = exports_external.object({
173157
+ attempts,
173060
173158
  failure: exports_external.object({
173159
+ diagnosis,
173061
173160
  error: exports_external.string()
173062
173161
  }),
173063
173162
  flowId: ids.flow,
@@ -173065,6 +173164,7 @@ var makeGetRunContract = (ids) => {
173065
173164
  status: exports_external.literal("failed")
173066
173165
  });
173067
173166
  const nonFailedFlow = exports_external.object({
173167
+ attempts,
173068
173168
  flowId: ids.flow,
173069
173169
  name: exports_external.string(),
173070
173170
  status: exports_external.enum(["queued", "running", "passed", "canceled"])
@@ -175510,6 +175610,8 @@ function createAgentRenderers() {
175510
175610
  writeStderrLine(humanMessage);
175511
175611
  },
175512
175612
  gap: () => writeStderrLine(""),
175613
+ stream: (_data, line) => writeStdoutRaw(`${line}
175614
+ `),
175513
175615
  write: (text) => writeStderrRaw(text),
175514
175616
  withProgress: async (steps, done) => {
175515
175617
  const results = [];
@@ -175545,6 +175647,8 @@ ${body}` : ""));
175545
175647
  },
175546
175648
  output: (_data, humanMessage) => clack.log.info(humanMessage),
175547
175649
  gap: () => process.stderr.write(`
175650
+ `),
175651
+ stream: (_data, line) => writeStdoutRaw(`${line}
175548
175652
  `),
175549
175653
  write: (text) => writeStdoutRaw(text),
175550
175654
  withProgress: async (steps, done) => {
@@ -175618,6 +175722,7 @@ function createJsonRenderers() {
175618
175722
  error: (title, body) => writeJsonDiagnostic({ type: "error", title, body }),
175619
175723
  output: (data, _humanMessage) => writeJsonLine(data),
175620
175724
  gap: () => {},
175725
+ stream: (data, _line) => writeJsonLine(data),
175621
175726
  write: () => {},
175622
175727
  withProgress: async (steps, done) => {
175623
175728
  const results = [];
@@ -175804,7 +175909,7 @@ function startUpdateCheck(deps) {
175804
175909
  // package.json
175805
175910
  var package_default = {
175806
175911
  name: "@qawolf/cli",
175807
- version: "1.8.1",
175912
+ version: "1.9.2",
175808
175913
  description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
175809
175914
  keywords: [
175810
175915
  "automation",
@@ -175864,13 +175969,12 @@ var package_default = {
175864
175969
  "@clack/prompts": "1.5.1",
175865
175970
  "@napi-rs/keyring": "1.3.0",
175866
175971
  "@oxc-node/core": "0.1.0",
175867
- "@qawolf/api-contracts": "0.24.0",
175972
+ "@qawolf/api-contracts": "0.25.0",
175868
175973
  "@qawolf/emails": "1.1.1",
175869
175974
  "@qawolf/flow-targets": "1.0.0",
175870
175975
  "@qawolf/flows": "0.1.4",
175871
175976
  "@qawolf/testkit": "1.1.1",
175872
175977
  appium: "2.11.3",
175873
- "appium-uiautomator2-driver": "3.7.0",
175874
175978
  commander: "14.0.3",
175875
175979
  "env-paths": "4.0.0",
175876
175980
  "expect-webdriverio": "5.6.5",
@@ -175889,6 +175993,7 @@ var package_default = {
175889
175993
  "@tsconfig/strictest": "2.0.8",
175890
175994
  "@types/bun": "1.3.14",
175891
175995
  "@types/picomatch": "4.0.3",
175996
+ "appium-uiautomator2-driver": "4.2.9",
175892
175997
  knip: "6.16.1",
175893
175998
  oxfmt: "0.54.0",
175894
175999
  oxlint: "1.69.0",
@@ -176614,7 +176719,7 @@ var playwrightVersion = "1.62.0";
176614
176719
  var emailsVersion = "1.1.1";
176615
176720
  var testkitVersion = "1.1.1";
176616
176721
  var appiumVersion = "2.11.3";
176617
- var appiumUiautomator2DriverVersion = "3.7.0";
176722
+ var appiumUiautomator2DriverVersion = "4.2.9";
176618
176723
  var expectWebdriverioVersion = "5.6.5";
176619
176724
 
176620
176725
  // src/domains/doctor/checks/playwright.ts
@@ -177708,10 +177813,6 @@ var pinnedPackages = [
177708
177813
  { name: "@qawolf/emails", version: emailsVersion },
177709
177814
  { name: "@qawolf/testkit", version: testkitVersion },
177710
177815
  { name: "appium", version: appiumVersion },
177711
- {
177712
- name: "appium-uiautomator2-driver",
177713
- version: appiumUiautomator2DriverVersion
177714
- },
177715
177816
  { name: "expect-webdriverio", version: expectWebdriverioVersion }
177716
177817
  ];
177717
177818
 
@@ -185988,16 +186089,29 @@ async function handlePublicApiCommand(ctx, spec, options) {
185988
186089
  return;
185989
186090
  }
185990
186091
 
186092
+ // src/domains/publicApi/skippedContracts.ts
186093
+ var handWrittenContractNames = new Set([
186094
+ "flow.list",
186095
+ "runner.evaluateSnippet",
186096
+ "runner.launch",
186097
+ "runner.performAction",
186098
+ "runner.readJournal",
186099
+ "runner.runFlow",
186100
+ "runner.stop",
186101
+ "runner.takeScreenshot"
186102
+ ]);
186103
+ var unexpressibleContractNames = new Set([
186104
+ "issue.update"
186105
+ ]);
186106
+ var skippedContractNames = new Set([
186107
+ ...handWrittenContractNames,
186108
+ ...unexpressibleContractNames
186109
+ ]);
186110
+
185991
186111
  // src/commands/publicApi/index.ts
185992
186112
  var groupDescriptions = {
185993
186113
  run: "Trigger and manage QA Wolf runs on the platform"
185994
186114
  };
185995
- var handWrittenContractNames = new Set(["flow.list"]);
185996
- var skippedContractNames = new Set([
185997
- ...handWrittenContractNames,
185998
- "issue.update",
185999
- "runner.performAction"
186000
- ]);
186001
186115
  var optionEnvironmentVariables = new Map([
186002
186116
  ["environmentId", "QAWOLF_ENVIRONMENT"],
186003
186117
  ["public.run.create.aiTaskId", "QAWOLF_AI_TASK_ID"]
@@ -186044,6 +186158,1017 @@ function registerPublicApiCommands(program2, signals, options = {}) {
186044
186158
  }
186045
186159
  }
186046
186160
 
186161
+ // src/core/interactiveRunner/runFiles.ts
186162
+ import { isAbsolute as isAbsolute5, relative as relative8, resolve as resolve11, sep as sep5 } from "node:path";
186163
+ function toCollectedPath(cwd, path9) {
186164
+ const absolute = isAbsolute5(path9) ? path9 : resolve11(cwd, path9);
186165
+ return relative8(cwd, absolute).split(sep5).join("/");
186166
+ }
186167
+ var namedLargestFileCount = 3;
186168
+ function largestFiles(files) {
186169
+ return Object.entries(files).map(([path9, content]) => ({
186170
+ byteLength: runFilesByteLength({ [path9]: content }),
186171
+ path: path9
186172
+ })).sort((a3, b4) => b4.byteLength - a3.byteLength).slice(0, namedLargestFileCount);
186173
+ }
186174
+ function checkPresenceAndSize(files, requiredPath) {
186175
+ if (!Object.hasOwn(files, requiredPath)) {
186176
+ return { path: requiredPath, type: "missing-file" };
186177
+ }
186178
+ const byteLength = runFilesByteLength(files);
186179
+ if (byteLength > maxRunFilesByteLength) {
186180
+ return {
186181
+ byteLength,
186182
+ largest: largestFiles(files),
186183
+ maxByteLength: maxRunFilesByteLength,
186184
+ type: "too-large"
186185
+ };
186186
+ }
186187
+ const encodedByteLength = Buffer.byteLength(JSON.stringify({ files, path: requiredPath }), "utf8");
186188
+ if (encodedByteLength > maxRunnerRequestEncodedByteLength) {
186189
+ return {
186190
+ byteLength: encodedByteLength,
186191
+ maxByteLength: maxRunnerRequestEncodedByteLength,
186192
+ type: "request-too-large"
186193
+ };
186194
+ }
186195
+ return { type: "ok" };
186196
+ }
186197
+ function checkRunFiles(files, entryPointPath) {
186198
+ const present = checkPresenceAndSize(files, entryPointPath);
186199
+ if (present.type !== "ok")
186200
+ return present;
186201
+ if (!Object.hasOwn(files, runPackageJsonPath)) {
186202
+ return { type: "missing-package-json" };
186203
+ }
186204
+ return { type: "ok" };
186205
+ }
186206
+ function checkSnippetFiles(files, contextFilePath) {
186207
+ return checkPresenceAndSize(files, contextFilePath);
186208
+ }
186209
+ function describeRunFilesCheck(check2) {
186210
+ switch (check2.type) {
186211
+ case "missing-file":
186212
+ return interactiveRunnerMessages.fileNotCollected(check2.path);
186213
+ case "missing-package-json":
186214
+ return interactiveRunnerMessages.missingPackageJson;
186215
+ case "too-large":
186216
+ return interactiveRunnerMessages.filesTooLarge(check2.byteLength, check2.maxByteLength, check2.largest);
186217
+ case "request-too-large":
186218
+ return interactiveRunnerMessages.requestTooLarge(check2.byteLength, check2.maxByteLength);
186219
+ }
186220
+ }
186221
+
186222
+ // src/domains/interactiveRunner/runnerCallOptions.ts
186223
+ var runnerCallOptions = { timeoutMs: 60000 };
186224
+
186225
+ // src/domains/interactiveRunner/runnerIds.ts
186226
+ function parseRunnerId(id) {
186227
+ const parsed = runnerIdSchema.safeParse(id);
186228
+ if (!parsed.success)
186229
+ return { error: exports_external.prettifyError(parsed.error), ok: false };
186230
+ return { id: parsed.data, ok: true };
186231
+ }
186232
+ function parseRunnerName(runnerName) {
186233
+ const parsed = runnerNameSchema.safeParse(runnerName);
186234
+ if (!parsed.success)
186235
+ return { error: exports_external.prettifyError(parsed.error), ok: false };
186236
+ return { ok: true, runnerName: parsed.data };
186237
+ }
186238
+
186239
+ // src/domains/interactiveRunner/launch.ts
186240
+ async function launchRunner(ctx, options) {
186241
+ const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.launch, {
186242
+ id: options.id,
186243
+ ...options.runnerName ? { runnerName: options.runnerName } : {}
186244
+ }, runnerCallOptions);
186245
+ if (!result.ok) {
186246
+ return {
186247
+ ...failureFields(result),
186248
+ exitCode: exitCodes.network,
186249
+ mayHaveArrived: result.mayHaveArrived ?? false,
186250
+ ok: false
186251
+ };
186252
+ }
186253
+ return { ok: true, value: result.value };
186254
+ }
186255
+ async function launchAndRemember(ctx, options, deps) {
186256
+ const priorDefault = await deps.store.readDefaultRunnerId().catch(() => {
186257
+ return;
186258
+ });
186259
+ const remembered = await deps.store.writeDefaultRunnerId(options.id).then(() => true).catch(() => {
186260
+ ctx.ui.warn(interactiveRunnerMessages.defaultNotRemembered(options.id));
186261
+ return false;
186262
+ });
186263
+ const launched = await launchRunner(ctx, options);
186264
+ if (launched.ok)
186265
+ return launched;
186266
+ if (remembered && !launched.mayHaveArrived) {
186267
+ await (priorDefault === undefined ? deps.store.clearDefaultRunnerId() : deps.store.writeDefaultRunnerId(priorDefault)).catch(() => {
186268
+ return;
186269
+ });
186270
+ }
186271
+ return {
186272
+ ...launched,
186273
+ error: launched.mayHaveArrived ? interactiveRunnerMessages.launchLost(options.id, launched.error) : interactiveRunnerMessages.launchFailed(options.id, launched.error)
186274
+ };
186275
+ }
186276
+ async function handleRunnerLaunch(ctx, options, deps) {
186277
+ const chosenId = options.id ?? deps.makeRunnerId();
186278
+ const id = parseRunnerId(chosenId);
186279
+ if (!id.ok)
186280
+ return { error: id.error, exitCode: exitCodes.invalidArgs };
186281
+ const runnerName = options.name === undefined ? undefined : parseRunnerName(options.name);
186282
+ if (runnerName !== undefined && !runnerName.ok) {
186283
+ return { error: runnerName.error, exitCode: exitCodes.invalidArgs };
186284
+ }
186285
+ const launched = await launchAndRemember(ctx, { id: id.id, runnerName: runnerName?.runnerName }, deps);
186286
+ if (!launched.ok) {
186287
+ return { ...failureFields(launched), exitCode: launched.exitCode };
186288
+ }
186289
+ ctx.ui.output(launched.value, launched.value.outcome === "launched" ? interactiveRunnerMessages.launched(launched.value.id) : interactiveRunnerMessages.alreadyRunning(launched.value.id));
186290
+ return;
186291
+ }
186292
+
186293
+ // src/domains/interactiveRunner/resolveRunner.ts
186294
+ var runnerIdEnvironmentVariable = "QAWOLF_RUNNER_ID";
186295
+ async function chooseRunnerId(runner, deps) {
186296
+ if (runner !== undefined)
186297
+ return runner;
186298
+ const fromEnvironment = deps.env[runnerIdEnvironmentVariable]?.trim();
186299
+ if (fromEnvironment)
186300
+ return fromEnvironment;
186301
+ return deps.store.readDefaultRunnerId();
186302
+ }
186303
+ async function resolveRunner(ctx, options, deps) {
186304
+ const chosen = await chooseRunnerId(options.runner, deps);
186305
+ if (chosen !== undefined) {
186306
+ const parsed = parseRunnerId(chosen);
186307
+ return parsed.ok ? { runnerId: parsed.id, type: "resolved" } : {
186308
+ error: parsed.error,
186309
+ exitCode: exitCodes.invalidArgs,
186310
+ type: "failed"
186311
+ };
186312
+ }
186313
+ if (!options.autoLaunch) {
186314
+ return {
186315
+ error: options.noRunnerIdMessage ?? interactiveRunnerMessages.noRunnerId,
186316
+ exitCode: exitCodes.invalidArgs,
186317
+ type: "failed"
186318
+ };
186319
+ }
186320
+ const launched = await launchAndRemember(ctx, { id: deps.makeRunnerId(), runnerName: undefined }, deps);
186321
+ if (!launched.ok) {
186322
+ return {
186323
+ ...failureFields(launched),
186324
+ exitCode: launched.exitCode,
186325
+ type: "failed"
186326
+ };
186327
+ }
186328
+ return { runnerId: launched.value.id, type: "launched" };
186329
+ }
186330
+ function announceRunner(ctx, resolved) {
186331
+ if (resolved.type === "launched") {
186332
+ ctx.ui.info(interactiveRunnerMessages.launchedForCommand(resolved.runnerId));
186333
+ }
186334
+ }
186335
+
186336
+ // src/domains/interactiveRunner/evaluateSnippet.ts
186337
+ var stdinArgument = "-";
186338
+ async function resolveScope(contextFile, deps) {
186339
+ if (contextFile === undefined) {
186340
+ return { filePath: undefined, files: undefined, ok: true };
186341
+ }
186342
+ const filePath = toCollectedPath(deps.cwd, contextFile);
186343
+ const files = await deps.collectRunFiles();
186344
+ const check2 = checkSnippetFiles(files, filePath);
186345
+ if (check2.type !== "ok") {
186346
+ return { error: describeRunFilesCheck(check2), ok: false };
186347
+ }
186348
+ return { filePath, files, ok: true };
186349
+ }
186350
+ async function readCode(source, deps) {
186351
+ const code = source === stdinArgument ? await deps.readStdin() : await deps.readFile(source).catch(() => {
186352
+ return;
186353
+ });
186354
+ if (code === undefined) {
186355
+ return {
186356
+ error: interactiveRunnerMessages.snippetFileUnreadable(source),
186357
+ ok: false
186358
+ };
186359
+ }
186360
+ if (code.trim() === "") {
186361
+ return source === stdinArgument ? { error: interactiveRunnerMessages.stdinEmptySnippet, ok: false } : { error: interactiveRunnerMessages.snippetEmpty(source), ok: false };
186362
+ }
186363
+ return { code, ok: true };
186364
+ }
186365
+ async function handleRunnerExec(ctx, options, deps) {
186366
+ const code = await readCode(options.source, deps);
186367
+ if (!code.ok)
186368
+ return { error: code.error, exitCode: exitCodes.invalidArgs };
186369
+ const scope = await resolveScope(options.contextFile, deps);
186370
+ if (!scope.ok)
186371
+ return { error: scope.error, exitCode: exitCodes.invalidArgs };
186372
+ const resolved = await resolveRunner(ctx, { autoLaunch: true, runner: options.runner }, deps);
186373
+ if (resolved.type === "failed") {
186374
+ return { ...failureFields(resolved), exitCode: resolved.exitCode };
186375
+ }
186376
+ announceRunner(ctx, resolved);
186377
+ const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.evaluateSnippet, {
186378
+ code: code.code,
186379
+ id: resolved.runnerId,
186380
+ ...scope.filePath === undefined ? {} : { filePath: scope.filePath },
186381
+ ...scope.files === undefined ? {} : { files: scope.files }
186382
+ }, runnerCallOptions);
186383
+ if (!result.ok) {
186384
+ return { ...failureFields(result), exitCode: exitCodes.network };
186385
+ }
186386
+ if (result.value.outcome === "runner-unreachable") {
186387
+ return {
186388
+ error: interactiveRunnerMessages.runnerHasNoScreenToEvaluate,
186389
+ exitCode: exitCodes.network
186390
+ };
186391
+ }
186392
+ switch (result.value.result) {
186393
+ case "success":
186394
+ ctx.ui.output({ outcome: "evaluated", result: "success" }, interactiveRunnerMessages.snippetRan);
186395
+ return;
186396
+ case "stopped":
186397
+ return {
186398
+ error: interactiveRunnerMessages.snippetStopped,
186399
+ exitCode: exitCodes.testFailure
186400
+ };
186401
+ case "error":
186402
+ return {
186403
+ error: interactiveRunnerMessages.snippetErrored(result.value.errorMessage),
186404
+ exitCode: exitCodes.testFailure
186405
+ };
186406
+ }
186407
+ }
186408
+
186409
+ // src/core/interactiveRunner/browserAction.ts
186410
+ function parseJsonPath(path9) {
186411
+ try {
186412
+ return { ok: true, value: JSON.parse(path9) };
186413
+ } catch {
186414
+ return {
186415
+ error: `--path must be a JSON array of points, for example '[{"x":10,"y":20},{"x":80,"y":90}]'.`,
186416
+ ok: false
186417
+ };
186418
+ }
186419
+ }
186420
+ function toNumber(value) {
186421
+ return value.trim() === "" ? Number.NaN : Number(value);
186422
+ }
186423
+ function buildBrowserAction(type, flags) {
186424
+ const parsedPath = flags.path === undefined ? undefined : parseJsonPath(flags.path);
186425
+ if (parsedPath !== undefined && !parsedPath.ok)
186426
+ return parsedPath;
186427
+ const candidate = {
186428
+ type,
186429
+ ...flags.button === undefined ? {} : { button: flags.button },
186430
+ ...flags.keys === undefined ? {} : { keys: flags.keys },
186431
+ ...parsedPath === undefined ? {} : { path: parsedPath.value },
186432
+ ...flags.scrollX === undefined ? {} : { scroll_x: toNumber(flags.scrollX) },
186433
+ ...flags.scrollY === undefined ? {} : { scroll_y: toNumber(flags.scrollY) },
186434
+ ...flags.text === undefined ? {} : { text: flags.text },
186435
+ ...flags.url === undefined ? {} : { url: flags.url },
186436
+ ...flags.x === undefined ? {} : { x: toNumber(flags.x) },
186437
+ ...flags.y === undefined ? {} : { y: toNumber(flags.y) }
186438
+ };
186439
+ return parseBrowserAction(candidate);
186440
+ }
186441
+ function parseBrowserAction(candidate) {
186442
+ const parsed = browserActionSchema.safeParse(candidate);
186443
+ if (!parsed.success) {
186444
+ return { error: exports_external.prettifyError(parsed.error), ok: false };
186445
+ }
186446
+ return { action: parsed.data, ok: true };
186447
+ }
186448
+
186449
+ // src/domains/interactiveRunner/performAction.ts
186450
+ var stdinArgument2 = "-";
186451
+ async function readAction(type, flags, deps) {
186452
+ if (type !== stdinArgument2)
186453
+ return buildBrowserAction(type, flags);
186454
+ if (Object.values(flags).some((value) => value !== undefined)) {
186455
+ return { error: interactiveRunnerMessages.actionFlagsWithStdin, ok: false };
186456
+ }
186457
+ const piped = (await deps.readStdin()).trim();
186458
+ if (piped === "") {
186459
+ return { error: interactiveRunnerMessages.stdinEmptyAction, ok: false };
186460
+ }
186461
+ try {
186462
+ return parseBrowserAction(JSON.parse(piped));
186463
+ } catch {
186464
+ return { error: interactiveRunnerMessages.actionNotJson, ok: false };
186465
+ }
186466
+ }
186467
+ async function handleRunnerAct(ctx, options, deps) {
186468
+ const built = await readAction(options.type, options.flags, deps);
186469
+ if (!built.ok)
186470
+ return { error: built.error, exitCode: exitCodes.invalidArgs };
186471
+ const resolved = await resolveRunner(ctx, { autoLaunch: true, runner: options.runner }, deps);
186472
+ if (resolved.type === "failed") {
186473
+ return { ...failureFields(resolved), exitCode: resolved.exitCode };
186474
+ }
186475
+ announceRunner(ctx, resolved);
186476
+ const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.performAction, { action: built.action, id: resolved.runnerId }, runnerCallOptions);
186477
+ if (!result.ok) {
186478
+ const fields = failureFields(result);
186479
+ return {
186480
+ ...fields,
186481
+ ...result.mayHaveArrived ? {
186482
+ error: `${fields.error} ${interactiveRunnerMessages.actionMayHaveHappened}`
186483
+ } : {},
186484
+ exitCode: exitCodes.network
186485
+ };
186486
+ }
186487
+ switch (result.value.outcome) {
186488
+ case "performed":
186489
+ ctx.ui.output({ action: built.action, outcome: "performed" }, interactiveRunnerMessages.actionPerformed(built.action.type));
186490
+ return;
186491
+ case "action-failed":
186492
+ return {
186493
+ error: interactiveRunnerMessages.actionFailed(result.value.errorMessage),
186494
+ exitCode: exitCodes.testFailure
186495
+ };
186496
+ case "screen-needs-a-run":
186497
+ return {
186498
+ error: interactiveRunnerMessages.screenNeedsARun,
186499
+ exitCode: exitCodes.invalidArgs
186500
+ };
186501
+ case "screen-not-ready":
186502
+ return {
186503
+ error: interactiveRunnerMessages.screenNotReady,
186504
+ exitCode: exitCodes.network
186505
+ };
186506
+ case "runner-has-no-screen":
186507
+ return {
186508
+ error: interactiveRunnerMessages.runnerHasNoScreen,
186509
+ exitCode: exitCodes.invalidArgs
186510
+ };
186511
+ case "runner-unreachable":
186512
+ return {
186513
+ error: interactiveRunnerMessages.actionMayHaveHappened,
186514
+ exitCode: exitCodes.network
186515
+ };
186516
+ }
186517
+ }
186518
+
186519
+ // src/domains/interactiveRunner/takeScreenshot.ts
186520
+ async function handleRunnerScreenshot(ctx, options, deps) {
186521
+ const resolved = await resolveRunner(ctx, {
186522
+ autoLaunch: false,
186523
+ noRunnerIdMessage: interactiveRunnerMessages.noRunnerIdForScreenshot,
186524
+ runner: options.runner
186525
+ }, deps);
186526
+ if (resolved.type === "failed") {
186527
+ return { ...failureFields(resolved), exitCode: resolved.exitCode };
186528
+ }
186529
+ const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.takeScreenshot, { id: resolved.runnerId }, runnerCallOptions);
186530
+ if (!result.ok) {
186531
+ return { ...failureFields(result), exitCode: exitCodes.network };
186532
+ }
186533
+ switch (result.value.outcome) {
186534
+ case "captured": {
186535
+ const written = await deps.writeScreenshot({
186536
+ imageJpegBase64: result.value.imageJpegBase64,
186537
+ path: options.out
186538
+ });
186539
+ if (!written.ok) {
186540
+ return written.reason === "not-a-jpeg" ? {
186541
+ error: interactiveRunnerMessages.screenshotNotAnImage,
186542
+ exitCode: exitCodes.network
186543
+ } : {
186544
+ error: interactiveRunnerMessages.screenshotUnwritable(options.out, written.detail),
186545
+ exitCode: exitCodes.invalidArgs
186546
+ };
186547
+ }
186548
+ ctx.ui.output({ outcome: "captured", path: options.out }, interactiveRunnerMessages.screenshotWritten(options.out));
186549
+ return;
186550
+ }
186551
+ case "screen-needs-a-run":
186552
+ return {
186553
+ error: interactiveRunnerMessages.screenNeedsARun,
186554
+ exitCode: exitCodes.invalidArgs
186555
+ };
186556
+ case "screen-not-ready":
186557
+ return {
186558
+ error: interactiveRunnerMessages.screenNotReady,
186559
+ exitCode: exitCodes.network
186560
+ };
186561
+ case "runner-has-no-screen":
186562
+ return {
186563
+ error: interactiveRunnerMessages.runnerHasNoScreen,
186564
+ exitCode: exitCodes.invalidArgs
186565
+ };
186566
+ case "runner-unreachable":
186567
+ return {
186568
+ error: interactiveRunnerMessages.runnerUnreachable,
186569
+ exitCode: exitCodes.network
186570
+ };
186571
+ }
186572
+ }
186573
+
186574
+ // src/shell/interactiveRunner/collectRunFiles.ts
186575
+ import { join as join42 } from "node:path";
186576
+ async function collectRunFiles(options) {
186577
+ const paths = await glob(shippableRunFileExtensions.map((extension) => `**/*${extension}`), {
186578
+ cwd: options.cwd,
186579
+ followSymbolicLinks: false,
186580
+ ignore: ["**/node_modules/**"]
186581
+ });
186582
+ const shippable = paths.filter(isShippableRunFilePath).sort();
186583
+ const files = {};
186584
+ const read = batchMap(shippable, async (path9) => ({
186585
+ content: await options.fs.readFile(join42(options.cwd, path9)),
186586
+ path: path9
186587
+ }), flowBatchSize);
186588
+ for await (const file2 of read)
186589
+ files[file2.path] = file2.content;
186590
+ return files;
186591
+ }
186592
+
186593
+ // src/shell/interactiveRunner/makeRunnerId.ts
186594
+ import { randomUUID } from "node:crypto";
186595
+ function makeRunnerId() {
186596
+ return `cli-${randomUUID().replaceAll("-", "").slice(0, 12)}`;
186597
+ }
186598
+
186599
+ // src/shell/interactiveRunner/runnerStore.ts
186600
+ import { join as join43 } from "node:path";
186601
+ var storeFileName = "runner.json";
186602
+ var storeSchema = exports_external.object({ defaultRunnerId: exports_external.string().optional() });
186603
+ function parseJson(text3) {
186604
+ try {
186605
+ return JSON.parse(text3);
186606
+ } catch {
186607
+ return;
186608
+ }
186609
+ }
186610
+ function makeRunnerStore(options) {
186611
+ const directory = join43(options.cwd, qawolfDir);
186612
+ const path9 = join43(directory, storeFileName);
186613
+ let pendingWrites = 0;
186614
+ const nextPendingPath = () => `${path9}.${process.pid}.${++pendingWrites}.tmp`;
186615
+ return {
186616
+ async clearDefaultRunnerId() {
186617
+ await options.fs.rm(path9, { force: true });
186618
+ },
186619
+ async readDefaultRunnerId() {
186620
+ const contents2 = await options.fs.readFile(path9).catch(() => {
186621
+ return;
186622
+ });
186623
+ if (contents2 === undefined)
186624
+ return;
186625
+ const parsed = storeSchema.safeParse(parseJson(contents2));
186626
+ return parsed.success ? parsed.data.defaultRunnerId : undefined;
186627
+ },
186628
+ async writeDefaultRunnerId(runnerId) {
186629
+ const pendingPath = nextPendingPath();
186630
+ await options.fs.mkdir(directory, { recursive: true });
186631
+ await options.fs.writeFile(pendingPath, `${JSON.stringify({ defaultRunnerId: runnerId }, undefined, 2)}
186632
+ `);
186633
+ await options.fs.rename(pendingPath, path9);
186634
+ }
186635
+ };
186636
+ }
186637
+
186638
+ // src/shell/interactiveRunner/writeScreenshot.ts
186639
+ import { dirname as dirname14 } from "node:path";
186640
+ var jpegStartOfImage = [255, 216, 255];
186641
+ async function writeScreenshot(options) {
186642
+ const bytes = Buffer.from(options.imageJpegBase64, "base64");
186643
+ if (jpegStartOfImage.some((byte, index2) => bytes[index2] !== byte)) {
186644
+ return { ok: false, reason: "not-a-jpeg" };
186645
+ }
186646
+ try {
186647
+ await options.fs.mkdir(dirname14(options.path), { recursive: true });
186648
+ await options.fs.writeFile(options.path, bytes);
186649
+ } catch (error51) {
186650
+ return { detail: errorMessage(error51), ok: false, reason: "unwritable" };
186651
+ }
186652
+ return { ok: true };
186653
+ }
186654
+
186655
+ // src/shell/stdin.ts
186656
+ async function readStdin2() {
186657
+ if (process.stdin.isTTY === true)
186658
+ return "";
186659
+ const chunks = [];
186660
+ for await (const chunk of process.stdin)
186661
+ chunks.push(chunk);
186662
+ return Buffer.concat(chunks).toString("utf8");
186663
+ }
186664
+
186665
+ // src/domains/interactiveRunner/deps.ts
186666
+ function makeInteractiveRunnerDeps(options) {
186667
+ return {
186668
+ collectRunFiles: () => collectRunFiles({ cwd: options.cwd, fs: options.fs }),
186669
+ cwd: options.cwd,
186670
+ env: options.env,
186671
+ makeRunnerId,
186672
+ readFile: (path9) => options.fs.readFile(path9),
186673
+ readStdin: readStdin2,
186674
+ sleep,
186675
+ store: makeRunnerStore({ cwd: options.cwd, fs: options.fs }),
186676
+ writeScreenshot: (screenshot) => writeScreenshot({ ...screenshot, fs: options.fs })
186677
+ };
186678
+ }
186679
+
186680
+ // src/commands/runner/context.ts
186681
+ var runnerFlagDescription = "Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner";
186682
+ function runnerDeps(ctx) {
186683
+ return makeInteractiveRunnerDeps({
186684
+ cwd: process.cwd(),
186685
+ env: process.env,
186686
+ fs: ctx.fs
186687
+ });
186688
+ }
186689
+
186690
+ // src/commands/runner/interact.register.ts
186691
+ var defaultScreenshotPath = "screenshot.jpg";
186692
+ var screenshotExamples = `
186693
+ Examples:
186694
+ $ qawolf runner screenshot
186695
+ $ qawolf runner screenshot --out screens/step-3.jpg`;
186696
+ var actExamples = `
186697
+ Examples:
186698
+ $ qawolf runner act click --button left --x 480 --y 260
186699
+ $ qawolf runner act type --text "hello@example.com"
186700
+ $ qawolf runner act keypress --keys Control a
186701
+ $ qawolf runner act navigate --url https://example.com
186702
+ $ qawolf runner act drag --path '[{"x":10,"y":20},{"x":80,"y":90}]'
186703
+ $ echo '{"type":"click","button":"left","x":1,"y":2}' | qawolf runner act -`;
186704
+ var execExamples = `
186705
+ Examples:
186706
+ $ qawolf runner exec snippet.ts
186707
+ $ echo 'console.log(await page.title())' | qawolf runner exec -
186708
+ $ qawolf runner exec snippet.ts --file flows/checkout.flow.ts`;
186709
+ function registerRunnerInteractCommands(runner, signals) {
186710
+ 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));
186711
+ 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").option("--button <button>", "click: left, right, wheel, back or forward").option("--keys <keys...>", "keypress: modifiers and the key, e.g. Control a").option("--path <json>", "drag: JSON array of points to drag through").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((action2, opts, command) => withAuthContext(signals, (ctx) => handleRunnerAct(ctx, {
186712
+ flags: {
186713
+ button: opts.button,
186714
+ keys: opts.keys,
186715
+ path: opts.path,
186716
+ scrollX: opts.scrollX,
186717
+ scrollY: opts.scrollY,
186718
+ text: opts.text,
186719
+ url: opts.url,
186720
+ x: opts.x,
186721
+ y: opts.y
186722
+ },
186723
+ runner: opts.runner,
186724
+ type: action2
186725
+ }, runnerDeps(ctx)))(opts, command));
186726
+ 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((file2, opts, command) => withAuthContext(signals, (ctx) => handleRunnerExec(ctx, { contextFile: opts.file, runner: opts.runner, source: file2 }, runnerDeps(ctx)))(opts, command));
186727
+ }
186728
+
186729
+ // src/domains/interactiveRunner/readJournal.ts
186730
+ function journalReadFailure(failed) {
186731
+ const { type: _type, ...failure } = failed;
186732
+ return failure;
186733
+ }
186734
+ var unreachableFailure = {
186735
+ error: interactiveRunnerMessages.runnerUnreachable,
186736
+ exitCode: exitCodes.network
186737
+ };
186738
+ async function readJournal(ctx, runnerId, request) {
186739
+ const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.readJournal, {
186740
+ id: runnerId,
186741
+ stream: request.stream,
186742
+ ...request.runId === undefined ? {} : { runId: request.runId },
186743
+ ...request.sinceSequence === undefined ? {} : { sinceSequence: request.sinceSequence },
186744
+ ...request.tail === undefined ? {} : { tail: request.tail }
186745
+ }, runnerCallOptions);
186746
+ if (!result.ok) {
186747
+ return {
186748
+ ...failureFields(result),
186749
+ exitCode: exitCodes.network,
186750
+ type: "failed"
186751
+ };
186752
+ }
186753
+ if (result.value.outcome === "runner-unreachable") {
186754
+ return { type: "unreachable" };
186755
+ }
186756
+ return { type: "read", value: result.value };
186757
+ }
186758
+
186759
+ // src/domains/interactiveRunner/keepalive.ts
186760
+ async function handleRunnerKeepalive(ctx, options, deps) {
186761
+ const resolved = await resolveRunner(ctx, { autoLaunch: false, runner: options.runner }, deps);
186762
+ if (resolved.type === "failed") {
186763
+ return { ...failureFields(resolved), exitCode: resolved.exitCode };
186764
+ }
186765
+ const window2 = await readJournal(ctx, resolved.runnerId, {
186766
+ stream: "run-status",
186767
+ tail: 1
186768
+ });
186769
+ if (window2.type === "unreachable")
186770
+ return { ...unreachableFailure };
186771
+ if (window2.type === "failed")
186772
+ return window2;
186773
+ ctx.ui.output({ id: resolved.runnerId, outcome: "alive" }, interactiveRunnerMessages.keptAlive(resolved.runnerId));
186774
+ return;
186775
+ }
186776
+
186777
+ // src/domains/interactiveRunner/stop.ts
186778
+ async function handleRunnerStop(ctx, options, deps) {
186779
+ const resolved = await resolveRunner(ctx, { autoLaunch: false, runner: options.runner }, deps);
186780
+ if (resolved.type === "failed") {
186781
+ return { ...failureFields(resolved), exitCode: resolved.exitCode };
186782
+ }
186783
+ const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.stop, {
186784
+ id: resolved.runnerId
186785
+ }, runnerCallOptions);
186786
+ if (!result.ok) {
186787
+ return { ...failureFields(result), exitCode: exitCodes.network };
186788
+ }
186789
+ await deps.store.readDefaultRunnerId().then(async (storedDefault) => {
186790
+ if (storedDefault === resolved.runnerId) {
186791
+ await deps.store.clearDefaultRunnerId();
186792
+ }
186793
+ }).catch(() => {
186794
+ ctx.ui.warn(interactiveRunnerMessages.defaultNotForgotten(resolved.runnerId));
186795
+ });
186796
+ ctx.ui.output(result.value, result.value.outcome === "stopped" ? interactiveRunnerMessages.stopped(result.value.id) : interactiveRunnerMessages.notRunning(result.value.id));
186797
+ return;
186798
+ }
186799
+
186800
+ // src/commands/runner/lifecycle.register.ts
186801
+ var launchExamples = `
186802
+ Examples:
186803
+ $ qawolf runner launch
186804
+ $ qawolf runner launch --name node20WithAndroid
186805
+ $ qawolf runner launch --id ci`;
186806
+ var keepaliveExamples = `
186807
+ Examples:
186808
+ $ qawolf runner keepalive
186809
+ $ qawolf runner keepalive --runner ci`;
186810
+ function registerRunnerLifecycleCommands(runner, signals) {
186811
+ declareCommandKind(runner.command("launch"), "write").description("Launch an interactive runner and make it this directory's default").option("--id <id>", "Id to launch under. Relaunching an id attaches to that runner").option("--name <image>", "Runner image to run, e.g. node20WithPlaywright").addHelpText("after", launchExamples).action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerLaunch(ctx, { id: opts.id, name: opts.name }, runnerDeps(ctx)))(opts, command));
186812
+ declareCommandKind(runner.command("stop"), "write").description("Stop an interactive runner").option("--runner <id>", runnerFlagDescription).action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerStop(ctx, { runner: opts.runner }, runnerDeps(ctx)))(opts, command));
186813
+ declareCommandKind(runner.command("keepalive"), "read").description("Reset a runner's inactivity clock, for a caller that pauses between actions").option("--runner <id>", runnerFlagDescription).addHelpText("after", keepaliveExamples).action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerKeepalive(ctx, { runner: opts.runner }, runnerDeps(ctx)))(opts, command));
186814
+ }
186815
+
186816
+ // src/core/interactiveRunner/followTimeout.ts
186817
+ var defaultFollowTimeoutSeconds = 3600;
186818
+ var followTimeoutSchema = exports_external.coerce.number().int().positive();
186819
+ function parseFollowTimeout(seconds) {
186820
+ if (seconds === undefined) {
186821
+ return { ok: true, seconds: defaultFollowTimeoutSeconds };
186822
+ }
186823
+ const parsed = followTimeoutSchema.safeParse(seconds);
186824
+ if (!parsed.success)
186825
+ return { error: exports_external.prettifyError(parsed.error), ok: false };
186826
+ return { ok: true, seconds: parsed.data };
186827
+ }
186828
+
186829
+ // src/core/interactiveRunner/journal.ts
186830
+ function formatJournalLine(entry, options) {
186831
+ const data2 = options.envelope ? entry : entry.payload;
186832
+ return { data: data2, line: JSON.stringify(data2) };
186833
+ }
186834
+ function countSkippedEntries(cursor3, oldestAvailableSequence) {
186835
+ if (oldestAvailableSequence <= cursor3 + 1)
186836
+ return 0;
186837
+ return oldestAvailableSequence - cursor3 - 1;
186838
+ }
186839
+ var runStatusPayloadSchema = exports_external.object({
186840
+ errorMessage: exports_external.string().optional(),
186841
+ runId: exports_external.string(),
186842
+ status: exports_external.string()
186843
+ });
186844
+ function readRunSettlement(payload) {
186845
+ const parsed = runStatusPayloadSchema.safeParse(payload);
186846
+ if (!parsed.success)
186847
+ return { type: "unreadable" };
186848
+ if (parsed.data.status === "in-progress")
186849
+ return { type: "in-progress" };
186850
+ return {
186851
+ errorMessage: parsed.data.errorMessage,
186852
+ status: parsed.data.status,
186853
+ type: "settled"
186854
+ };
186855
+ }
186856
+ var runLogPayloadSchema = exports_external.object({ message: exports_external.string() });
186857
+ function formatRunLogLine(payload) {
186858
+ const parsed = runLogPayloadSchema.safeParse(payload);
186859
+ return parsed.success ? parsed.data.message : JSON.stringify(payload);
186860
+ }
186861
+
186862
+ // src/domains/interactiveRunner/eventsOptions.ts
186863
+ var countSchema = exports_external.coerce.number().int().positive();
186864
+ var sequenceSchema = exports_external.coerce.number().int().nonnegative();
186865
+ function parseEventsOptions(options) {
186866
+ const parsed = exports_external.object({
186867
+ run: readJournalRequestSchema.shape.runId,
186868
+ since: sequenceSchema.optional(),
186869
+ stream: journalStreamSchema,
186870
+ tail: countSchema.optional()
186871
+ }).safeParse({
186872
+ stream: options.stream,
186873
+ ...options.run === undefined ? {} : { run: options.run },
186874
+ ...options.since === undefined ? {} : { since: options.since },
186875
+ ...options.tail === undefined ? {} : { tail: options.tail }
186876
+ });
186877
+ if (!parsed.success)
186878
+ return { error: exports_external.prettifyError(parsed.error), ok: false };
186879
+ return {
186880
+ ok: true,
186881
+ value: {
186882
+ runId: parsed.data.run,
186883
+ sinceSequence: parsed.data.since,
186884
+ stream: parsed.data.stream,
186885
+ tail: parsed.data.tail
186886
+ }
186887
+ };
186888
+ }
186889
+
186890
+ // src/domains/interactiveRunner/journalCursor.ts
186891
+ function createJournalCursor(ctx, runnerId, request) {
186892
+ let sinceSequence = request.sinceSequence;
186893
+ let tail = request.tail;
186894
+ let warnedUnsearchedHistory = false;
186895
+ return async function read() {
186896
+ const window2 = await readJournal(ctx, runnerId, {
186897
+ ...request,
186898
+ sinceSequence,
186899
+ tail
186900
+ });
186901
+ if (window2.type !== "read")
186902
+ return window2;
186903
+ if (sinceSequence !== undefined) {
186904
+ const skipped = countSkippedEntries(sinceSequence, window2.value.oldestAvailableSequence);
186905
+ if (skipped > 0) {
186906
+ ctx.ui.warn(interactiveRunnerMessages.skippedEntries(request.stream, skipped));
186907
+ }
186908
+ }
186909
+ if (window2.value.hasUnsearchedHistory && !warnedUnsearchedHistory) {
186910
+ warnedUnsearchedHistory = true;
186911
+ ctx.ui.warn(interactiveRunnerMessages.unsearchedHistory(request.stream));
186912
+ }
186913
+ tail = undefined;
186914
+ sinceSequence = Math.max(sinceSequence ?? 0, window2.value.nextSequence);
186915
+ return { entries: window2.value.entries, type: "entries" };
186916
+ };
186917
+ }
186918
+ var unreachableGraceMs = 60000;
186919
+ function createUnreachableBudget(pollIntervalMs2) {
186920
+ const limit = Math.ceil(unreachableGraceMs / pollIntervalMs2);
186921
+ let consecutive = 0;
186922
+ return {
186923
+ exhausted: () => ++consecutive >= limit,
186924
+ reset: () => {
186925
+ consecutive = 0;
186926
+ }
186927
+ };
186928
+ }
186929
+
186930
+ // src/domains/interactiveRunner/events.ts
186931
+ var pollIntervalMs2 = 1000;
186932
+ async function handleRunnerEvents(ctx, options, deps) {
186933
+ const parsed = parseEventsOptions(options);
186934
+ if (!parsed.ok)
186935
+ return { error: parsed.error, exitCode: exitCodes.invalidArgs };
186936
+ const timeout = parseFollowTimeout(options.timeout);
186937
+ if (!timeout.ok) {
186938
+ return { error: timeout.error, exitCode: exitCodes.invalidArgs };
186939
+ }
186940
+ const resolved = await resolveRunner(ctx, { autoLaunch: false, runner: options.runner }, deps);
186941
+ if (resolved.type === "failed") {
186942
+ return { ...failureFields(resolved), exitCode: resolved.exitCode };
186943
+ }
186944
+ const known = knownJournalStreams;
186945
+ if (!known.includes(parsed.value.stream)) {
186946
+ ctx.ui.warn(interactiveRunnerMessages.unknownStream(parsed.value.stream, known));
186947
+ }
186948
+ const read = createJournalCursor(ctx, resolved.runnerId, parsed.value);
186949
+ const unreachable = createUnreachableBudget(pollIntervalMs2);
186950
+ const maxPolls = Math.max(1, Math.ceil(timeout.seconds * 1000 / pollIntervalMs2));
186951
+ for (let poll = 1;; poll++) {
186952
+ const window2 = await read();
186953
+ if (window2.type === "failed")
186954
+ return journalReadFailure(window2);
186955
+ if (window2.type === "unreachable") {
186956
+ if (!options.follow || unreachable.exhausted()) {
186957
+ return { ...unreachableFailure };
186958
+ }
186959
+ } else {
186960
+ unreachable.reset();
186961
+ for (const entry of window2.entries) {
186962
+ const { data: data2, line } = formatJournalLine(entry, {
186963
+ envelope: options.envelope
186964
+ });
186965
+ ctx.ui.stream(data2, line);
186966
+ }
186967
+ if (!options.follow)
186968
+ return;
186969
+ }
186970
+ if (poll >= maxPolls) {
186971
+ return {
186972
+ error: interactiveRunnerMessages.followEventsTimedOut(parsed.value.stream, timeout.seconds),
186973
+ exitCode: exitCodes.timeout
186974
+ };
186975
+ }
186976
+ await deps.sleep(pollIntervalMs2);
186977
+ }
186978
+ }
186979
+
186980
+ // src/domains/interactiveRunner/collectFiles.ts
186981
+ async function collectRunFiles2(deps) {
186982
+ try {
186983
+ return { files: await deps.collectRunFiles(), ok: true };
186984
+ } catch (error51) {
186985
+ const path9 = readErrorPath(error51);
186986
+ return {
186987
+ error: path9 === undefined ? interactiveRunnerMessages.filesUnreadable(errorMessage(error51)) : interactiveRunnerMessages.fileUnreadable(path9, errorMessage(error51)),
186988
+ ok: false
186989
+ };
186990
+ }
186991
+ }
186992
+ function readErrorPath(error51) {
186993
+ if (typeof error51 !== "object" || error51 === null)
186994
+ return;
186995
+ const path9 = error51.path;
186996
+ return typeof path9 === "string" ? path9 : undefined;
186997
+ }
186998
+
186999
+ // src/domains/interactiveRunner/followRun.ts
187000
+ var pollIntervalMs3 = 1000;
187001
+ function findSettlement(entries) {
187002
+ for (const entry of entries) {
187003
+ const settlement = readRunSettlement(entry.payload);
187004
+ if (settlement.type !== "settled")
187005
+ continue;
187006
+ if (settlement.status === "passed")
187007
+ return { type: "passed" };
187008
+ if (settlement.status === "failed") {
187009
+ return { errorMessage: settlement.errorMessage, type: "failed" };
187010
+ }
187011
+ return { status: settlement.status, type: "unrecognized" };
187012
+ }
187013
+ return;
187014
+ }
187015
+ function reportSettlement(ctx, settlement) {
187016
+ if (settlement.type === "passed") {
187017
+ ctx.ui.success(interactiveRunnerMessages.runPassed);
187018
+ return;
187019
+ }
187020
+ return {
187021
+ error: settlement.type === "failed" ? interactiveRunnerMessages.runFailed(settlement.errorMessage) : interactiveRunnerMessages.runSettledUnknown(settlement.status),
187022
+ exitCode: exitCodes.testFailure
187023
+ };
187024
+ }
187025
+ async function followRun(ctx, options, deps) {
187026
+ const readLogs = createJournalCursor(ctx, options.runnerId, {
187027
+ runId: options.runId,
187028
+ stream: "run-logs"
187029
+ });
187030
+ const readStatus = createJournalCursor(ctx, options.runnerId, {
187031
+ runId: options.runId,
187032
+ stream: "run-status"
187033
+ });
187034
+ const unreachable = createUnreachableBudget(pollIntervalMs3);
187035
+ const printLogs = async () => {
187036
+ const logs = await readLogs();
187037
+ if (logs.type !== "entries")
187038
+ return logs;
187039
+ for (const entry of logs.entries) {
187040
+ ctx.ui.stream(entry, formatRunLogLine(entry.payload));
187041
+ }
187042
+ return logs;
187043
+ };
187044
+ const maxPolls = Math.max(1, Math.ceil(options.timeoutSeconds * 1000 / pollIntervalMs3));
187045
+ for (let poll = 1;; poll++) {
187046
+ const logs = await printLogs();
187047
+ if (logs.type === "failed")
187048
+ return journalReadFailure(logs);
187049
+ const status = logs.type === "unreachable" ? logs : await readStatus();
187050
+ if (status.type === "failed")
187051
+ return journalReadFailure(status);
187052
+ if (logs.type === "unreachable" || status.type === "unreachable") {
187053
+ if (unreachable.exhausted())
187054
+ return { ...unreachableFailure };
187055
+ } else {
187056
+ unreachable.reset();
187057
+ const settlement = findSettlement(status.entries);
187058
+ if (settlement !== undefined) {
187059
+ await printLogs();
187060
+ return reportSettlement(ctx, settlement);
187061
+ }
187062
+ }
187063
+ if (poll >= maxPolls) {
187064
+ return {
187065
+ error: interactiveRunnerMessages.followTimedOut(options.runId, options.runnerId, options.timeoutSeconds),
187066
+ exitCode: exitCodes.timeout
187067
+ };
187068
+ }
187069
+ await deps.sleep(pollIntervalMs3);
187070
+ }
187071
+ }
187072
+
187073
+ // src/domains/interactiveRunner/runFlow.ts
187074
+ async function handleRunnerRun(ctx, options, deps) {
187075
+ const timeout = parseFollowTimeout(options.timeout);
187076
+ if (!timeout.ok) {
187077
+ return { error: timeout.error, exitCode: exitCodes.invalidArgs };
187078
+ }
187079
+ const entryPointPath = toCollectedPath(deps.cwd, options.entryPoint);
187080
+ const collected = await collectRunFiles2(deps);
187081
+ if (!collected.ok) {
187082
+ return { error: collected.error, exitCode: exitCodes.config };
187083
+ }
187084
+ const files = collected.files;
187085
+ const check2 = checkRunFiles(files, entryPointPath);
187086
+ if (check2.type !== "ok") {
187087
+ return {
187088
+ error: describeRunFilesCheck(check2),
187089
+ exitCode: exitCodes.invalidArgs
187090
+ };
187091
+ }
187092
+ const resolved = await resolveRunner(ctx, { autoLaunch: true, runner: options.runner }, deps);
187093
+ if (resolved.type === "failed") {
187094
+ return { ...failureFields(resolved), exitCode: resolved.exitCode };
187095
+ }
187096
+ announceRunner(ctx, resolved);
187097
+ const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.runFlow, { entryPointPath, files, id: resolved.runnerId }, runnerCallOptions);
187098
+ if (!result.ok) {
187099
+ return { ...failureFields(result), exitCode: exitCodes.network };
187100
+ }
187101
+ switch (result.value.outcome) {
187102
+ case "runner-target-mismatch":
187103
+ return {
187104
+ error: interactiveRunnerMessages.targetMismatch(result.value.runnerName, result.value.requiredRunnerName),
187105
+ exitCode: exitCodes.invalidArgs
187106
+ };
187107
+ case "runner-unreachable":
187108
+ return {
187109
+ error: interactiveRunnerMessages.submitMayHaveStarted,
187110
+ exitCode: exitCodes.network
187111
+ };
187112
+ case "submitted": {
187113
+ const runId = result.value.runId;
187114
+ if (!options.follow) {
187115
+ ctx.ui.output({ runId, runnerId: resolved.runnerId }, interactiveRunnerMessages.runSubmitted(runId));
187116
+ return;
187117
+ }
187118
+ ctx.ui.info(interactiveRunnerMessages.runSubmitted(runId));
187119
+ return followRun(ctx, {
187120
+ runId,
187121
+ runnerId: resolved.runnerId,
187122
+ timeoutSeconds: timeout.seconds
187123
+ }, deps);
187124
+ }
187125
+ default: {
187126
+ result.value;
187127
+ return {
187128
+ error: interactiveRunnerMessages.runSubmitAnsweredUnknown(String(result.value.outcome)),
187129
+ exitCode: exitCodes.network
187130
+ };
187131
+ }
187132
+ }
187133
+ }
187134
+
187135
+ // src/commands/runner/run.register.ts
187136
+ var runExamples2 = `
187137
+ Examples:
187138
+ $ qawolf runner run flows/checkout.flow.ts
187139
+ $ qawolf runner run flows/checkout.flow.ts --follow`;
187140
+ var eventsExamples = `
187141
+ Examples:
187142
+ $ qawolf runner events recorder --tail 5
187143
+ $ qawolf runner events run-logs --run <runId> --follow
187144
+ $ qawolf runner events console --since 120 --json`;
187145
+ function registerRunnerRunCommands(runner, signals) {
187146
+ declareCommandKind(runner.command("run <file>"), "write").description("Run a flow on an interactive runner, shipping the current directory's files with it").option("--follow", "Stream the run's logs until it settles", false).option("--runner <id>", runnerFlagDescription).option("--timeout <seconds>", "Give up following after this long. Following keeps the runner alive, so a run that never settles would otherwise bill until the terminal closed", String(defaultFollowTimeoutSeconds)).addHelpText("after", runExamples2).action((file2, opts, command) => withAuthContext(signals, (ctx) => handleRunnerRun(ctx, {
187147
+ entryPoint: file2,
187148
+ follow: opts.follow,
187149
+ runner: opts.runner,
187150
+ timeout: opts.timeout
187151
+ }, runnerDeps(ctx)))(opts, command));
187152
+ declareCommandKind(runner.command("events <stream>"), "read").description(`Print a runner's journal, one entry per line. QA Wolf writes ${knownJournalStreams.join(", ")}`).option("--follow", "Keep reading as new entries arrive. Reading counts as activity, so a follow left open keeps the runner alive and billing", false).option("--run <id>", "Restrict run-scoped streams to one run").option("--runner <id>", runnerFlagDescription).option("--since <sequence>", "Read entries after this sequence").option("--tail <count>", "Read only the newest <count> entries").option("--timeout <seconds>", "Give up following after this long. Reading keeps the runner alive, so a follow left open would otherwise bill until the terminal closed", String(defaultFollowTimeoutSeconds)).addHelpText("after", eventsExamples).action((stream, opts, command) => withAuthContext(signals, (ctx) => handleRunnerEvents(ctx, {
187153
+ envelope: Boolean(command.optsWithGlobals().json),
187154
+ follow: opts.follow,
187155
+ run: opts.run,
187156
+ runner: opts.runner,
187157
+ since: opts.since,
187158
+ stream,
187159
+ tail: opts.tail,
187160
+ timeout: opts.timeout
187161
+ }, runnerDeps(ctx)))(opts, command));
187162
+ }
187163
+
187164
+ // src/commands/runner/index.ts
187165
+ function registerRunnerCommand(program2, signals) {
187166
+ const runner = program2.command("runner").description("Drive an interactive runner on the QA Wolf platform");
187167
+ registerRunnerLifecycleCommands(runner, signals);
187168
+ registerRunnerRunCommands(runner, signals);
187169
+ registerRunnerInteractCommands(runner, signals);
187170
+ }
187171
+
186047
187172
  // src/commands/program.ts
186048
187173
  function createProgram({
186049
187174
  signals
@@ -186056,6 +187181,7 @@ function createProgram({
186056
187181
  registerFlowsCommand(program2, signals);
186057
187182
  registerInitCommand(program2, signals);
186058
187183
  registerInstallCommand(program2, signals);
187184
+ registerRunnerCommand(program2, signals);
186059
187185
  registerPublicApiCommands(program2, signals);
186060
187186
  return program2;
186061
187187
  }
@@ -186079,4 +187205,4 @@ createProgram({ signals }).parseAsync().catch(() => {
186079
187205
  process.exitCode = 1;
186080
187206
  }).finally(() => flushAndExit(typeof process.exitCode === "number" ? process.exitCode : 0));
186081
187207
 
186082
- //# debugId=45F0ABBEFD04330664756E2164756E21
187208
+ //# debugId=80A0CD4106C384B664756E2164756E21