@qawolf/cli 1.7.1 → 1.7.3

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.
Files changed (2) hide show
  1. package/dist/cli.js +410 -272
  2. package/package.json +1 -2
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 join34(s3) {
8517
+ BufferList.prototype.join = function join35(s3) {
8518
8518
  if (this.length === 0)
8519
8519
  return "";
8520
8520
  var p3 = this.head;
@@ -23070,7 +23070,7 @@ var require_BufferList2 = __commonJS((exports, module) => {
23070
23070
  this.head = this.tail = null;
23071
23071
  this.length = 0;
23072
23072
  };
23073
- BufferList.prototype.join = function join34(s3) {
23073
+ BufferList.prototype.join = function join35(s3) {
23074
23074
  if (this.length === 0)
23075
23075
  return "";
23076
23076
  var p3 = this.head;
@@ -155283,7 +155283,8 @@ var doctorMessages = {
155283
155283
  playwright: {
155284
155284
  notFound: (path) => toolNotInstalled("Playwright", path),
155285
155285
  launchFailed: "Could not launch Playwright. Try reinstalling the qawolf CLI.",
155286
- versionUnparseable: "Could not parse playwright version output"
155286
+ versionUnparseable: "Could not parse playwright version output",
155287
+ versionMismatch: (installed, pinned) => `Playwright ${installed} is installed but the flow runtime requires ${pinned}; local runs will fail to launch browsers. Run \`qawolf install\` to repair the runtime dependencies.`
155287
155288
  },
155288
155289
  appium: {
155289
155290
  notFound: (path) => toolNotInstalled("Appium", path),
@@ -155313,6 +155314,8 @@ var environmentsMessages = {
155313
155314
  static: "Static environments",
155314
155315
  preview: "Preview (PR) environments"
155315
155316
  },
155317
+ resolvedAlias: (ref, id) => `Environment ${ref} resolved to ${id}.`,
155318
+ couldNotResolve: (ref, detail) => `Could not resolve environment ${ref}: ${detail} If ${ref} is an alias, note that aliases require a team API key.`,
155316
155319
  aborted: "Aborted; no environment selected.",
155317
155320
  tooManyPages: (pages) => `Stopped listing environments after ${pages} pages. Pass --env explicitly.`
155318
155321
  };
@@ -155330,6 +155333,9 @@ var flowsMessages = {
155330
155333
  remoteRequiresEnv: "--remote requires an environment. Pass --env <env> or set QAWOLF_ENVIRONMENT.",
155331
155334
  flagsRequireRemote: "--env and --include-drafts require --remote"
155332
155335
  },
155336
+ run: {
155337
+ requiresEnv: "An environment is required. Pass --env <env> or set QAWOLF_ENVIRONMENT."
155338
+ },
155333
155339
  pull: {
155334
155340
  requiresEnv: "An environment is required. Pass --env <env> or set QAWOLF_ENVIRONMENT.",
155335
155341
  downloadingBundle: "Downloading flows bundle",
@@ -155390,11 +155396,20 @@ var initMessages = {
155390
155396
  outro: "Run `qawolf auth login`, then `qawolf flows pull`, then `qawolf flows run`.",
155391
155397
  createPackageJsonPrompt: "Create package.json (required to run flows)?",
155392
155398
  skippedCreatingPackageJson: "Skipped package.json",
155393
- skippedAddingTestE2e: "Skipped adding test:e2e to package.json",
155399
+ skippedUpdatingPackageJson: "Skipped updating package.json",
155394
155400
  createdPackageJson: "Created package.json",
155395
- packageJsonInvalidJson: "package.json is not valid JSON — skipped adding `test:e2e`",
155396
- packageJsonHasTestE2e: "package.json already has `test:e2e` — skipped",
155397
- addTestE2ePrompt: "Add `test:e2e: qawolf flows run` to package.json?",
155401
+ packageJsonInvalidJson: "`package.json` contains invalid JSON. Correct the JSON syntax, then run `qawolf init` again.",
155402
+ packageJsonNotAnObject: "`package.json` must contain a JSON object. Update it, then run `qawolf init` again.",
155403
+ packageJsonMalformedSection: (field) => `The \`${field}\` field in \`package.json\` must be a JSON object. Update it, then run \`qawolf init\` again.`,
155404
+ packageJsonHasTestE2e: "package.json already has `test:e2e` — leaving it as is",
155405
+ packageJsonUpToDate: "package.json already configured — nothing to update",
155406
+ typeChanged: (from) => `Changed "type" from "${from}" to "module" — .js files in this package now load as ES modules.`,
155407
+ pkgChanges: {
155408
+ script: "add `test:e2e` script",
155409
+ type: 'set "type": "module"',
155410
+ flowsDep: "add @qawolf/flows dependency"
155411
+ },
155412
+ updatePackageJsonPrompt: (changes) => `Update package.json (${changes.join(", ")})?`,
155398
155413
  updatedPackageJson: "Updated package.json",
155399
155414
  overwritePrompt: (relPath) => `Overwrite ${relPath}?`,
155400
155415
  skippedFile: (relPath) => `Skipped ${relPath}`,
@@ -174611,7 +174626,7 @@ function startUpdateCheck(deps) {
174611
174626
  // package.json
174612
174627
  var package_default = {
174613
174628
  name: "@qawolf/cli",
174614
- version: "1.7.1",
174629
+ version: "1.7.3",
174615
174630
  description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
174616
174631
  keywords: [
174617
174632
  "automation",
@@ -174671,7 +174686,6 @@ var package_default = {
174671
174686
  "@clack/prompts": "1.5.1",
174672
174687
  "@napi-rs/keyring": "1.3.0",
174673
174688
  "@oxc-node/core": "0.1.0",
174674
- "@playwright/test": "1.62.0",
174675
174689
  "@qawolf/api-contracts": "0.22.0",
174676
174690
  "@qawolf/emails": "1.1.1",
174677
174691
  "@qawolf/flow-targets": "1.0.0",
@@ -174765,7 +174779,7 @@ function withContext(signals, fn) {
174765
174779
  try {
174766
174780
  const result = await fn(ctx);
174767
174781
  if (result !== undefined) {
174768
- ctx.ui.error(result.error);
174782
+ ctx.ui.error(result.error, result.errorBody);
174769
174783
  process.exitCode = result.exitCode ?? 1;
174770
174784
  }
174771
174785
  } catch (err) {
@@ -174798,7 +174812,7 @@ function withAuthContext(signals, fn, deps = {}) {
174798
174812
  apiKeySource: resolved.source
174799
174813
  });
174800
174814
  if (result !== undefined) {
174801
- ctx.ui.error(result.error);
174815
+ ctx.ui.error(result.error, result.errorBody);
174802
174816
  process.exitCode = result.exitCode ?? 1;
174803
174817
  }
174804
174818
  } catch (err) {
@@ -175042,9 +175056,6 @@ function nodeModulesBinCandidates(envDir, name, platform) {
175042
175056
  function appiumCliCandidates(envDir, platform) {
175043
175057
  return nodeModulesBinCandidates(envDir, "appium", platform);
175044
175058
  }
175045
- function playwrightCliCandidates(envDir, platform) {
175046
- return nodeModulesBinCandidates(envDir, "playwright", platform);
175047
- }
175048
175059
 
175049
175060
  // src/domains/doctor/checks/androidAppium.ts
175050
175061
  function firstLine(result) {
@@ -175407,19 +175418,48 @@ async function checkNpmRegistry(deps) {
175407
175418
  return { name: "npm-registry", status: "pass" };
175408
175419
  }
175409
175420
 
175421
+ // src/core/playwrightCli.ts
175422
+ import { join as join13 } from "node:path";
175423
+ function playwrightCliJsPath(envDir) {
175424
+ return join13(envDir, "node_modules", "playwright", "cli.js");
175425
+ }
175426
+ function playwrightCliInvocation(args) {
175427
+ return {
175428
+ cmd: args.execPath,
175429
+ args: [playwrightCliJsPath(args.envDir), ...args.cliArgs],
175430
+ env: { BUN_BE_BUN: "1" }
175431
+ };
175432
+ }
175433
+
175434
+ // src/generated/dependencyVersions.ts
175435
+ var flowsVersion = "0.1.4";
175436
+ var playwrightVersion = "1.62.0";
175437
+ var emailsVersion = "1.1.1";
175438
+ var testkitVersion = "1.1.1";
175439
+ var appiumVersion = "2.11.3";
175440
+ var appiumXcuitestDriverVersion = "9.10.5";
175441
+ var appiumUiautomator2DriverVersion = "3.7.0";
175442
+ var expectWebdriverioVersion = "5.6.5";
175443
+
175410
175444
  // src/domains/doctor/checks/playwright.ts
175411
175445
  async function checkPlaywright(deps) {
175412
- const candidates = deps.envDir ? playwrightCliCandidates(deps.envDir, deps.platform) : [];
175413
- const cliPath = candidates.find(deps.checkExists);
175414
- if (cliPath === undefined) {
175446
+ const { envDir } = deps;
175447
+ const cliJsPath = envDir === undefined ? undefined : playwrightCliJsPath(envDir);
175448
+ if (envDir === undefined || cliJsPath === undefined || !deps.checkExists(cliJsPath)) {
175415
175449
  return {
175416
175450
  name: "playwright",
175417
175451
  status: "fail",
175418
- detail: doctorMessages.playwright.notFound(candidates[0])
175452
+ detail: doctorMessages.playwright.notFound(cliJsPath)
175419
175453
  };
175420
175454
  }
175421
- const result = await deps.spawn(cliPath, ["--version"], {
175422
- platform: deps.platform
175455
+ const invocation = playwrightCliInvocation({
175456
+ envDir,
175457
+ execPath: deps.execPath,
175458
+ cliArgs: ["--version"]
175459
+ });
175460
+ const result = await deps.spawn(invocation.cmd, invocation.args, {
175461
+ platform: deps.platform,
175462
+ env: invocation.env
175423
175463
  });
175424
175464
  if (result.exitCode < 0) {
175425
175465
  return {
@@ -175441,6 +175481,14 @@ async function checkPlaywright(deps) {
175441
175481
  detail: doctorMessages.playwright.versionUnparseable
175442
175482
  };
175443
175483
  }
175484
+ if (version2 !== playwrightVersion) {
175485
+ return {
175486
+ name: "playwright",
175487
+ status: "fail",
175488
+ version: version2,
175489
+ detail: doctorMessages.playwright.versionMismatch(version2, playwrightVersion)
175490
+ };
175491
+ }
175444
175492
  return { name: "playwright", status: "pass", version: version2 };
175445
175493
  }
175446
175494
 
@@ -175461,6 +175509,7 @@ async function runChecks(deps) {
175461
175509
  }),
175462
175510
  checkPlaywright({
175463
175511
  spawn: deps.spawn,
175512
+ execPath: deps.execPath,
175464
175513
  envDir: deps.envDir,
175465
175514
  platform: deps.platform,
175466
175515
  checkExists: deps.checkExists
@@ -175520,11 +175569,11 @@ function renderResults(ui, results) {
175520
175569
  }
175521
175570
 
175522
175571
  // src/domains/flows/ensureDeps.ts
175523
- import { dirname as dirname4, join as join13 } from "node:path";
175572
+ import { dirname as dirname4, join as join14 } from "node:path";
175524
175573
  function findEnvDir(flowPath, fs2) {
175525
175574
  let dir = dirname4(flowPath);
175526
175575
  while (true) {
175527
- if (fs2.existsSync(join13(dir, "package.json")))
175576
+ if (fs2.existsSync(join14(dir, "package.json")))
175528
175577
  return dir;
175529
175578
  const parent = dirname4(dir);
175530
175579
  if (parent === dir)
@@ -175550,7 +175599,7 @@ function resolveProjectDirSafe(files, fs2) {
175550
175599
  }
175551
175600
 
175552
175601
  // src/domains/flows/expand.ts
175553
- import { join as join14, resolve as resolve3 } from "node:path";
175602
+ import { join as join15, resolve as resolve3 } from "node:path";
175554
175603
 
175555
175604
  // node_modules/tinyglobby/dist/index.mjs
175556
175605
  import { readdir, readdirSync, realpath, realpathSync, stat, statSync } from "fs";
@@ -176441,11 +176490,11 @@ function makePeekFlowMeta(fs2) {
176441
176490
  };
176442
176491
  }
176443
176492
  async function resolveGlobRoots(cwd, fs2) {
176444
- const qawolfPath = join14(cwd, ".qawolf");
176493
+ const qawolfPath = join15(cwd, ".qawolf");
176445
176494
  let envDirs = [];
176446
176495
  try {
176447
176496
  const entries = await fs2.readdirWithTypes(qawolfPath);
176448
- envDirs = entries.filter((e) => e.isDirectory()).map((e) => join14(qawolfPath, e.name));
176497
+ envDirs = entries.filter((e) => e.isDirectory()).map((e) => join15(qawolfPath, e.name));
176449
176498
  } catch {}
176450
176499
  return [cwd, ...envDirs];
176451
176500
  }
@@ -176470,21 +176519,11 @@ async function expandPatterns(patterns, cwd, logger, fs2) {
176470
176519
  }
176471
176520
 
176472
176521
  // src/domains/runtimeEnv/clearRuntimeEnv.ts
176473
- import { join as join16 } from "node:path";
176522
+ import { join as join17 } from "node:path";
176474
176523
 
176475
176524
  // src/domains/runtimeEnv/managedEnvDir.ts
176476
176525
  import { createHash } from "node:crypto";
176477
- import { join as join15, resolve as resolve4 } from "node:path";
176478
-
176479
- // src/generated/dependencyVersions.ts
176480
- var flowsVersion = "0.1.4";
176481
- var playwrightVersion = "1.62.0";
176482
- var emailsVersion = "1.1.1";
176483
- var testkitVersion = "1.1.1";
176484
- var appiumVersion = "2.11.3";
176485
- var appiumXcuitestDriverVersion = "9.10.5";
176486
- var appiumUiautomator2DriverVersion = "3.7.0";
176487
- var expectWebdriverioVersion = "5.6.5";
176526
+ import { join as join16, resolve as resolve4 } from "node:path";
176488
176527
 
176489
176528
  // src/domains/runtimeEnv/pinnedPackages.ts
176490
176529
  var pinnedPackages = [
@@ -176517,19 +176556,19 @@ function managedEnvBaseDir() {
176517
176556
  const override = process.env["QAWOLF_RUNTIME_DIR"]?.trim();
176518
176557
  if (override)
176519
176558
  return resolve4(override);
176520
- return join15(getDataDir(), "runtime");
176559
+ return join16(getDataDir(), "runtime");
176521
176560
  }
176522
176561
  function runStagingRoot() {
176523
176562
  return `${managedEnvBaseDir()}-runs`;
176524
176563
  }
176525
176564
  function managedEnvDir() {
176526
- return join15(managedEnvBaseDir(), managedEnvHash());
176565
+ return join16(managedEnvBaseDir(), managedEnvHash());
176527
176566
  }
176528
176567
  async function scaffoldManagedEnv(dir, fs2) {
176529
176568
  await fs2.mkdir(dir, { recursive: true });
176530
176569
  const dependencies = Object.fromEntries(pinnedPackages.map(({ name, version: version2 }) => [name, version2]));
176531
- await fs2.writeFile(join15(dir, "package.json"), JSON.stringify({ name: "qawolf-runtime", private: true, dependencies }, undefined, 2));
176532
- await fs2.writeFile(join15(dir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
176570
+ await fs2.writeFile(join16(dir, "package.json"), JSON.stringify({ name: "qawolf-runtime", private: true, dependencies }, undefined, 2));
176571
+ await fs2.writeFile(join16(dir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
176533
176572
  `);
176534
176573
  }
176535
176574
 
@@ -176551,14 +176590,14 @@ async function isManagedRuntimeBase(dir, fs2) {
176551
176590
  for (const entry of entries) {
176552
176591
  if (!entry.isDirectory())
176553
176592
  return false;
176554
- if (!await hasRuntimeSentinel(join16(dir, entry.name), fs2))
176593
+ if (!await hasRuntimeSentinel(join17(dir, entry.name), fs2))
176555
176594
  return false;
176556
176595
  }
176557
176596
  return true;
176558
176597
  }
176559
176598
  async function hasRuntimeSentinel(hashDir, fs2) {
176560
176599
  try {
176561
- const pkg = JSON.parse(await fs2.readFile(join16(hashDir, "package.json")));
176600
+ const pkg = JSON.parse(await fs2.readFile(join17(hashDir, "package.json")));
176562
176601
  return pkg.name === runtimeSentinel;
176563
176602
  } catch {
176564
176603
  return false;
@@ -176632,10 +176671,10 @@ function spawnNpmInstall(cwd, spawn2 = spawn) {
176632
176671
  }
176633
176672
 
176634
176673
  // src/domains/runtimeEnv/resolvePinned.ts
176635
- import { join as join17 } from "node:path";
176674
+ import { join as join18 } from "node:path";
176636
176675
  function readInstalledVersion(dir, pkgName, fs2) {
176637
176676
  try {
176638
- const pkgPath = join17(dir, "node_modules", ...pkgName.split("/"), "package.json");
176677
+ const pkgPath = join18(dir, "node_modules", ...pkgName.split("/"), "package.json");
176639
176678
  const raw = JSON.parse(fs2.readFileSync(pkgPath));
176640
176679
  const { version: version2 } = raw;
176641
176680
  return typeof version2 === "string" ? version2 : undefined;
@@ -176644,8 +176683,18 @@ function readInstalledVersion(dir, pkgName, fs2) {
176644
176683
  }
176645
176684
  }
176646
176685
  var shims = [
176647
- { name: "playwright", candidates: playwrightCliCandidates },
176648
- { name: "appium", candidates: appiumCliCandidates }
176686
+ {
176687
+ name: "playwright",
176688
+ display: "node_modules/playwright/cli.js",
176689
+ candidates: (dir, _platform) => [
176690
+ playwrightCliJsPath(dir)
176691
+ ]
176692
+ },
176693
+ {
176694
+ name: "appium",
176695
+ display: "node_modules/.bin/appium",
176696
+ candidates: appiumCliCandidates
176697
+ }
176649
176698
  ];
176650
176699
  function pinnedResolutionFailures(dir, fs2, platform) {
176651
176700
  const failures = [];
@@ -176655,16 +176704,16 @@ function pinnedResolutionFailures(dir, fs2, platform) {
176655
176704
  failures.push({ kind: "package", name, pinned: version2, installed });
176656
176705
  }
176657
176706
  }
176658
- for (const { name, candidates } of shims) {
176707
+ for (const { name, display, candidates } of shims) {
176659
176708
  if (!candidates(dir, platform).some((p2) => fs2.existsSync(p2))) {
176660
- failures.push({ kind: "shim", name });
176709
+ failures.push({ kind: "shim", name, display });
176661
176710
  }
176662
176711
  }
176663
176712
  return failures;
176664
176713
  }
176665
176714
  function describePinnedFailure(failure) {
176666
176715
  if (failure.kind === "shim") {
176667
- return `node_modules/.bin/${failure.name} (missing)`;
176716
+ return `${failure.display} (missing)`;
176668
176717
  }
176669
176718
  return failure.installed === undefined ? `${failure.name} (missing, pinned ${failure.pinned})` : `${failure.name} ${failure.installed} (pinned ${failure.pinned})`;
176670
176719
  }
@@ -176673,10 +176722,10 @@ function allPinnedResolved(dir, fs2, platform) {
176673
176722
  }
176674
176723
 
176675
176724
  // src/domains/runtimeEnv/shimDeps.ts
176676
- import { join as join19 } from "node:path";
176725
+ import { join as join20 } from "node:path";
176677
176726
 
176678
176727
  // src/shell/resolveExport.ts
176679
- import { join as join18 } from "node:path";
176728
+ import { join as join19 } from "node:path";
176680
176729
  function pickPath(entry, preference) {
176681
176730
  if (typeof entry === "string")
176682
176731
  return entry;
@@ -176708,28 +176757,28 @@ function resolveFromEnvDir(envDir, specifier, preference = "esm", fs2 = makeDefa
176708
176757
  const pkgName = parts.slice(0, pkgPartCount).join("/");
176709
176758
  const rest = parts.slice(pkgPartCount);
176710
176759
  const subpath = rest.length > 0 ? "./" + rest.join("/") : ".";
176711
- const pkgDir = join18(envDir, "node_modules", ...pkgName.split("/"));
176760
+ const pkgDir = join19(envDir, "node_modules", ...pkgName.split("/"));
176712
176761
  let pkg;
176713
176762
  try {
176714
- pkg = JSON.parse(fs2.readFileSync(join18(pkgDir, "package.json")));
176763
+ pkg = JSON.parse(fs2.readFileSync(join19(pkgDir, "package.json")));
176715
176764
  } catch (err) {
176716
176765
  if (isNoEntError(err))
176717
- throw new Error(`Package '${pkgName}' not found in ${join18(envDir, "node_modules")}`, { cause: err });
176766
+ throw new Error(`Package '${pkgName}' not found in ${join19(envDir, "node_modules")}`, { cause: err });
176718
176767
  throw err;
176719
176768
  }
176720
176769
  const exportsMap = normalizeExports(pkg.exports);
176721
176770
  const entry = exportsMap?.[subpath];
176722
176771
  const relative3 = pickPath(entry, preference) ?? (subpath === "." ? pkg.module ?? pkg.main : undefined);
176723
176772
  if (!relative3) {
176724
- throw new Error(`No entry for "${subpath}" in exports of ${join18(pkgDir, "package.json")}`);
176773
+ throw new Error(`No entry for "${subpath}" in exports of ${join19(pkgDir, "package.json")}`);
176725
176774
  }
176726
- return join18(pkgDir, relative3);
176775
+ return join19(pkgDir, relative3);
176727
176776
  }
176728
176777
 
176729
176778
  // src/domains/runtimeEnv/shimDeps.ts
176730
176779
  function readShimMarker(shimDir, fs2) {
176731
176780
  try {
176732
- const pkg = JSON.parse(fs2.readFileSync(join19(shimDir, "package.json")));
176781
+ const pkg = JSON.parse(fs2.readFileSync(join20(shimDir, "package.json")));
176733
176782
  if (pkg._qawolf_format) {
176734
176783
  return {
176735
176784
  _qawolf_version: pkg._qawolf_version ?? "",
@@ -176745,11 +176794,11 @@ function resolveBunBuild(bunBuild) {
176745
176794
  return bunBuild === false ? undefined : { build: bunBuild };
176746
176795
  }
176747
176796
  async function removeStaleShims(flowsDir, flowsDeps, fs2) {
176748
- const shimsDir = join19(flowsDir, "node_modules");
176797
+ const shimsDir = join20(flowsDir, "node_modules");
176749
176798
  if (!fs2.existsSync(shimsDir))
176750
176799
  return;
176751
176800
  for (const dep of flowsDeps) {
176752
- const shimDepDir = join19(shimsDir, ...dep.split("/"));
176801
+ const shimDepDir = join20(shimsDir, ...dep.split("/"));
176753
176802
  if (readShimMarker(shimDepDir, fs2)) {
176754
176803
  await fs2.rm(shimDepDir, { recursive: true, force: true });
176755
176804
  }
@@ -176758,17 +176807,17 @@ async function removeStaleShims(flowsDir, flowsDeps, fs2) {
176758
176807
  async function buildDepShim(args) {
176759
176808
  const { envDir, flowsDir, dep, fs: fs2, build: build2 } = args;
176760
176809
  const depParts = dep.split("/");
176761
- const depDir = join19(envDir, "node_modules", ...depParts);
176810
+ const depDir = join20(envDir, "node_modules", ...depParts);
176762
176811
  if (!fs2.existsSync(depDir))
176763
176812
  return;
176764
176813
  let depVersion;
176765
176814
  try {
176766
- const pkg = JSON.parse(fs2.readFileSync(join19(depDir, "package.json")));
176815
+ const pkg = JSON.parse(fs2.readFileSync(join20(depDir, "package.json")));
176767
176816
  depVersion = pkg.version ?? "unknown";
176768
176817
  } catch {
176769
176818
  depVersion = "unknown";
176770
176819
  }
176771
- const shimDir = join19(flowsDir, "node_modules", ...depParts);
176820
+ const shimDir = join20(flowsDir, "node_modules", ...depParts);
176772
176821
  if (fs2.existsSync(shimDir)) {
176773
176822
  const marker = readShimMarker(shimDir, fs2);
176774
176823
  if (!marker)
@@ -176795,20 +176844,20 @@ async function buildDepShim(args) {
176795
176844
  }
176796
176845
  const shimCode = await output.text();
176797
176846
  await fs2.mkdir(shimDir, { recursive: true });
176798
- await fs2.writeFile(join19(shimDir, "package.json"), JSON.stringify({
176847
+ await fs2.writeFile(join20(shimDir, "package.json"), JSON.stringify({
176799
176848
  name: dep,
176800
176849
  _qawolf_version: depVersion,
176801
176850
  _qawolf_format: "bun-build-v1"
176802
176851
  }));
176803
- await fs2.writeFile(join19(shimDir, "index.js"), shimCode);
176852
+ await fs2.writeFile(join20(shimDir, "index.js"), shimCode);
176804
176853
  }
176805
176854
  async function shimFlowsDeps(envDir, fs2 = makeDefaultFs(), bunBuild) {
176806
- const flowsDir = join19(envDir, "node_modules", "@qawolf", "flows");
176855
+ const flowsDir = join20(envDir, "node_modules", "@qawolf", "flows");
176807
176856
  if (!fs2.existsSync(flowsDir))
176808
176857
  return;
176809
176858
  let flowsDeps;
176810
176859
  try {
176811
- const flowsPkg = JSON.parse(fs2.readFileSync(join19(flowsDir, "package.json")));
176860
+ const flowsPkg = JSON.parse(fs2.readFileSync(join20(flowsDir, "package.json")));
176812
176861
  flowsDeps = Object.keys(flowsPkg.dependencies ?? {});
176813
176862
  } catch {
176814
176863
  return;
@@ -176932,6 +176981,7 @@ async function handleDoctor(ctx, opts) {
176932
176981
  apiKey: resolved?.key,
176933
176982
  fetch: globalThis.fetch,
176934
176983
  spawn: defaultSpawn,
176984
+ execPath: process.execPath,
176935
176985
  apiBaseUrl: ctx.apiBaseUrl,
176936
176986
  enginesNode: package_default.engines.node,
176937
176987
  processVersion: process.version,
@@ -177074,11 +177124,11 @@ async function flowsListRemote(ctx, pattern, options) {
177074
177124
  }
177075
177125
 
177076
177126
  // src/domains/flows/pull/handler.ts
177077
- import { dirname as dirname7, join as join24, resolve as resolve8 } from "node:path";
177127
+ import { dirname as dirname7, join as join25, resolve as resolve8 } from "node:path";
177078
177128
 
177079
177129
  // src/shell/manifest/io.ts
177080
177130
  import { createHash as createHash2 } from "node:crypto";
177081
- import { join as join20 } from "node:path";
177131
+ import { join as join21 } from "node:path";
177082
177132
  var manifestFilename = ".manifest.json";
177083
177133
  var flowEntrySchema = exports_external.object({
177084
177134
  path: exports_external.string(),
@@ -177097,7 +177147,7 @@ var manifestSchema = exports_external.object({
177097
177147
  async function readManifest(envDir, fs2 = makeDefaultFs()) {
177098
177148
  let raw;
177099
177149
  try {
177100
- raw = await fs2.readFile(join20(envDir, manifestFilename));
177150
+ raw = await fs2.readFile(join21(envDir, manifestFilename));
177101
177151
  } catch (err) {
177102
177152
  if (isNoEntError(err))
177103
177153
  return "missing";
@@ -177125,7 +177175,7 @@ async function readManifest(envDir, fs2 = makeDefaultFs()) {
177125
177175
  }
177126
177176
  async function writeManifest(envDir, manifest, fs2 = makeDefaultFs()) {
177127
177177
  const body = JSON.stringify(manifest, undefined, 2);
177128
- await fs2.writeFile(join20(envDir, manifestFilename), `${body}
177178
+ await fs2.writeFile(join21(envDir, manifestFilename), `${body}
177129
177179
  `);
177130
177180
  }
177131
177181
  function hashFile(absPath, fs2 = makeDefaultFs()) {
@@ -177260,7 +177310,7 @@ async function checkSafety(args) {
177260
177310
  }
177261
177311
 
177262
177312
  // src/domains/flows/pull/bundle.ts
177263
- import { join as join21, relative as relative4 } from "node:path";
177313
+ import { join as join22, relative as relative4 } from "node:path";
177264
177314
  async function flattenSingleWrapper(dir, fs2 = makeDefaultFs()) {
177265
177315
  const entries = await fs2.readdir(dir);
177266
177316
  if (entries.length !== 1)
@@ -177268,12 +177318,12 @@ async function flattenSingleWrapper(dir, fs2 = makeDefaultFs()) {
177268
177318
  const innerName = entries[0];
177269
177319
  if (!innerName)
177270
177320
  return;
177271
- const inner = join21(dir, innerName);
177321
+ const inner = join22(dir, innerName);
177272
177322
  const innerStat = await fs2.stat(inner);
177273
177323
  if (!innerStat.isDirectory())
177274
177324
  return;
177275
177325
  for (const e of await fs2.readdir(inner)) {
177276
- await fs2.rename(join21(inner, e), join21(dir, e));
177326
+ await fs2.rename(join22(inner, e), join22(dir, e));
177277
177327
  }
177278
177328
  const leftovers = await fs2.readdir(inner);
177279
177329
  if (leftovers.length > 0) {
@@ -177292,7 +177342,7 @@ async function buildManifest(args, fs2 = makeDefaultFs()) {
177292
177342
  const flowPaths = await walkForFlows(args.bundleDir, fs2);
177293
177343
  const flows = await Promise.all(flowPaths.map(async (rel) => ({
177294
177344
  path: rel,
177295
- contentHash: await hashFile(join21(args.bundleDir, rel), fs2)
177345
+ contentHash: await hashFile(join22(args.bundleDir, rel), fs2)
177296
177346
  })));
177297
177347
  return {
177298
177348
  envId: args.envId,
@@ -177313,7 +177363,7 @@ async function walkForFlows(root, fs2) {
177313
177363
  async function walk(current, root, out, fs2) {
177314
177364
  const entries = await fs2.readdirWithTypes(current);
177315
177365
  for (const e of entries) {
177316
- const abs = join21(current, e.name);
177366
+ const abs = join22(current, e.name);
177317
177367
  if (e.isDirectory()) {
177318
177368
  await walk(abs, root, out, fs2);
177319
177369
  } else if (e.isFile() && flowExtensions.some((ext) => e.name.endsWith(ext))) {
@@ -177326,11 +177376,11 @@ async function sampleQawolfCommittedAt(bundleDir, fs2 = makeDefaultFs()) {
177326
177376
  const sample = flowPaths[0];
177327
177377
  if (!sample)
177328
177378
  return;
177329
- return (await fs2.stat(join21(bundleDir, sample))).mtime.toISOString();
177379
+ return (await fs2.stat(join22(bundleDir, sample))).mtime.toISOString();
177330
177380
  }
177331
177381
 
177332
177382
  // src/domains/flows/pull/applyTeamStorageRewrite.ts
177333
- import { join as join22, relative as relative5 } from "node:path";
177383
+ import { join as join23, relative as relative5 } from "node:path";
177334
177384
 
177335
177385
  // src/domains/flows/pull/rewriteTeamStorage.ts
177336
177386
  var runnerMountPrefix = "/home/wolf/team-storage/";
@@ -177366,7 +177416,7 @@ function isFlowFile(name) {
177366
177416
  async function walk2(dir, out, fs2) {
177367
177417
  const entries = await fs2.readdirWithTypes(dir);
177368
177418
  for (const e of entries) {
177369
- const abs = join22(dir, e.name);
177419
+ const abs = join23(dir, e.name);
177370
177420
  if (e.isDirectory()) {
177371
177421
  await walk2(abs, out, fs2);
177372
177422
  } else if (e.isFile() && isSourceFile(e.name)) {
@@ -177394,7 +177444,7 @@ async function applyTeamStorageRewrite(rootDir, fs2 = makeDefaultFs()) {
177394
177444
  }
177395
177445
 
177396
177446
  // src/domains/flows/pull/envVars.ts
177397
- import { join as join23 } from "node:path";
177447
+ import { join as join24 } from "node:path";
177398
177448
 
177399
177449
  // src/core/dotenv.ts
177400
177450
  var bareKeyPattern = /^[A-Za-z_][A-Za-z0-9_]*$/;
@@ -177459,7 +177509,7 @@ function unquote(escaped) {
177459
177509
  async function writeEnvFile(envDir, vars, fs2 = makeDefaultFs()) {
177460
177510
  if (Object.keys(vars).length === 0)
177461
177511
  return;
177462
- await fs2.writeFile(join23(envDir, ".env"), serializeDotenv(vars), {
177512
+ await fs2.writeFile(join24(envDir, ".env"), serializeDotenv(vars), {
177463
177513
  mode: 384
177464
177514
  });
177465
177515
  }
@@ -180832,8 +180882,8 @@ async function handleFlowsPull(ctx, opts, deps) {
180832
180882
  ctx.ui.error(validation.error);
180833
180883
  return { error: validation.error };
180834
180884
  }
180835
- const destAbs = resolve8(opts.out ?? join24(".qawolf", opts.env));
180836
- const assetsAbs = join24(dirname7(destAbs), "assets");
180885
+ const destAbs = resolve8(opts.out ?? join25(".qawolf", opts.env));
180886
+ const assetsAbs = join25(dirname7(destAbs), "assets");
180837
180887
  await resolvedDeps.fs.mkdir(assetsAbs, { recursive: true });
180838
180888
  const yes = opts.yes ?? false;
180839
180889
  let archive;
@@ -180899,7 +180949,7 @@ async function handleFlowsPull(ctx, opts, deps) {
180899
180949
  assetDownloadedCount: assetResult.downloadedCount,
180900
180950
  assetReusedCount: assetResult.reusedCount,
180901
180951
  assetSkippedCount: assetResult.skippedCount,
180902
- manifestPath: join24(result.envDir, manifestFilename)
180952
+ manifestPath: join25(result.envDir, manifestFilename)
180903
180953
  }, "");
180904
180954
  }
180905
180955
  } finally {
@@ -180908,20 +180958,9 @@ async function handleFlowsPull(ctx, opts, deps) {
180908
180958
  }
180909
180959
  }
180910
180960
 
180911
- // src/domains/environments/resolveEnvironment.ts
180961
+ // src/domains/environments/pickEnvironment.ts
180912
180962
  var findContract = publicContractsV1.environment.find;
180913
- async function resolveEnvironment(deps, opts) {
180914
- const explicit = opts.explicit?.trim();
180915
- if (explicit)
180916
- return { kind: "resolved", env: explicit };
180917
- const fromEnvVar = deps.env["QAWOLF_ENVIRONMENT"]?.trim();
180918
- if (fromEnvVar) {
180919
- deps.ui.info(environmentsMessages.usingFromEnvVar(fromEnvVar));
180920
- return { kind: "resolved", env: fromEnvVar };
180921
- }
180922
- if (deps.ui.mode !== "human") {
180923
- return { kind: "error", error: opts.requiredMessage };
180924
- }
180963
+ async function pickEnvironment(deps) {
180925
180964
  const fetched = await fetchAllEnvironments(deps.platformClient);
180926
180965
  if (!fetched.ok)
180927
180966
  return { kind: "error", error: fetched.error };
@@ -180994,6 +181033,39 @@ async function fetchAllEnvironments(platformClient) {
180994
181033
  return { ok: false, error: environmentsMessages.tooManyPages(maxPages) };
180995
181034
  }
180996
181035
 
181036
+ // src/domains/environments/resolveEnvironment.ts
181037
+ var findContract2 = publicContractsV1.environment.find;
181038
+ var getContract = publicContractsV1.environment.get;
181039
+ async function resolveEnvironment(deps, opts) {
181040
+ const explicit = opts.explicit?.trim();
181041
+ if (explicit)
181042
+ return resolveRef2(deps, explicit);
181043
+ const fromEnvVar = deps.env["QAWOLF_ENVIRONMENT"]?.trim();
181044
+ if (fromEnvVar) {
181045
+ deps.ui.info(environmentsMessages.usingFromEnvVar(fromEnvVar));
181046
+ return resolveRef2(deps, fromEnvVar);
181047
+ }
181048
+ if (deps.ui.mode !== "human") {
181049
+ return { kind: "error", error: opts.requiredMessage };
181050
+ }
181051
+ return pickEnvironment(deps);
181052
+ }
181053
+ async function resolveRef2(deps, ref) {
181054
+ const result = await deps.platformClient.callPublicApi(getContract, {
181055
+ environmentId: ref
181056
+ });
181057
+ if (!result.ok) {
181058
+ return {
181059
+ kind: "error",
181060
+ error: environmentsMessages.couldNotResolve(ref, result.error)
181061
+ };
181062
+ }
181063
+ if (result.value.id !== ref) {
181064
+ deps.ui.info(environmentsMessages.resolvedAlias(ref, result.value.id));
181065
+ }
181066
+ return { kind: "resolved", env: result.value.id };
181067
+ }
181068
+
180997
181069
  // src/commands/flows/withResolvedEnv.ts
180998
181070
  function withResolvedEnv(signals, args, fn2) {
180999
181071
  return (opts, command) => {
@@ -181063,6 +181135,72 @@ function buildPatternArgs(pattern) {
181063
181135
  return pattern ? [pattern] : [];
181064
181136
  }
181065
181137
 
181138
+ // src/shell/reporter/createCompositeReporter.ts
181139
+ function callEach(reporters, pick2, event) {
181140
+ for (const r2 of reporters) {
181141
+ try {
181142
+ pick2(r2)?.(event);
181143
+ } catch (err) {
181144
+ process.stderr.write(`[reporter error] ${String(err)}
181145
+ `);
181146
+ }
181147
+ }
181148
+ }
181149
+ function createCompositeReporter(reporters) {
181150
+ return {
181151
+ onFlowStart: (e) => callEach(reporters, (r2) => r2.onFlowStart, e),
181152
+ onFlowPass: (e) => callEach(reporters, (r2) => r2.onFlowPass, e),
181153
+ onFlowFail: (e) => callEach(reporters, (r2) => r2.onFlowFail, e),
181154
+ onTestStart: (e) => callEach(reporters, (r2) => r2.onTestStart, e),
181155
+ onTestResult: (e) => callEach(reporters, (r2) => r2.onTestResult, e),
181156
+ onScreenshot: (e) => callEach(reporters, (r2) => r2.onScreenshot, e),
181157
+ onRunComplete: (e) => callEach(reporters, (r2) => r2.onRunComplete, e)
181158
+ };
181159
+ }
181160
+
181161
+ // src/shell/reporter/formatErrorWithCause.ts
181162
+ function filterStack(stack) {
181163
+ const cwd = process.cwd();
181164
+ return stack.split(`
181165
+ `).filter((line) => {
181166
+ if (!/^\s+at /.test(line))
181167
+ return true;
181168
+ return !line.includes("node_modules") && !line.includes("dist/cli.js");
181169
+ }).map((line) => {
181170
+ if (!/^\s+at /.test(line))
181171
+ return line;
181172
+ return line.replace(`file://${cwd}/`, "").replace(`${cwd}/`, "");
181173
+ }).join(`
181174
+ `);
181175
+ }
181176
+ function renderCause(cause) {
181177
+ if (cause instanceof Error)
181178
+ return filterStack(cause.stack ?? cause.message);
181179
+ if (typeof cause === "object" && cause !== null) {
181180
+ const obj = cause;
181181
+ if (typeof obj["message"] === "string")
181182
+ return obj["message"];
181183
+ try {
181184
+ return JSON.stringify(cause);
181185
+ } catch {
181186
+ return String(cause);
181187
+ }
181188
+ }
181189
+ return String(cause);
181190
+ }
181191
+ function formatErrorWithCause(err) {
181192
+ const parts = [String(err)];
181193
+ let cause = err.cause;
181194
+ while (cause !== undefined && cause !== null) {
181195
+ parts.push(`Caused by: ${renderCause(cause)}`);
181196
+ if (!(cause instanceof Error))
181197
+ break;
181198
+ cause = cause.cause;
181199
+ }
181200
+ return parts.join(`
181201
+ `);
181202
+ }
181203
+
181066
181204
  // src/domains/runner/runAndroidFlowUtils.ts
181067
181205
  import { parseExecutionTarget as parseExecutionTarget3 } from "@qawolf/flow-targets";
181068
181206
 
@@ -181410,9 +181548,16 @@ async function flowsRun(ctx, files, flags, deps) {
181410
181548
  return { error: runnerMessages.androidWorkersUnsupported, exitCode: 2 };
181411
181549
  }
181412
181550
  const { webOptions, androidOptions } = buildRunOptions(flags);
181551
+ const failureDetails = [];
181552
+ const reporter = ctx.outputMode === "json" ? createCompositeReporter([
181553
+ deps.reporter,
181554
+ {
181555
+ onFlowFail: ({ err }) => failureDetails.push(formatErrorWithCause(err))
181556
+ }
181557
+ ]) : deps.reporter;
181413
181558
  const result = await executeFlows({
181414
181559
  ctx,
181415
- deps,
181560
+ deps: { ...deps, reporter },
181416
181561
  flags,
181417
181562
  flows,
181418
181563
  webFlows,
@@ -181440,7 +181585,13 @@ async function flowsRun(ctx, files, flags, deps) {
181440
181585
  deps.reporter.onRunComplete?.({ summary });
181441
181586
  ctx.ui.gap();
181442
181587
  if (counts.flowsFailed > 0) {
181443
- return { error: runnerMessages.flowsFailed(counts.flowsFailed) };
181588
+ const error51 = runnerMessages.flowsFailed(counts.flowsFailed);
181589
+ if (ctx.outputMode === "json" && failureDetails.length > 0) {
181590
+ return { error: error51, errorBody: failureDetails.join(`
181591
+
181592
+ `) };
181593
+ }
181594
+ return { error: error51 };
181444
181595
  }
181445
181596
  }
181446
181597
 
@@ -181491,15 +181642,15 @@ async function defaultRunWebFlowDeps(envDir = process.cwd(), signals) {
181491
181642
 
181492
181643
  // src/domains/runtimeEnv/prepareRunDir.ts
181493
181644
  import { createHash as createHash4 } from "node:crypto";
181494
- import { join as join31, resolve as resolve9 } from "node:path";
181645
+ import { join as join32, resolve as resolve9 } from "node:path";
181495
181646
 
181496
181647
  // src/domains/runtimeEnv/execSubpathImports.ts
181497
- import { join as join25 } from "node:path";
181648
+ import { join as join26 } from "node:path";
181498
181649
  var flowSubpathImports = { "#playwright": "playwright" };
181499
181650
  var packageJsonSchema = exports_external.looseObject({ imports: exports_external.record(exports_external.string(), exports_external.unknown()).catch({}) }).catch({ imports: {} });
181500
181651
  async function writeExecSubpathImports(args) {
181501
181652
  const { execDir, fs: fs3 } = args;
181502
- const pkgPath = join25(execDir, "package.json");
181653
+ const pkgPath = join26(execDir, "package.json");
181503
181654
  const pkg = packageJsonSchema.parse(await readPackageJson(pkgPath, fs3));
181504
181655
  const merged = { ...pkg, imports: { ...pkg.imports, ...flowSubpathImports } };
181505
181656
  await fs3.writeFile(pkgPath, JSON.stringify(merged, undefined, 2));
@@ -181521,10 +181672,10 @@ async function readPackageJson(pkgPath, fs3) {
181521
181672
  }
181522
181673
 
181523
181674
  // src/domains/runtimeEnv/innerHop.ts
181524
- import { join as join27 } from "node:path";
181675
+ import { join as join28 } from "node:path";
181525
181676
 
181526
181677
  // src/domains/runtimeEnv/linkPinnedPackages.ts
181527
- import { dirname as dirname8, join as join26 } from "node:path";
181678
+ import { dirname as dirname8, join as join27 } from "node:path";
181528
181679
 
181529
181680
  // src/domains/runtimeEnv/symlinkDir.ts
181530
181681
  import { symlink } from "node:fs/promises";
@@ -181536,16 +181687,16 @@ async function createDirSymlink(source, target) {
181536
181687
  // src/domains/runtimeEnv/linkPinnedPackages.ts
181537
181688
  async function linkPinnedPackages(args) {
181538
181689
  const { depsRoot, nodeModulesDir, fs: fs3 } = args;
181539
- const managedModules = join26(depsRoot, "node_modules");
181690
+ const managedModules = join27(depsRoot, "node_modules");
181540
181691
  await fs3.mkdir(nodeModulesDir, { recursive: true });
181541
181692
  for (const { name } of pinnedPackages) {
181542
181693
  const segments = name.split("/");
181543
- const target = join26(nodeModulesDir, ...segments);
181694
+ const target = join27(nodeModulesDir, ...segments);
181544
181695
  if (segments.length > 1) {
181545
181696
  await fs3.mkdir(dirname8(target), { recursive: true });
181546
181697
  }
181547
181698
  await fs3.rm(target, { recursive: true, force: true });
181548
- await createDirSymlink(join26(managedModules, ...segments), target);
181699
+ await createDirSymlink(join27(managedModules, ...segments), target);
181549
181700
  }
181550
181701
  }
181551
181702
 
@@ -181554,14 +181705,14 @@ async function populateInnerHop(args) {
181554
181705
  const { depsRoot, execDir, fs: fs3 } = args;
181555
181706
  await linkPinnedPackages({
181556
181707
  depsRoot,
181557
- nodeModulesDir: join27(execDir, "node_modules"),
181708
+ nodeModulesDir: join28(execDir, "node_modules"),
181558
181709
  fs: fs3
181559
181710
  });
181560
181711
  }
181561
181712
 
181562
181713
  // src/domains/runtimeEnv/outerHop.ts
181563
181714
  import { lstat } from "node:fs/promises";
181564
- import { dirname as dirname9, join as join28 } from "node:path";
181715
+ import { dirname as dirname9, join as join29 } from "node:path";
181565
181716
  var executorPackageNames = new Set(pinnedPackages.map((p3) => p3.name));
181566
181717
  async function populateOuterHop(args) {
181567
181718
  const { projectDir, runDir, fs: fs3 } = args;
@@ -181571,7 +181722,7 @@ async function populateOuterHop(args) {
181571
181722
  const depNames = Object.keys(installableDeps);
181572
181723
  const { satisfying, rejected } = await findSatisfyingNodeModulesDir(projectDir, depNames, fs3);
181573
181724
  if (satisfying !== undefined) {
181574
- await createDirSymlink(satisfying, join28(runDir, "node_modules"));
181725
+ await createDirSymlink(satisfying, join29(runDir, "node_modules"));
181575
181726
  return { mode: "symlink", nodeModulesDir: satisfying };
181576
181727
  }
181577
181728
  if (depNames.length === 0)
@@ -181580,7 +181731,7 @@ async function populateOuterHop(args) {
181580
181731
  await installOuterDeps(runDir, installableDeps, fs3, args.install ?? spawnNpmInstall);
181581
181732
  await linkPinnedPackages({
181582
181733
  depsRoot: args.depsRoot,
181583
- nodeModulesDir: join28(runDir, "node_modules"),
181734
+ nodeModulesDir: join29(runDir, "node_modules"),
181584
181735
  fs: fs3
181585
181736
  });
181586
181737
  return { mode: "install", depCount: depNames.length, rejected };
@@ -181589,10 +181740,10 @@ async function findSatisfyingNodeModulesDir(startDir, depNames, fs3) {
181589
181740
  const rejected = [];
181590
181741
  let dir = startDir;
181591
181742
  while (true) {
181592
- const candidate = join28(dir, "node_modules");
181743
+ const candidate = join29(dir, "node_modules");
181593
181744
  const stats = await lstatSafe(candidate);
181594
181745
  if (stats !== undefined && stats.isDirectory() && !stats.isSymbolicLink()) {
181595
- const missing = depNames.filter((name) => !fs3.existsSync(join28(candidate, name, "package.json")));
181746
+ const missing = depNames.filter((name) => !fs3.existsSync(join29(candidate, name, "package.json")));
181596
181747
  if (missing.length === 0)
181597
181748
  return { satisfying: candidate, rejected };
181598
181749
  rejected.push({ dir: candidate, missing });
@@ -181613,7 +181764,7 @@ async function lstatSafe(path3) {
181613
181764
  async function readInstallableDeps(projectDir, fs3) {
181614
181765
  let content;
181615
181766
  try {
181616
- content = await fs3.readFile(join28(projectDir, "package.json"));
181767
+ content = await fs3.readFile(join29(projectDir, "package.json"));
181617
181768
  } catch {
181618
181769
  return {};
181619
181770
  }
@@ -181631,8 +181782,8 @@ async function readInstallableDeps(projectDir, fs3) {
181631
181782
  return Object.fromEntries(Object.entries(rawDeps).filter(([name]) => !executorPackageNames.has(name)).filter((entry) => typeof entry[1] === "string"));
181632
181783
  }
181633
181784
  async function installOuterDeps(runDir, deps, fs3, install) {
181634
- await fs3.writeFile(join28(runDir, "package.json"), JSON.stringify({ name: "qawolf-run", private: true, dependencies: deps }, undefined, 2));
181635
- await fs3.writeFile(join28(runDir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
181785
+ await fs3.writeFile(join29(runDir, "package.json"), JSON.stringify({ name: "qawolf-run", private: true, dependencies: deps }, undefined, 2));
181786
+ await fs3.writeFile(join29(runDir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
181636
181787
  `);
181637
181788
  const result = await install(runDir);
181638
181789
  if (result.exitCode !== 0) {
@@ -181642,14 +181793,14 @@ async function installOuterDeps(runDir, deps, fs3, install) {
181642
181793
 
181643
181794
  // src/domains/runtimeEnv/stageFlowFiles.ts
181644
181795
  import { createHash as createHash3 } from "node:crypto";
181645
- import { basename as basename4, dirname as dirname10, join as join30, sep as sep4 } from "node:path";
181796
+ import { basename as basename4, dirname as dirname10, join as join31, sep as sep4 } from "node:path";
181646
181797
 
181647
181798
  // src/shell/copyDir.ts
181648
181799
  import { cp, readdir as readdir2 } from "node:fs/promises";
181649
- import { basename as basename3, join as join29 } from "node:path";
181800
+ import { basename as basename3, join as join30 } from "node:path";
181650
181801
  async function copyDirExcluding(source, destination, excludedNames) {
181651
181802
  const entries = await readdir2(source, { withFileTypes: true });
181652
- await Promise.all(entries.filter((entry) => !excludedNames.has(entry.name)).map((entry) => cp(join29(source, entry.name), join29(destination, entry.name), {
181803
+ await Promise.all(entries.filter((entry) => !excludedNames.has(entry.name)).map((entry) => cp(join30(source, entry.name), join30(destination, entry.name), {
181653
181804
  recursive: true,
181654
181805
  filter: (path3) => !excludedNames.has(basename3(path3))
181655
181806
  })));
@@ -181666,14 +181817,14 @@ async function stageFlowFiles(args) {
181666
181817
  if (files.length > 1) {
181667
181818
  return Promise.all(files.map((f3) => stageOneFile(f3, execDir, fs3)));
181668
181819
  }
181669
- await Promise.all(files.map((f3) => fs3.copyFile(f3, join30(execDir, basename4(f3)))));
181670
- return files.map((f3) => join30(execDir, basename4(f3)));
181820
+ await Promise.all(files.map((f3) => fs3.copyFile(f3, join31(execDir, basename4(f3)))));
181821
+ return files.map((f3) => join31(execDir, basename4(f3)));
181671
181822
  }
181672
181823
  async function stageOneFile(file2, execDir, fs3) {
181673
181824
  const dirHash = createHash3("sha256").update(dirname10(file2)).digest("hex").slice(0, 8);
181674
- const subDir = join30(execDir, dirHash);
181825
+ const subDir = join31(execDir, dirHash);
181675
181826
  await fs3.mkdir(subDir, { recursive: true });
181676
- const dest = join30(subDir, basename4(file2));
181827
+ const dest = join31(subDir, basename4(file2));
181677
181828
  await fs3.copyFile(file2, dest);
181678
181829
  return dest;
181679
181830
  }
@@ -181681,7 +181832,7 @@ function remapPath(file2, projectDir, execDir) {
181681
181832
  if (file2 === projectDir)
181682
181833
  return execDir;
181683
181834
  if (file2.startsWith(projectDir + sep4)) {
181684
- return join30(execDir, file2.slice(projectDir.length + 1));
181835
+ return join31(execDir, file2.slice(projectDir.length + 1));
181685
181836
  }
181686
181837
  return;
181687
181838
  }
@@ -181691,10 +181842,10 @@ async function prepareRunDir(args) {
181691
181842
  const { files, projectDir, depsRoot, runRoot } = args;
181692
181843
  const fs3 = args.fs ?? makeDefaultFs();
181693
181844
  const runId = buildRunId(projectDir, files);
181694
- const runDir = join31(runRoot, runId);
181845
+ const runDir = join32(runRoot, runId);
181695
181846
  await fs3.rm(runDir, { recursive: true, force: true });
181696
181847
  await fs3.mkdir(runDir, { recursive: true });
181697
- const execDir = join31(runDir, "exec");
181848
+ const execDir = join32(runDir, "exec");
181698
181849
  await fs3.mkdir(execDir, { recursive: true });
181699
181850
  await populateInnerHop({ depsRoot, execDir, fs: fs3 });
181700
181851
  const stagedFiles = await stageFlowFiles({ files, projectDir, execDir, fs: fs3 });
@@ -181725,17 +181876,17 @@ function buildRunId(projectDir, files) {
181725
181876
  }
181726
181877
 
181727
181878
  // src/shell/testkit.ts
181728
- import { join as join32 } from "node:path";
181879
+ import { join as join33 } from "node:path";
181729
181880
  function notAvailableLocally(name) {
181730
181881
  throw new Error(runnerMessages.notAvailableLocally(name));
181731
181882
  }
181732
181883
  async function loadSdkDeps(cwd) {
181733
- const testkitDir = join32(cwd, "node_modules", "@qawolf", "testkit");
181884
+ const testkitDir = join33(cwd, "node_modules", "@qawolf", "testkit");
181734
181885
  try {
181735
181886
  const clientMod = await importFromPath(resolveFromEnvDir(cwd, "@qawolf/testkit/client"));
181736
181887
  if (typeof clientMod.createTestkitClient !== "function")
181737
181888
  throw new Error("createTestkitClient is not a function");
181738
- const scopeMod = await importFromPath(join32(testkitDir, "dist", "clientScope.js"));
181889
+ const scopeMod = await importFromPath(join33(testkitDir, "dist", "clientScope.js"));
181739
181890
  if (typeof scopeMod.configureTestkitClient !== "function")
181740
181891
  throw new Error("configureTestkitClient is not a function");
181741
181892
  return {
@@ -181793,9 +181944,9 @@ async function registerLazyEmailsClient(getInbox, cwd, deps) {
181793
181944
  }
181794
181945
 
181795
181946
  // src/commands/flows/envVarDeps.ts
181796
- import { join as join33 } from "node:path";
181947
+ import { join as join34 } from "node:path";
181797
181948
  function envFilePath(envDir) {
181798
- return join33(envDir, ".env");
181949
+ return join34(envDir, ".env");
181799
181950
  }
181800
181951
  async function readEnv(envDir, fs3) {
181801
181952
  try {
@@ -182292,17 +182443,21 @@ async function findFlowStamp(flowAbsPath) {
182292
182443
 
182293
182444
  // src/domains/install/browsers.ts
182294
182445
  async function installBrowserList(ctx, browsers, deps) {
182295
- const candidates = playwrightCliCandidates(deps.envDir, deps.platform);
182296
- const cliPath = candidates.find(deps.checkExists);
182297
- if (!cliPath) {
182298
- throw new Error(installMessages.playwrightNotFound(candidates[0] ?? deps.envDir));
182446
+ const cliJsPath = playwrightCliJsPath(deps.envDir);
182447
+ if (!deps.checkExists(cliJsPath)) {
182448
+ throw new Error(installMessages.playwrightNotFound(cliJsPath));
182299
182449
  }
182300
182450
  await ctx.ui.withProgress(browsers.map((browser) => ({
182301
182451
  message: installMessages.installingBrowser(browser),
182302
182452
  task: async () => {
182303
- const args = buildArgs(browser, deps.platform, deps.browserDeps);
182304
- const result = await deps.spawn(cliPath, args, {
182305
- platform: deps.platform
182453
+ const invocation = playwrightCliInvocation({
182454
+ envDir: deps.envDir,
182455
+ execPath: deps.execPath,
182456
+ cliArgs: buildArgs(browser, deps.platform, deps.browserDeps)
182457
+ });
182458
+ const result = await deps.spawn(invocation.cmd, invocation.args, {
182459
+ platform: deps.platform,
182460
+ env: invocation.env
182306
182461
  });
182307
182462
  if (result.exitCode !== 0) {
182308
182463
  throw new Error(formatError2(browser, result));
@@ -182320,6 +182475,7 @@ async function installBrowsers(ctx, pattern, deps) {
182320
182475
  }
182321
182476
  await installBrowserList(ctx, browsers, {
182322
182477
  spawn: deps.spawn,
182478
+ execPath: deps.execPath,
182323
182479
  platform: deps.platform,
182324
182480
  browserDeps: deps.browserDeps,
182325
182481
  envDir: await deps.resolveDepsRoot(files),
@@ -183194,14 +183350,14 @@ async function runWebFlow({
183194
183350
 
183195
183351
  // src/shell/embeddedWorkerCli.ts
183196
183352
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
183197
- import { basename as basename6, join as join34 } from "node:path";
183353
+ import { basename as basename6, join as join35 } from "node:path";
183198
183354
  var embeddedCliPathEnv = "QAWOLF_EMBEDDED_CLI_PATH";
183199
183355
  function extractEmbeddedWorkerCli() {
183200
183356
  const assetPath = process.env[embeddedCliPathEnv];
183201
183357
  if (assetPath === undefined || assetPath === "")
183202
183358
  return;
183203
- const dir = join34(getDataDir(), "worker");
183204
- const dest = join34(dir, basename6(assetPath));
183359
+ const dir = join35(getDataDir(), "worker");
183360
+ const dest = join35(dir, basename6(assetPath));
183205
183361
  if (existsSync3(dest))
183206
183362
  return dest;
183207
183363
  mkdirSync3(dir, { recursive: true });
@@ -183360,51 +183516,6 @@ import { dirname as dirname12 } from "node:path";
183360
183516
 
183361
183517
  // src/shell/reporter/createConsoleReporter.ts
183362
183518
  import { styleText as styleText3 } from "node:util";
183363
-
183364
- // src/shell/reporter/formatErrorWithCause.ts
183365
- function filterStack(stack) {
183366
- const cwd = process.cwd();
183367
- return stack.split(`
183368
- `).filter((line) => {
183369
- if (!/^\s+at /.test(line))
183370
- return true;
183371
- return !line.includes("node_modules") && !line.includes("dist/cli.js");
183372
- }).map((line) => {
183373
- if (!/^\s+at /.test(line))
183374
- return line;
183375
- return line.replace(`file://${cwd}/`, "").replace(`${cwd}/`, "");
183376
- }).join(`
183377
- `);
183378
- }
183379
- function renderCause(cause) {
183380
- if (cause instanceof Error)
183381
- return filterStack(cause.stack ?? cause.message);
183382
- if (typeof cause === "object" && cause !== null) {
183383
- const obj = cause;
183384
- if (typeof obj["message"] === "string")
183385
- return obj["message"];
183386
- try {
183387
- return JSON.stringify(cause);
183388
- } catch {
183389
- return String(cause);
183390
- }
183391
- }
183392
- return String(cause);
183393
- }
183394
- function formatErrorWithCause(err) {
183395
- const parts = [String(err)];
183396
- let cause = err.cause;
183397
- while (cause !== undefined && cause !== null) {
183398
- parts.push(`Caused by: ${renderCause(cause)}`);
183399
- if (!(cause instanceof Error))
183400
- break;
183401
- cause = cause.cause;
183402
- }
183403
- return parts.join(`
183404
- `);
183405
- }
183406
-
183407
- // src/shell/reporter/createConsoleReporter.ts
183408
183519
  function fmtDuration(ms2) {
183409
183520
  return `(${(ms2 / 1000).toFixed(2)}s)`;
183410
183521
  }
@@ -183483,30 +183594,8 @@ ${styleText3("dim", "─".repeat(width))}
183483
183594
  }
183484
183595
  };
183485
183596
  }
183486
- // src/shell/reporter/createCompositeReporter.ts
183487
- function callEach(reporters, pick2, event) {
183488
- for (const r2 of reporters) {
183489
- try {
183490
- pick2(r2)?.(event);
183491
- } catch (err) {
183492
- process.stderr.write(`[reporter error] ${String(err)}
183493
- `);
183494
- }
183495
- }
183496
- }
183497
- function createCompositeReporter(reporters) {
183498
- return {
183499
- onFlowStart: (e) => callEach(reporters, (r2) => r2.onFlowStart, e),
183500
- onFlowPass: (e) => callEach(reporters, (r2) => r2.onFlowPass, e),
183501
- onFlowFail: (e) => callEach(reporters, (r2) => r2.onFlowFail, e),
183502
- onTestStart: (e) => callEach(reporters, (r2) => r2.onTestStart, e),
183503
- onTestResult: (e) => callEach(reporters, (r2) => r2.onTestResult, e),
183504
- onScreenshot: (e) => callEach(reporters, (r2) => r2.onScreenshot, e),
183505
- onRunComplete: (e) => callEach(reporters, (r2) => r2.onRunComplete, e)
183506
- };
183507
- }
183508
183597
  // src/shell/reporter/createJUnitReporter.ts
183509
- import { join as join35 } from "node:path";
183598
+ import { join as join36 } from "node:path";
183510
183599
 
183511
183600
  // src/shell/reporter/junitXml.ts
183512
183601
  function escapeXml(str) {
@@ -183545,7 +183634,7 @@ ${suites}`;
183545
183634
 
183546
183635
  // src/shell/reporter/createJUnitReporter.ts
183547
183636
  function resolveJUnitOutputPath(junit, outputDir) {
183548
- return typeof junit === "string" && junit.trim() !== "" ? junit : join35(outputDir, "junit-report.xml");
183637
+ return typeof junit === "string" && junit.trim() !== "" ? junit : join36(outputDir, "junit-report.xml");
183549
183638
  }
183550
183639
  function createJUnitReporter(deps) {
183551
183640
  const flows = [];
@@ -183607,6 +183696,7 @@ function buildFlowsRunDeps(args) {
183607
183696
  peekFlowMeta: makePeekFlowMeta(ctx.fs),
183608
183697
  installBrowsers: (innerCtx, browsers) => installBrowserList(innerCtx, browsers, {
183609
183698
  spawn: defaultSpawn,
183699
+ execPath: process.execPath,
183610
183700
  platform: process.platform,
183611
183701
  browserDeps: flags.browserDeps,
183612
183702
  envDir: resolvedDir,
@@ -183634,11 +183724,11 @@ function buildFlowsRunDeps(args) {
183634
183724
 
183635
183725
  // src/commands/flows/loadEnvFile.ts
183636
183726
  import { readFile } from "node:fs/promises";
183637
- import { join as join36 } from "node:path";
183727
+ import { join as join37 } from "node:path";
183638
183728
  async function loadEnvFile(envDir) {
183639
183729
  let content;
183640
183730
  try {
183641
- content = await readFile(join36(envDir, ".env"), "utf8");
183731
+ content = await readFile(join37(envDir, ".env"), "utf8");
183642
183732
  } catch (err) {
183643
183733
  if (isNoEntError(err))
183644
183734
  return;
@@ -183739,7 +183829,7 @@ async function handleFlowsRun(ctx, pattern, flags, deps) {
183739
183829
  }
183740
183830
 
183741
183831
  // src/commands/flows/hybridRunDefaults.ts
183742
- import { join as join37, resolve as resolve10 } from "node:path";
183832
+ import { join as join38, resolve as resolve10 } from "node:path";
183743
183833
  function makeDefaultHybridDeps(fs9) {
183744
183834
  return {
183745
183835
  expandPatterns: (patterns, cwd, logger) => expandPatterns(patterns, cwd, logger, fs9),
@@ -183758,7 +183848,7 @@ async function handleHybridFlowsRun(ctx, pattern, flags, deps) {
183758
183848
  if (validation !== "ok") {
183759
183849
  return { error: validation.error, exitCode: 2 };
183760
183850
  }
183761
- const envDir = resolve10(join37(".qawolf", flags.env));
183851
+ const envDir = resolve10(join38(".qawolf", flags.env));
183762
183852
  const patternArgs = buildPatternArgs(pattern);
183763
183853
  const globFlows = () => resolvedDeps.expandPatterns(patternArgs, envDir, ctx.log("flows"));
183764
183854
  let files = await globFlows();
@@ -183797,8 +183887,10 @@ function registerFlowsRunCommand(flows, signals) {
183797
183887
  kindNote: "read with --env"
183798
183888
  }).description("Run flows matching [pattern], or every flow when omitted; with --env, pull missing flows from that QA Wolf environment").option("--retries <n>", "Retry each failing flow up to this many times", parseInteger("--retries", { min: 0 }), 0).option("--bail", "Stop the run after the first failure", false).option("--workers <n>", "Parallel worker count for web flows", parseInteger("--workers", { min: 1 }), 1).option("--timeout <ms>", "Default timeout for actions and assertions, in milliseconds", parseInteger("--timeout", { min: 0 }), 30000).option("--video <mode>", "Record video: on | off | retain-on-failure", parseEnum("--video", videoModes), videoDefault).option("--trace <mode>", "Record Playwright trace: on | off | retain-on-failure", parseEnum("--trace", traceModes), traceDefault).option("--har <mode>", "Record HAR network log: on | off | retain-on-failure", parseEnum("--har", harModes), harDefault).option("--har-content <mode>", "HAR response bodies: omit | full (full uses more memory)", parseEnum("--har-content", harContentModes), harContentDefault).option("--output-dir <path>", "Directory for run artifacts (videos, traces, HAR)", defaultOutputDir).option("--junit [path]", "Write a JUnit XML report (default: <output-dir>/junit-report.xml)").option("--headed", "Show the browser window instead of headless", false).option("--env <env>", "Pull and run a flow from this environment (UUID or slug) if not cached locally").option("--deps <dir>", "Use this prepared dependency directory instead of auto-installing the runtime; or set QAWOLF_RUNTIME_DIR to relocate the managed runtime").option("--no-browser-deps", "Skip installing OS-level browser dependencies (Linux --with-deps, which needs root); requires the system libraries to already be present").addHelpText("after", runExamples).action((pattern, opts, command) => {
183799
183889
  if (opts.env !== undefined) {
183800
- const hybridFlags = { ...opts, env: opts.env };
183801
- return withAuthContext(signals, (ctx) => handleHybridFlowsRun(ctx, pattern, hybridFlags))(opts, command);
183890
+ return withResolvedEnv(signals, {
183891
+ explicit: opts.env,
183892
+ requiredMessage: flowsMessages.run.requiresEnv
183893
+ }, (ctx, env3) => handleHybridFlowsRun(ctx, pattern, { ...opts, env: env3 }))(opts, command);
183802
183894
  }
183803
183895
  return withContext(signals, (ctx) => handleFlowsRun(ctx, pattern, opts))(opts, command);
183804
183896
  });
@@ -183902,7 +183994,100 @@ function registerFlowsCommand(program2, signals) {
183902
183994
  }
183903
183995
 
183904
183996
  // src/domains/init/init.ts
183905
- import { dirname as dirname13, join as join38, relative as relative7 } from "node:path";
183997
+ import { dirname as dirname13, join as join40, relative as relative7 } from "node:path";
183998
+
183999
+ // src/domains/init/ensurePackageJson.ts
184000
+ import { join as join39 } from "node:path";
184001
+ async function ensurePackageJson(ctx, yes, deps) {
184002
+ const pkgPath = join39(deps.cwd, "package.json");
184003
+ if (!await deps.fs.pathExists(pkgPath)) {
184004
+ const confirmed2 = await ctx.ui.confirm(initMessages.createPackageJsonPrompt, { yes });
184005
+ if (!confirmed2.ok || !confirmed2.value) {
184006
+ ctx.ui.info(initMessages.skippedCreatingPackageJson);
184007
+ return;
184008
+ }
184009
+ const pkg2 = {
184010
+ private: true,
184011
+ type: "module",
184012
+ dependencies: { "@qawolf/flows": flowsVersion },
184013
+ scripts: { "test:e2e": "qawolf flows run" }
184014
+ };
184015
+ await deps.fs.writeFile(pkgPath, JSON.stringify(pkg2, undefined, 2) + `
184016
+ `);
184017
+ ctx.ui.step(initMessages.createdPackageJson);
184018
+ return;
184019
+ }
184020
+ const raw = await deps.fs.readFile(pkgPath);
184021
+ let parsed;
184022
+ try {
184023
+ parsed = JSON.parse(raw);
184024
+ } catch {
184025
+ ctx.ui.warn(initMessages.packageJsonInvalidJson);
184026
+ return;
184027
+ }
184028
+ if (!isRecord3(parsed)) {
184029
+ ctx.ui.warn(initMessages.packageJsonNotAnObject);
184030
+ return;
184031
+ }
184032
+ const pkg = parsed;
184033
+ const scripts = readSection(ctx, pkg, "scripts");
184034
+ const dependencies = readSection(ctx, pkg, "dependencies");
184035
+ const devDependencies = readSection(ctx, pkg, "devDependencies");
184036
+ const needScript = scripts !== undefined && !scripts["test:e2e"];
184037
+ const priorType = pkg["type"];
184038
+ const needType = priorType !== "module";
184039
+ const needFlowsDep = dependencies !== undefined && !dependencies["@qawolf/flows"] && !devDependencies?.["@qawolf/flows"];
184040
+ const changes = [];
184041
+ if (needScript)
184042
+ changes.push(initMessages.pkgChanges.script);
184043
+ if (needType)
184044
+ changes.push(initMessages.pkgChanges.type);
184045
+ if (needFlowsDep)
184046
+ changes.push(initMessages.pkgChanges.flowsDep);
184047
+ if (changes.length === 0) {
184048
+ ctx.ui.info(initMessages.packageJsonUpToDate);
184049
+ return;
184050
+ }
184051
+ if (scripts !== undefined && scripts["test:e2e"]) {
184052
+ ctx.ui.warn(initMessages.packageJsonHasTestE2e);
184053
+ }
184054
+ const confirmed = await ctx.ui.confirm(initMessages.updatePackageJsonPrompt(changes), { yes, destructive: true });
184055
+ if (!confirmed.ok || !confirmed.value) {
184056
+ ctx.ui.info(initMessages.skippedUpdatingPackageJson);
184057
+ return;
184058
+ }
184059
+ if (needScript && scripts !== undefined) {
184060
+ scripts["test:e2e"] = "qawolf flows run";
184061
+ pkg["scripts"] = scripts;
184062
+ }
184063
+ if (needType)
184064
+ pkg["type"] = "module";
184065
+ if (needFlowsDep && dependencies !== undefined) {
184066
+ dependencies["@qawolf/flows"] = flowsVersion;
184067
+ pkg["dependencies"] = dependencies;
184068
+ }
184069
+ const trailingNewline = raw.endsWith(`
184070
+ `) ? `
184071
+ ` : "";
184072
+ await deps.fs.writeFile(pkgPath, JSON.stringify(pkg, undefined, 2) + trailingNewline);
184073
+ ctx.ui.step(initMessages.updatedPackageJson);
184074
+ if (needType && priorType !== undefined) {
184075
+ const label = typeof priorType === "string" ? priorType : JSON.stringify(priorType);
184076
+ ctx.ui.warn(initMessages.typeChanged(label));
184077
+ }
184078
+ }
184079
+ function isRecord3(value) {
184080
+ return typeof value === "object" && value !== null && !Array.isArray(value);
184081
+ }
184082
+ function readSection(ctx, pkg, key) {
184083
+ const value = pkg[key];
184084
+ if (value === undefined)
184085
+ return {};
184086
+ if (isRecord3(value))
184087
+ return value;
184088
+ ctx.ui.warn(initMessages.packageJsonMalformedSection(key));
184089
+ return;
184090
+ }
183906
184091
 
183907
184092
  // src/domains/init/templates.ts
183908
184093
  var qawolfConfigTs = `export default {
@@ -183940,9 +184125,9 @@ function makeDefaultInitDeps() {
183940
184125
  async function handleInit(ctx, opts, deps = makeDefaultInitDeps()) {
183941
184126
  ctx.ui.gap();
183942
184127
  ctx.ui.intro(initMessages.title);
183943
- const configPath = join38(deps.cwd, "qawolf.config.ts");
183944
- const flowPath = join38(deps.cwd, "src", "flows", "example.flow.ts");
183945
- const gitignorePath = join38(deps.cwd, ".qawolf", ".gitignore");
184128
+ const configPath = join40(deps.cwd, "qawolf.config.ts");
184129
+ const flowPath = join40(deps.cwd, "src", "flows", "example.flow.ts");
184130
+ const gitignorePath = join40(deps.cwd, ".qawolf", ".gitignore");
183946
184131
  await writeWithPrompt(ctx, configPath, qawolfConfigTs, opts.yes, deps);
183947
184132
  await writeWithPrompt(ctx, flowPath, exampleFlowTs, opts.yes, deps);
183948
184133
  await writeWithPrompt(ctx, gitignorePath, qawolfGitignore, opts.yes, deps);
@@ -183965,54 +184150,6 @@ async function writeWithPrompt(ctx, filePath, content, yes, deps) {
183965
184150
  await deps.fs.writeFile(filePath, content);
183966
184151
  ctx.ui.step(initMessages.createdFile(relPath));
183967
184152
  }
183968
- async function ensurePackageJson(ctx, yes, deps) {
183969
- const pkgPath = join38(deps.cwd, "package.json");
183970
- if (!await deps.fs.pathExists(pkgPath)) {
183971
- const confirmed2 = await ctx.ui.confirm(initMessages.createPackageJsonPrompt, { yes });
183972
- if (!confirmed2.ok || !confirmed2.value) {
183973
- ctx.ui.info(initMessages.skippedCreatingPackageJson);
183974
- return;
183975
- }
183976
- const pkg2 = {
183977
- private: true,
183978
- type: "module",
183979
- dependencies: { "@qawolf/flows": flowsVersion },
183980
- scripts: { "test:e2e": "qawolf flows run" }
183981
- };
183982
- await deps.fs.writeFile(pkgPath, JSON.stringify(pkg2, undefined, 2) + `
183983
- `);
183984
- ctx.ui.step(initMessages.createdPackageJson);
183985
- return;
183986
- }
183987
- const raw = await deps.fs.readFile(pkgPath);
183988
- let pkg;
183989
- try {
183990
- pkg = JSON.parse(raw);
183991
- } catch {
183992
- ctx.ui.warn(initMessages.packageJsonInvalidJson);
183993
- return;
183994
- }
183995
- const scripts = pkg["scripts"] ?? {};
183996
- if (scripts["test:e2e"]) {
183997
- ctx.ui.warn(initMessages.packageJsonHasTestE2e);
183998
- return;
183999
- }
184000
- const confirmed = await ctx.ui.confirm(initMessages.addTestE2ePrompt, {
184001
- yes,
184002
- destructive: true
184003
- });
184004
- if (!confirmed.ok || !confirmed.value) {
184005
- ctx.ui.info(initMessages.skippedAddingTestE2e);
184006
- return;
184007
- }
184008
- scripts["test:e2e"] = "qawolf flows run";
184009
- pkg["scripts"] = scripts;
184010
- const trailingNewline = raw.endsWith(`
184011
- `) ? `
184012
- ` : "";
184013
- await deps.fs.writeFile(pkgPath, JSON.stringify(pkg, undefined, 2) + trailingNewline);
184014
- ctx.ui.step(initMessages.updatedPackageJson);
184015
- }
184016
184153
 
184017
184154
  // src/commands/init/index.ts
184018
184155
  function registerInitCommand(program2, signals) {
@@ -184028,7 +184165,7 @@ Examples:
184028
184165
  import { parseExecutionTarget as parseExecutionTarget4 } from "@qawolf/flow-targets";
184029
184166
 
184030
184167
  // src/domains/install/android/avd.ts
184031
- import { join as join39 } from "node:path";
184168
+ import { join as join41 } from "node:path";
184032
184169
  async function installAvds(ctx, specs, deps) {
184033
184170
  const sdkManagerPath = sdkManagerBin(deps.androidHome, deps.platform);
184034
184171
  const avdManagerPath = avdManagerBin(deps.androidHome, deps.platform);
@@ -184038,7 +184175,7 @@ async function installAvds(ctx, specs, deps) {
184038
184175
  if (versionResult.exitCode !== 0) {
184039
184176
  throw new Error(installMessages.android.sdkmanagerNotFound(sdkManagerPath));
184040
184177
  }
184041
- const licenseFile = join39(deps.androidHome, "licenses", "android-sdk-license");
184178
+ const licenseFile = join41(deps.androidHome, "licenses", "android-sdk-license");
184042
184179
  if (deps.checkExists(licenseFile)) {
184043
184180
  ctx.ui.info(installMessages.android.licensesAlreadyAccepted);
184044
184181
  } else {
@@ -184053,7 +184190,7 @@ async function installAvds(ctx, specs, deps) {
184053
184190
  for (const spec of specs) {
184054
184191
  if (installedImages.has(spec.systemImage))
184055
184192
  continue;
184056
- const systemImg = join39(deps.androidHome, ...spec.systemImage.split(";"), "system.img");
184193
+ const systemImg = join41(deps.androidHome, ...spec.systemImage.split(";"), "system.img");
184057
184194
  if (deps.checkExists(systemImg)) {
184058
184195
  ctx.ui.info(installMessages.android.systemImageAlreadyInstalled(spec.systemImage));
184059
184196
  installedImages.add(spec.systemImage);
@@ -184207,6 +184344,7 @@ async function handleInstallBrowsers(ctx, pattern, options) {
184207
184344
  return installBrowsers(ctx, pattern, {
184208
184345
  cwd,
184209
184346
  spawn: defaultSpawn,
184347
+ execPath: process.execPath,
184210
184348
  platform: process.platform,
184211
184349
  browserDeps,
184212
184350
  expandPatterns: (patterns, dir) => expandPatterns(patterns, dir ?? cwd, undefined, fs9),
@@ -184697,4 +184835,4 @@ createProgram({ signals }).parseAsync().catch(() => {
184697
184835
  process.exitCode = 1;
184698
184836
  }).finally(() => flushAndExit(typeof process.exitCode === "number" ? process.exitCode : 0));
184699
184837
 
184700
- //# debugId=80FB9FE67B1F0A9564756E2164756E21
184838
+ //# debugId=C0B1B96CCF6C4A9564756E2164756E21