@qawolf/cli 1.11.0 → 1.11.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
@@ -8514,7 +8514,7 @@ var require_BufferList = __commonJS((exports, module) => {
8514
8514
  this.head = this.tail = null;
8515
8515
  this.length = 0;
8516
8516
  };
8517
- BufferList.prototype.join = function join35(s3) {
8517
+ BufferList.prototype.join = function join36(s3) {
8518
8518
  if (this.length === 0)
8519
8519
  return "";
8520
8520
  var p3 = this.head;
@@ -23182,7 +23182,7 @@ var require_BufferList2 = __commonJS((exports, module) => {
23182
23182
  this.head = this.tail = null;
23183
23183
  this.length = 0;
23184
23184
  };
23185
- BufferList.prototype.join = function join35(s3) {
23185
+ BufferList.prototype.join = function join36(s3) {
23186
23186
  if (this.length === 0)
23187
23187
  return "";
23188
23188
  var p3 = this.head;
@@ -134457,7 +134457,7 @@ var require_snapshot_utils = __commonJS((exports, module) => {
134457
134457
  // node_modules/undici/lib/mock/snapshot-recorder.js
134458
134458
  var require_snapshot_recorder = __commonJS((exports, module) => {
134459
134459
  var { writeFile, readFile, mkdir } = __require("node:fs/promises");
134460
- var { dirname: dirname11, resolve: resolve10 } = __require("node:path");
134460
+ var { dirname: dirname12, resolve: resolve10 } = __require("node:path");
134461
134461
  var { setTimeout: setTimeout3, clearTimeout: clearTimeout2 } = __require("node:timers");
134462
134462
  var { InvalidArgumentError: InvalidArgumentError3, UndiciError } = require_errors7();
134463
134463
  var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
@@ -134652,7 +134652,7 @@ var require_snapshot_recorder = __commonJS((exports, module) => {
134652
134652
  throw new InvalidArgumentError3("Snapshot path is required");
134653
134653
  }
134654
134654
  const resolvedPath = resolve10(path4);
134655
- await mkdir(dirname11(resolvedPath), { recursive: true });
134655
+ await mkdir(dirname12(resolvedPath), { recursive: true });
134656
134656
  const data2 = Array.from(this.#snapshots.entries()).map(([hash2, snapshot]) => ({
134657
134657
  hash: hash2,
134658
134658
  snapshot
@@ -156448,6 +156448,12 @@ var interactiveRunnerMessages = {
156448
156448
  targetMismatch: (runnerName, requiredRunnerName) => `This flow needs a ${requiredRunnerName} runner and ${runnerName} is running. Launch one with --name ${requiredRunnerName}.`
156449
156449
  };
156450
156450
  // src/core/messages/runner.ts
156451
+ var maxListedNames = 5;
156452
+ function listNames(names) {
156453
+ const shown = names.slice(0, maxListedNames).join(", ");
156454
+ const rest = names.length - maxListedNames;
156455
+ return rest > 0 ? `${shown} and ${rest} more` : shown;
156456
+ }
156451
156457
  var runnerMessages = {
156452
156458
  playwrightLoadFailed: (envDir, detail) => packageLoadFailed("Playwright", envDir, detail),
156453
156459
  androidWorkersUnsupported: "Android flows are not yet supported with --workers > 1; rerun Android flows with --workers 1.",
@@ -156477,6 +156483,12 @@ ${dir}`,
156477
156483
  ].join(`
156478
156484
  `),
156479
156485
  outerHopCandidateRejected: (dir, missing) => `outer-hop candidate rejected: ${dir} missing ${missing.join(", ")}`,
156486
+ outerHopFallbackNotice: (dir, missing) => [
156487
+ `Not reusing ${dir} — it is missing ${listNames(missing)}.`,
156488
+ `Installing the declared dependencies instead. Run 'npm install' there to reuse it.`
156489
+ ].join(`
156490
+ `),
156491
+ outerHopCarriedOver: (names) => `Carried over ${pluralize(names.length, "undeclared package")} from the project node_modules: ${listNames(names)}. Declare them in package.json "dependencies".`,
156480
156492
  moduleNotFoundHint: (pkg, projectDir) => projectDir === undefined ? `Hint: '${pkg}' could not be resolved. Run from within your flows project so its dependencies can be found.` : `Hint: '${pkg}' could not be resolved. Ensure it is declared in ${projectDir}/package.json "dependencies" and run npm install in that project.`
156481
156493
  };
156482
156494
  // src/core/messages/updateCheck.ts
@@ -175911,7 +175923,7 @@ function startUpdateCheck(deps) {
175911
175923
  // package.json
175912
175924
  var package_default = {
175913
175925
  name: "@qawolf/cli",
175914
- version: "1.11.0",
175926
+ version: "1.11.2",
175915
175927
  description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
175916
175928
  keywords: [
175917
175929
  "automation",
@@ -175979,7 +175991,6 @@ var package_default = {
175979
175991
  appium: "2.11.3",
175980
175992
  commander: "14.0.3",
175981
175993
  "env-paths": "4.0.0",
175982
- "expect-webdriverio": "5.6.5",
175983
175994
  picomatch: "4.0.4",
175984
175995
  pino: "10.3.1",
175985
175996
  playwright: "1.62.0",
@@ -175996,6 +176007,7 @@ var package_default = {
175996
176007
  "@types/bun": "1.3.14",
175997
176008
  "@types/picomatch": "4.0.3",
175998
176009
  "appium-uiautomator2-driver": "4.2.9",
176010
+ "expect-webdriverio": "5.6.5",
175999
176011
  knip: "6.16.1",
176000
176012
  oxfmt: "0.54.0",
176001
176013
  oxlint: "1.69.0",
@@ -177789,7 +177801,8 @@ async function expandPatterns(patterns, cwd, logger, fs3) {
177789
177801
  for (const root of roots) {
177790
177802
  const matches = await glob(effectivePatterns, {
177791
177803
  cwd: root,
177792
- absolute: true
177804
+ absolute: true,
177805
+ ignore: ["**/node_modules/**"]
177793
177806
  });
177794
177807
  for (const match of matches) {
177795
177808
  const file2 = resolve3(match);
@@ -182468,6 +182481,14 @@ function buildPatternArgs(pattern) {
182468
182481
  return pattern ? [pattern] : [];
182469
182482
  }
182470
182483
 
182484
+ // src/core/flowFailureHint.ts
182485
+ function flowFailureHint(errText, projectDir) {
182486
+ const missingPackage = extractMissingPackage(errText);
182487
+ if (missingPackage === undefined)
182488
+ return;
182489
+ return runnerMessages.moduleNotFoundHint(missingPackage, projectDir);
182490
+ }
182491
+
182471
182492
  // src/shell/reporter/createCompositeReporter.ts
182472
182493
  function callEach(reporters, pick2, event) {
182473
182494
  for (const r2 of reporters) {
@@ -182534,6 +182555,26 @@ function formatErrorWithCause(err) {
182534
182555
  `);
182535
182556
  }
182536
182557
 
182558
+ // src/domains/runner/jsonFailureDetails.ts
182559
+ function collectJsonFailureDetails(reporter, projectDir) {
182560
+ const details = [];
182561
+ return {
182562
+ reporter: createCompositeReporter([
182563
+ reporter,
182564
+ {
182565
+ onFlowFail: ({ err }) => {
182566
+ const detail = formatErrorWithCause(err);
182567
+ const hint = flowFailureHint(detail, projectDir);
182568
+ details.push(hint === undefined ? detail : `${detail}
182569
+
182570
+ ${hint}`);
182571
+ }
182572
+ }
182573
+ ]),
182574
+ details
182575
+ };
182576
+ }
182577
+
182537
182578
  // src/domains/runner/runAndroidFlowUtils.ts
182538
182579
  import { parseExecutionTarget as parseExecutionTarget3 } from "@qawolf/flow-targets";
182539
182580
 
@@ -182589,7 +182630,7 @@ class FlowRunError extends Error {
182589
182630
  }
182590
182631
  }
182591
182632
 
182592
- // src/domains/runner/runInternals.ts
182633
+ // src/domains/runner/dispatchFlow.ts
182593
182634
  async function dispatchFlow({
182594
182635
  deps,
182595
182636
  flow,
@@ -182881,13 +182922,9 @@ async function flowsRun(ctx, files, flags, deps) {
182881
182922
  return { error: runnerMessages.androidWorkersUnsupported, exitCode: 2 };
182882
182923
  }
182883
182924
  const { webOptions, androidOptions } = buildRunOptions(flags);
182884
- const failureDetails = [];
182885
- const reporter = ctx.outputMode === "json" ? createCompositeReporter([
182886
- deps.reporter,
182887
- {
182888
- onFlowFail: ({ err }) => failureDetails.push(formatErrorWithCause(err))
182889
- }
182890
- ]) : deps.reporter;
182925
+ const collected = ctx.outputMode === "json" ? collectJsonFailureDetails(deps.reporter, deps.projectDir) : undefined;
182926
+ const failureDetails = collected?.details ?? [];
182927
+ const reporter = collected?.reporter ?? deps.reporter;
182891
182928
  const result = await executeFlows({
182892
182929
  ctx,
182893
182930
  deps: { ...deps, reporter },
@@ -182975,7 +183012,7 @@ async function defaultRunWebFlowDeps(envDir = process.cwd(), signals) {
182975
183012
 
182976
183013
  // src/domains/runtimeEnv/prepareRunDir.ts
182977
183014
  import { createHash as createHash4 } from "node:crypto";
182978
- import { join as join32, resolve as resolve9 } from "node:path";
183015
+ import { join as join33, resolve as resolve9 } from "node:path";
182979
183016
 
182980
183017
  // src/domains/runtimeEnv/execSubpathImports.ts
182981
183018
  import { join as join26 } from "node:path";
@@ -183044,9 +183081,82 @@ async function populateInnerHop(args) {
183044
183081
  }
183045
183082
 
183046
183083
  // src/domains/runtimeEnv/outerHop.ts
183084
+ import { lstat as lstat2 } from "node:fs/promises";
183085
+ import { dirname as dirname10, join as join30 } from "node:path";
183086
+
183087
+ // src/domains/runtimeEnv/carryOverPackages.ts
183047
183088
  import { lstat } from "node:fs/promises";
183048
183089
  import { dirname as dirname9, join as join29 } from "node:path";
183090
+
183091
+ // src/domains/runtimeEnv/carryOverPlan.ts
183049
183092
  var executorPackageNames = new Set(pinnedPackages.map((p3) => p3.name));
183093
+ function planCarryOver(args) {
183094
+ const installed = new Set(args.installed);
183095
+ return args.present.filter((name) => !name.startsWith(".") && !installed.has(name) && !executorPackageNames.has(name));
183096
+ }
183097
+
183098
+ // src/domains/runtimeEnv/carryOverPackages.ts
183099
+ async function carryOverPackages(args) {
183100
+ const { projectModulesDir, nodeModulesDir, fs: fs4 } = args;
183101
+ const present = await listPackageNames(projectModulesDir, fs4);
183102
+ if (present.length === 0)
183103
+ return [];
183104
+ const names = planCarryOver({
183105
+ present,
183106
+ installed: await listPackageNames(nodeModulesDir, fs4)
183107
+ });
183108
+ const linked = [];
183109
+ for (const name of names) {
183110
+ const segments = name.split("/");
183111
+ const target = join29(nodeModulesDir, ...segments);
183112
+ if (await pathOccupied(target))
183113
+ continue;
183114
+ if (segments.length > 1)
183115
+ await fs4.mkdir(dirname9(target), { recursive: true });
183116
+ await createDirSymlink(join29(projectModulesDir, ...segments), target);
183117
+ linked.push(name);
183118
+ }
183119
+ return linked;
183120
+ }
183121
+ async function pathOccupied(path3) {
183122
+ try {
183123
+ await lstat(path3);
183124
+ return true;
183125
+ } catch {
183126
+ return false;
183127
+ }
183128
+ }
183129
+ async function listPackageNames(dir, fs4) {
183130
+ let entries;
183131
+ try {
183132
+ entries = await fs4.readdir(dir);
183133
+ } catch {
183134
+ return [];
183135
+ }
183136
+ const names = [];
183137
+ for (const entry of entries) {
183138
+ if (!entry.startsWith("@")) {
183139
+ if (fs4.existsSync(join29(dir, entry, "package.json")))
183140
+ names.push(entry);
183141
+ continue;
183142
+ }
183143
+ let scoped;
183144
+ try {
183145
+ scoped = await fs4.readdir(join29(dir, entry));
183146
+ } catch {
183147
+ continue;
183148
+ }
183149
+ for (const inner of scoped) {
183150
+ if (fs4.existsSync(join29(dir, entry, inner, "package.json"))) {
183151
+ names.push(`${entry}/${inner}`);
183152
+ }
183153
+ }
183154
+ }
183155
+ return names;
183156
+ }
183157
+
183158
+ // src/domains/runtimeEnv/outerHop.ts
183159
+ var executorPackageNames2 = new Set(pinnedPackages.map((p3) => p3.name));
183050
183160
  async function populateOuterHop(args) {
183051
183161
  const { projectDir, runDir, fs: fs4 } = args;
183052
183162
  if (projectDir === undefined)
@@ -183055,7 +183165,7 @@ async function populateOuterHop(args) {
183055
183165
  const depNames = Object.keys(installableDeps);
183056
183166
  const { satisfying, rejected } = await findSatisfyingNodeModulesDir(projectDir, depNames, fs4);
183057
183167
  if (satisfying !== undefined) {
183058
- await createDirSymlink(satisfying, join29(runDir, "node_modules"));
183168
+ await createDirSymlink(satisfying, join30(runDir, "node_modules"));
183059
183169
  return { mode: "symlink", nodeModulesDir: satisfying };
183060
183170
  }
183061
183171
  if (depNames.length === 0)
@@ -183064,24 +183174,29 @@ async function populateOuterHop(args) {
183064
183174
  await installOuterDeps(runDir, installableDeps, fs4, args.install ?? spawnNpmInstall);
183065
183175
  await linkPinnedPackages({
183066
183176
  depsRoot: args.depsRoot,
183067
- nodeModulesDir: join29(runDir, "node_modules"),
183177
+ nodeModulesDir: join30(runDir, "node_modules"),
183068
183178
  fs: fs4
183069
183179
  });
183070
- return { mode: "install", depCount: depNames.length, rejected };
183180
+ const carriedOver = await carryOverPackages({
183181
+ projectModulesDir: join30(projectDir, "node_modules"),
183182
+ nodeModulesDir: join30(runDir, "node_modules"),
183183
+ fs: fs4
183184
+ });
183185
+ return { mode: "install", depCount: depNames.length, rejected, carriedOver };
183071
183186
  }
183072
183187
  async function findSatisfyingNodeModulesDir(startDir, depNames, fs4) {
183073
183188
  const rejected = [];
183074
183189
  let dir = startDir;
183075
183190
  while (true) {
183076
- const candidate = join29(dir, "node_modules");
183191
+ const candidate = join30(dir, "node_modules");
183077
183192
  const stats = await lstatSafe(candidate);
183078
183193
  if (stats !== undefined && stats.isDirectory() && !stats.isSymbolicLink()) {
183079
- const missing = depNames.filter((name) => !fs4.existsSync(join29(candidate, name, "package.json")));
183194
+ const missing = depNames.filter((name) => !fs4.existsSync(join30(candidate, name, "package.json")));
183080
183195
  if (missing.length === 0)
183081
183196
  return { satisfying: candidate, rejected };
183082
183197
  rejected.push({ dir: candidate, missing });
183083
183198
  }
183084
- const parent = dirname9(dir);
183199
+ const parent = dirname10(dir);
183085
183200
  if (parent === dir)
183086
183201
  return { satisfying: undefined, rejected };
183087
183202
  dir = parent;
@@ -183089,7 +183204,7 @@ async function findSatisfyingNodeModulesDir(startDir, depNames, fs4) {
183089
183204
  }
183090
183205
  async function lstatSafe(path3) {
183091
183206
  try {
183092
- return await lstat(path3);
183207
+ return await lstat2(path3);
183093
183208
  } catch {
183094
183209
  return;
183095
183210
  }
@@ -183097,7 +183212,7 @@ async function lstatSafe(path3) {
183097
183212
  async function readInstallableDeps(projectDir, fs4) {
183098
183213
  let content;
183099
183214
  try {
183100
- content = await fs4.readFile(join29(projectDir, "package.json"));
183215
+ content = await fs4.readFile(join30(projectDir, "package.json"));
183101
183216
  } catch {
183102
183217
  return {};
183103
183218
  }
@@ -183112,11 +183227,11 @@ async function readInstallableDeps(projectDir, fs4) {
183112
183227
  const rawDeps = parsed["dependencies"];
183113
183228
  if (typeof rawDeps !== "object" || rawDeps === null)
183114
183229
  return {};
183115
- return Object.fromEntries(Object.entries(rawDeps).filter(([name]) => !executorPackageNames.has(name)).filter((entry) => typeof entry[1] === "string"));
183230
+ return Object.fromEntries(Object.entries(rawDeps).filter(([name]) => !executorPackageNames2.has(name)).filter((entry) => typeof entry[1] === "string"));
183116
183231
  }
183117
183232
  async function installOuterDeps(runDir, deps, fs4, install) {
183118
- await fs4.writeFile(join29(runDir, "package.json"), JSON.stringify({ name: "qawolf-run", private: true, dependencies: deps }, undefined, 2));
183119
- await fs4.writeFile(join29(runDir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
183233
+ await fs4.writeFile(join30(runDir, "package.json"), JSON.stringify({ name: "qawolf-run", private: true, dependencies: deps }, undefined, 2));
183234
+ await fs4.writeFile(join30(runDir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
183120
183235
  `);
183121
183236
  const result = await install(runDir);
183122
183237
  if (result.exitCode !== 0) {
@@ -183126,14 +183241,14 @@ async function installOuterDeps(runDir, deps, fs4, install) {
183126
183241
 
183127
183242
  // src/domains/runtimeEnv/stageFlowFiles.ts
183128
183243
  import { createHash as createHash3 } from "node:crypto";
183129
- import { basename as basename4, dirname as dirname10, join as join31, sep as sep4 } from "node:path";
183244
+ import { basename as basename4, dirname as dirname11, join as join32, sep as sep4 } from "node:path";
183130
183245
 
183131
183246
  // src/shell/copyDir.ts
183132
183247
  import { cp, readdir as readdir2 } from "node:fs/promises";
183133
- import { basename as basename3, join as join30 } from "node:path";
183248
+ import { basename as basename3, join as join31 } from "node:path";
183134
183249
  async function copyDirExcluding(source, destination, excludedNames) {
183135
183250
  const entries = await readdir2(source, { withFileTypes: true });
183136
- await Promise.all(entries.filter((entry) => !excludedNames.has(entry.name)).map((entry) => cp(join30(source, entry.name), join30(destination, entry.name), {
183251
+ await Promise.all(entries.filter((entry) => !excludedNames.has(entry.name)).map((entry) => cp(join31(source, entry.name), join31(destination, entry.name), {
183137
183252
  recursive: true,
183138
183253
  filter: (path3) => !excludedNames.has(basename3(path3))
183139
183254
  })));
@@ -183150,14 +183265,14 @@ async function stageFlowFiles(args) {
183150
183265
  if (files.length > 1) {
183151
183266
  return Promise.all(files.map((f3) => stageOneFile(f3, execDir, fs4)));
183152
183267
  }
183153
- await Promise.all(files.map((f3) => fs4.copyFile(f3, join31(execDir, basename4(f3)))));
183154
- return files.map((f3) => join31(execDir, basename4(f3)));
183268
+ await Promise.all(files.map((f3) => fs4.copyFile(f3, join32(execDir, basename4(f3)))));
183269
+ return files.map((f3) => join32(execDir, basename4(f3)));
183155
183270
  }
183156
183271
  async function stageOneFile(file2, execDir, fs4) {
183157
- const dirHash = createHash3("sha256").update(dirname10(file2)).digest("hex").slice(0, 8);
183158
- const subDir = join31(execDir, dirHash);
183272
+ const dirHash = createHash3("sha256").update(dirname11(file2)).digest("hex").slice(0, 8);
183273
+ const subDir = join32(execDir, dirHash);
183159
183274
  await fs4.mkdir(subDir, { recursive: true });
183160
- const dest = join31(subDir, basename4(file2));
183275
+ const dest = join32(subDir, basename4(file2));
183161
183276
  await fs4.copyFile(file2, dest);
183162
183277
  return dest;
183163
183278
  }
@@ -183165,7 +183280,7 @@ function remapPath(file2, projectDir, execDir) {
183165
183280
  if (file2 === projectDir)
183166
183281
  return execDir;
183167
183282
  if (file2.startsWith(projectDir + sep4)) {
183168
- return join31(execDir, file2.slice(projectDir.length + 1));
183283
+ return join32(execDir, file2.slice(projectDir.length + 1));
183169
183284
  }
183170
183285
  return;
183171
183286
  }
@@ -183175,10 +183290,10 @@ async function prepareRunDir(args) {
183175
183290
  const { files, projectDir, depsRoot, runRoot } = args;
183176
183291
  const fs4 = args.fs ?? makeDefaultFs();
183177
183292
  const runId = buildRunId(projectDir, files);
183178
- const runDir = join32(runRoot, runId);
183293
+ const runDir = join33(runRoot, runId);
183179
183294
  await fs4.rm(runDir, { recursive: true, force: true });
183180
183295
  await fs4.mkdir(runDir, { recursive: true });
183181
- const execDir = join32(runDir, "exec");
183296
+ const execDir = join33(runDir, "exec");
183182
183297
  await fs4.mkdir(execDir, { recursive: true });
183183
183298
  await populateInnerHop({ depsRoot, execDir, fs: fs4 });
183184
183299
  const stagedFiles = await stageFlowFiles({ files, projectDir, execDir, fs: fs4 });
@@ -183209,17 +183324,17 @@ function buildRunId(projectDir, files) {
183209
183324
  }
183210
183325
 
183211
183326
  // src/shell/testkit.ts
183212
- import { join as join33 } from "node:path";
183327
+ import { join as join34 } from "node:path";
183213
183328
  function notAvailableLocally(name) {
183214
183329
  throw new Error(runnerMessages.notAvailableLocally(name));
183215
183330
  }
183216
183331
  async function loadSdkDeps(cwd) {
183217
- const testkitDir = join33(cwd, "node_modules", "@qawolf", "testkit");
183332
+ const testkitDir = join34(cwd, "node_modules", "@qawolf", "testkit");
183218
183333
  try {
183219
183334
  const clientMod = await importFromPath(resolveFromEnvDir(cwd, "@qawolf/testkit/client"));
183220
183335
  if (typeof clientMod.createTestkitClient !== "function")
183221
183336
  throw new Error("createTestkitClient is not a function");
183222
- const scopeMod = await importFromPath(join33(testkitDir, "dist", "clientScope.js"));
183337
+ const scopeMod = await importFromPath(join34(testkitDir, "dist", "clientScope.js"));
183223
183338
  if (typeof scopeMod.configureTestkitClient !== "function")
183224
183339
  throw new Error("configureTestkitClient is not a function");
183225
183340
  return {
@@ -183277,9 +183392,9 @@ async function registerLazyEmailsClient(getInbox, cwd, deps) {
183277
183392
  }
183278
183393
 
183279
183394
  // src/commands/flows/envVarDeps.ts
183280
- import { join as join34 } from "node:path";
183395
+ import { join as join35 } from "node:path";
183281
183396
  function envFilePath(envDir) {
183282
- return join34(envDir, ".env");
183397
+ return join35(envDir, ".env");
183283
183398
  }
183284
183399
  async function readEnv(envDir, fs4) {
183285
183400
  try {
@@ -183383,6 +183498,9 @@ async function createFlowRuntimeDeps({
183383
183498
  };
183384
183499
  }
183385
183500
 
183501
+ // src/commands/flows/runStagedFlows.ts
183502
+ import { join as join39 } from "node:path";
183503
+
183386
183504
  // src/shell/appium/createAppiumServer.ts
183387
183505
  import { existsSync as existsSync2 } from "node:fs";
183388
183506
 
@@ -183746,12 +183864,12 @@ Install Android Studio and open Tools > SDK Manager.`);
183746
183864
  }
183747
183865
 
183748
183866
  // src/shell/manifest/lookup.ts
183749
- import { basename as basename5, dirname as dirname11, relative as relative6 } from "node:path";
183867
+ import { basename as basename5, dirname as dirname12, relative as relative6 } from "node:path";
183750
183868
  async function findFlowStamp(flowAbsPath) {
183751
183869
  let envDir;
183752
- let current = dirname11(flowAbsPath);
183753
- while (current !== dirname11(current)) {
183754
- const parent2 = dirname11(current);
183870
+ let current = dirname12(flowAbsPath);
183871
+ while (current !== dirname12(current)) {
183872
+ const parent2 = dirname12(current);
183755
183873
  if (basename5(parent2) === ".qawolf") {
183756
183874
  envDir = current;
183757
183875
  break;
@@ -184683,14 +184801,14 @@ async function runWebFlow({
184683
184801
 
184684
184802
  // src/shell/embeddedWorkerCli.ts
184685
184803
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
184686
- import { basename as basename6, join as join35 } from "node:path";
184804
+ import { basename as basename6, join as join36 } from "node:path";
184687
184805
  var embeddedCliPathEnv = "QAWOLF_EMBEDDED_CLI_PATH";
184688
184806
  function extractEmbeddedWorkerCli() {
184689
184807
  const assetPath = process.env[embeddedCliPathEnv];
184690
184808
  if (assetPath === undefined || assetPath === "")
184691
184809
  return;
184692
- const dir = join35(getDataDir(), "worker");
184693
- const dest = join35(dir, basename6(assetPath));
184810
+ const dir = join36(getDataDir(), "worker");
184811
+ const dest = join36(dir, basename6(assetPath));
184694
184812
  if (existsSync3(dest))
184695
184813
  return dest;
184696
184814
  mkdirSync3(dir, { recursive: true });
@@ -184845,7 +184963,7 @@ function makePooledDispatch(resolvedDir) {
184845
184963
  }
184846
184964
 
184847
184965
  // src/commands/flows/buildRunReporter.ts
184848
- import { dirname as dirname12 } from "node:path";
184966
+ import { dirname as dirname13 } from "node:path";
184849
184967
 
184850
184968
  // src/shell/reporter/createConsoleReporter.ts
184851
184969
  import { styleText as styleText3 } from "node:util";
@@ -184892,9 +185010,9 @@ function createConsoleReporter(deps) {
184892
185010
  deps.stderr.write(`${formatted}
184893
185011
 
184894
185012
  `);
184895
- const missingPackage = extractMissingPackage(errStr);
184896
- if (missingPackage !== undefined) {
184897
- deps.stderr.write(`${styleText3("yellow", `${indent}${runnerMessages.moduleNotFoundHint(missingPackage, deps.projectDir)}`)}
185013
+ const hint = flowFailureHint(errStr, deps.projectDir);
185014
+ if (hint !== undefined) {
185015
+ deps.stderr.write(`${styleText3("yellow", `${indent}${hint}`)}
184898
185016
 
184899
185017
  `);
184900
185018
  }
@@ -184928,7 +185046,7 @@ ${styleText3("dim", "─".repeat(width))}
184928
185046
  };
184929
185047
  }
184930
185048
  // src/shell/reporter/createJUnitReporter.ts
184931
- import { join as join36 } from "node:path";
185049
+ import { join as join37 } from "node:path";
184932
185050
 
184933
185051
  // src/shell/reporter/junitXml.ts
184934
185052
  function escapeXml(str) {
@@ -184967,7 +185085,7 @@ ${suites}`;
184967
185085
 
184968
185086
  // src/shell/reporter/createJUnitReporter.ts
184969
185087
  function resolveJUnitOutputPath(junit, outputDir) {
184970
- return typeof junit === "string" && junit.trim() !== "" ? junit : join36(outputDir, "junit-report.xml");
185088
+ return typeof junit === "string" && junit.trim() !== "" ? junit : join37(outputDir, "junit-report.xml");
184971
185089
  }
184972
185090
  function createJUnitReporter(deps) {
184973
185091
  const flows = [];
@@ -185006,7 +185124,7 @@ function buildRunReporter(flags, deps) {
185006
185124
  const junit = createJUnitReporter({
185007
185125
  outputPath: resolveJUnitOutputPath(flags.junit, flags.outputDir),
185008
185126
  writeFile: (path9, content) => {
185009
- deps.fs.mkdirSync(dirname12(path9), { recursive: true });
185127
+ deps.fs.mkdirSync(dirname13(path9), { recursive: true });
185010
185128
  deps.fs.writeFileSync(path9, content);
185011
185129
  },
185012
185130
  stderr
@@ -185045,6 +185163,7 @@ function buildFlowsRunDeps(args) {
185045
185163
  findFlowStamp,
185046
185164
  warn: (message) => ctx.ui.warn(message),
185047
185165
  logger: ctx.log("runner"),
185166
+ ...projectDir !== undefined ? { projectDir } : {},
185048
185167
  reporter: buildRunReporter(flags, {
185049
185168
  fs: ctx.fs,
185050
185169
  stdout: { write: (text3) => ctx.ui.write(text3) },
@@ -185057,11 +185176,11 @@ function buildFlowsRunDeps(args) {
185057
185176
 
185058
185177
  // src/commands/flows/loadEnvFile.ts
185059
185178
  import { readFile } from "node:fs/promises";
185060
- import { join as join37 } from "node:path";
185179
+ import { join as join38 } from "node:path";
185061
185180
  async function loadEnvFile(envDir) {
185062
185181
  let content;
185063
185182
  try {
185064
- content = await readFile(join37(envDir, ".env"), "utf8");
185183
+ content = await readFile(join38(envDir, ".env"), "utf8");
185065
185184
  } catch (err) {
185066
185185
  if (isNoEntError(err))
185067
185186
  return;
@@ -185106,6 +185225,14 @@ async function runStagedFlows(args) {
185106
185225
  for (const candidate of staged.outerHop.rejected) {
185107
185226
  log31.debug(runnerMessages.outerHopCandidateRejected(candidate.dir, candidate.missing));
185108
185227
  }
185228
+ const ownModules = projectDir === undefined ? undefined : join39(projectDir, "node_modules");
185229
+ const own = staged.outerHop.rejected.find((c3) => c3.dir === ownModules);
185230
+ if (own !== undefined) {
185231
+ ctx.ui.warn(runnerMessages.outerHopFallbackNotice(own.dir, own.missing));
185232
+ }
185233
+ if (staged.outerHop.carriedOver.length > 0) {
185234
+ ctx.ui.warn(runnerMessages.outerHopCarriedOver(staged.outerHop.carriedOver));
185235
+ }
185109
185236
  }
185110
185237
  const unregisterCleanup = ctx.signals.register(staged.cleanup);
185111
185238
  try {
@@ -185162,7 +185289,7 @@ async function handleFlowsRun(ctx, pattern, flags, deps) {
185162
185289
  }
185163
185290
 
185164
185291
  // src/commands/flows/hybridRunDefaults.ts
185165
- import { join as join38, resolve as resolve10 } from "node:path";
185292
+ import { join as join40, resolve as resolve10 } from "node:path";
185166
185293
  function makeDefaultHybridDeps(fs9) {
185167
185294
  return {
185168
185295
  expandPatterns: (patterns, cwd, logger) => expandPatterns(patterns, cwd, logger, fs9),
@@ -185181,7 +185308,7 @@ async function handleHybridFlowsRun(ctx, pattern, flags, deps) {
185181
185308
  if (validation !== "ok") {
185182
185309
  return { error: validation.error, exitCode: 2 };
185183
185310
  }
185184
- const envDir = resolve10(join38(".qawolf", flags.env));
185311
+ const envDir = resolve10(join40(".qawolf", flags.env));
185185
185312
  const patternArgs = buildPatternArgs(pattern);
185186
185313
  const globFlows = () => resolvedDeps.expandPatterns(patternArgs, envDir, ctx.log("flows"));
185187
185314
  let files = await globFlows();
@@ -185327,12 +185454,12 @@ function registerFlowsCommand(program2, signals) {
185327
185454
  }
185328
185455
 
185329
185456
  // src/domains/init/init.ts
185330
- import { dirname as dirname13, join as join40, relative as relative7 } from "node:path";
185457
+ import { dirname as dirname14, join as join42, relative as relative7 } from "node:path";
185331
185458
 
185332
185459
  // src/domains/init/ensurePackageJson.ts
185333
- import { join as join39 } from "node:path";
185460
+ import { join as join41 } from "node:path";
185334
185461
  async function ensurePackageJson(ctx, yes, deps) {
185335
- const pkgPath = join39(deps.cwd, "package.json");
185462
+ const pkgPath = join41(deps.cwd, "package.json");
185336
185463
  if (!await deps.fs.pathExists(pkgPath)) {
185337
185464
  const confirmed2 = await ctx.ui.confirm(initMessages.createPackageJsonPrompt, { yes });
185338
185465
  if (!confirmed2.ok || !confirmed2.value) {
@@ -185458,9 +185585,9 @@ function makeDefaultInitDeps() {
185458
185585
  async function handleInit(ctx, opts, deps = makeDefaultInitDeps()) {
185459
185586
  ctx.ui.gap();
185460
185587
  ctx.ui.intro(initMessages.title);
185461
- const configPath = join40(deps.cwd, "qawolf.config.ts");
185462
- const flowPath = join40(deps.cwd, "src", "flows", "example.flow.ts");
185463
- const gitignorePath = join40(deps.cwd, ".qawolf", ".gitignore");
185588
+ const configPath = join42(deps.cwd, "qawolf.config.ts");
185589
+ const flowPath = join42(deps.cwd, "src", "flows", "example.flow.ts");
185590
+ const gitignorePath = join42(deps.cwd, ".qawolf", ".gitignore");
185464
185591
  await writeWithPrompt(ctx, configPath, qawolfConfigTs, opts.yes, deps);
185465
185592
  await writeWithPrompt(ctx, flowPath, exampleFlowTs, opts.yes, deps);
185466
185593
  await writeWithPrompt(ctx, gitignorePath, qawolfGitignore, opts.yes, deps);
@@ -185479,7 +185606,7 @@ async function writeWithPrompt(ctx, filePath, content, yes, deps) {
185479
185606
  return;
185480
185607
  }
185481
185608
  }
185482
- await deps.fs.mkdir(dirname13(filePath), { recursive: true });
185609
+ await deps.fs.mkdir(dirname14(filePath), { recursive: true });
185483
185610
  await deps.fs.writeFile(filePath, content);
185484
185611
  ctx.ui.step(initMessages.createdFile(relPath));
185485
185612
  }
@@ -185498,7 +185625,7 @@ Examples:
185498
185625
  import { parseExecutionTarget as parseExecutionTarget4 } from "@qawolf/flow-targets";
185499
185626
 
185500
185627
  // src/domains/install/android/avd.ts
185501
- import { join as join41 } from "node:path";
185628
+ import { join as join43 } from "node:path";
185502
185629
  async function installAvds(ctx, specs, deps) {
185503
185630
  const sdkManagerPath = sdkManagerBin(deps.androidHome, deps.platform);
185504
185631
  const avdManagerPath = avdManagerBin(deps.androidHome, deps.platform);
@@ -185508,7 +185635,7 @@ async function installAvds(ctx, specs, deps) {
185508
185635
  if (versionResult.exitCode !== 0) {
185509
185636
  throw new Error(installMessages.android.sdkmanagerNotFound(sdkManagerPath));
185510
185637
  }
185511
- const licenseFile = join41(deps.androidHome, "licenses", "android-sdk-license");
185638
+ const licenseFile = join43(deps.androidHome, "licenses", "android-sdk-license");
185512
185639
  if (deps.checkExists(licenseFile)) {
185513
185640
  ctx.ui.info(installMessages.android.licensesAlreadyAccepted);
185514
185641
  } else {
@@ -185523,7 +185650,7 @@ async function installAvds(ctx, specs, deps) {
185523
185650
  for (const spec of specs) {
185524
185651
  if (installedImages.has(spec.systemImage))
185525
185652
  continue;
185526
- const systemImg = join41(deps.androidHome, ...spec.systemImage.split(";"), "system.img");
185653
+ const systemImg = join43(deps.androidHome, ...spec.systemImage.split(";"), "system.img");
185527
185654
  if (deps.checkExists(systemImg)) {
185528
185655
  ctx.ui.info(installMessages.android.systemImageAlreadyInstalled(spec.systemImage));
185529
185656
  installedImages.add(spec.systemImage);
@@ -186575,7 +186702,7 @@ async function handleRunnerScreenshot(ctx, options, deps) {
186575
186702
  }
186576
186703
 
186577
186704
  // src/shell/interactiveRunner/collectRunFiles.ts
186578
- import { join as join42 } from "node:path";
186705
+ import { join as join44 } from "node:path";
186579
186706
  async function collectRunFiles(options) {
186580
186707
  const paths = await glob(shippableRunFileExtensions.map((extension) => `**/*${extension}`), {
186581
186708
  cwd: options.cwd,
@@ -186585,7 +186712,7 @@ async function collectRunFiles(options) {
186585
186712
  const shippable = paths.filter(isShippableRunFilePath).sort();
186586
186713
  const files = {};
186587
186714
  const read = batchMap(shippable, async (path9) => ({
186588
- content: await options.fs.readFile(join42(options.cwd, path9)),
186715
+ content: await options.fs.readFile(join44(options.cwd, path9)),
186589
186716
  path: path9
186590
186717
  }), flowBatchSize);
186591
186718
  for await (const file2 of read)
@@ -186600,7 +186727,7 @@ function makeRunnerId() {
186600
186727
  }
186601
186728
 
186602
186729
  // src/shell/interactiveRunner/runnerStore.ts
186603
- import { join as join43 } from "node:path";
186730
+ import { join as join45 } from "node:path";
186604
186731
  var storeFileName = "runner.json";
186605
186732
  var storeSchema = exports_external.object({ defaultRunnerId: exports_external.string().optional() });
186606
186733
  function parseJson(text3) {
@@ -186611,8 +186738,8 @@ function parseJson(text3) {
186611
186738
  }
186612
186739
  }
186613
186740
  function makeRunnerStore(options) {
186614
- const directory = join43(options.cwd, qawolfDir);
186615
- const path9 = join43(directory, storeFileName);
186741
+ const directory = join45(options.cwd, qawolfDir);
186742
+ const path9 = join45(directory, storeFileName);
186616
186743
  let pendingWrites = 0;
186617
186744
  const nextPendingPath = () => `${path9}.${process.pid}.${++pendingWrites}.tmp`;
186618
186745
  return {
@@ -186639,7 +186766,7 @@ function makeRunnerStore(options) {
186639
186766
  }
186640
186767
 
186641
186768
  // src/shell/interactiveRunner/writeScreenshot.ts
186642
- import { dirname as dirname14 } from "node:path";
186769
+ import { dirname as dirname15 } from "node:path";
186643
186770
  var jpegStartOfImage = [255, 216, 255];
186644
186771
  async function writeScreenshot(options) {
186645
186772
  const bytes = Buffer.from(options.imageJpegBase64, "base64");
@@ -186647,7 +186774,7 @@ async function writeScreenshot(options) {
186647
186774
  return { ok: false, reason: "not-a-jpeg" };
186648
186775
  }
186649
186776
  try {
186650
- await options.fs.mkdir(dirname14(options.path), { recursive: true });
186777
+ await options.fs.mkdir(dirname15(options.path), { recursive: true });
186651
186778
  await options.fs.writeFile(options.path, bytes);
186652
186779
  } catch (error51) {
186653
186780
  return { detail: errorMessage(error51), ok: false, reason: "unwritable" };
@@ -187285,4 +187412,4 @@ createProgram({ signals }).parseAsync().catch(() => {
187285
187412
  process.exitCode = 1;
187286
187413
  }).finally(() => flushAndExit(typeof process.exitCode === "number" ? process.exitCode : 0));
187287
187414
 
187288
- //# debugId=229B48C8231EE6BC64756E2164756E21
187415
+ //# debugId=0187A36B3165B15F64756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qawolf/cli",
3
- "version": "1.11.0",
3
+ "version": "1.11.2",
4
4
  "description": "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
5
5
  "keywords": [
6
6
  "automation",
@@ -68,7 +68,6 @@
68
68
  "appium": "2.11.3",
69
69
  "commander": "14.0.3",
70
70
  "env-paths": "4.0.0",
71
- "expect-webdriverio": "5.6.5",
72
71
  "picomatch": "4.0.4",
73
72
  "pino": "10.3.1",
74
73
  "playwright": "1.62.0",
@@ -85,6 +84,7 @@
85
84
  "@types/bun": "1.3.14",
86
85
  "@types/picomatch": "4.0.3",
87
86
  "appium-uiautomator2-driver": "4.2.9",
87
+ "expect-webdriverio": "5.6.5",
88
88
  "knip": "6.16.1",
89
89
  "oxfmt": "0.54.0",
90
90
  "oxlint": "1.69.0",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: qawolf-cli
3
- description: Manage QA Wolf through the qawolf CLI. Use when asked which QA Wolf environment variables are available or to list, set, or delete them; manage environments, flows, runs, tags, or issues; authenticate; install; run or list flows; or drive a live cloud browser (launch a runner, screenshot it, click and type on it, read its recorder) from a shell.
3
+ description: Manage QA Wolf through the qawolf CLI. Use when asked to create, update, or list coverage requests, bug reports, or maintenance reports; start a run of flows or tags on the QA Wolf platform or read a run's results; list, set, or delete environment variables; manage environments, flows, or tags; request automation of draft flows; run or list flows locally; authenticate; install the local runtime; or drive a live cloud browser (launch a runner, screenshot it, click and type on it, read its recorder) from a shell.
4
4
  license: Apache-2.0
5
5
  compatibility: Requires the qawolf CLI on PATH. Install it from @qawolf/cli or use a standalone binary from GitHub Releases.
6
6
  ---