@qawolf/cli 1.2.0 → 1.3.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 join32(s3) {
8517
+ BufferList.prototype.join = function join33(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 join32(s3) {
23073
+ BufferList.prototype.join = function join33(s3) {
23074
23074
  if (this.length === 0)
23075
23075
  return "";
23076
23076
  var p3 = this.head;
@@ -134044,7 +134044,7 @@ var require_snapshot_utils = __commonJS((exports, module) => {
134044
134044
  // node_modules/undici/lib/mock/snapshot-recorder.js
134045
134045
  var require_snapshot_recorder = __commonJS((exports, module) => {
134046
134046
  var { writeFile, readFile, mkdir } = __require("node:fs/promises");
134047
- var { dirname: dirname10, resolve: resolve9 } = __require("node:path");
134047
+ var { dirname: dirname11, resolve: resolve9 } = __require("node:path");
134048
134048
  var { setTimeout: setTimeout3, clearTimeout: clearTimeout2 } = __require("node:timers");
134049
134049
  var { InvalidArgumentError: InvalidArgumentError3, UndiciError } = require_errors6();
134050
134050
  var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
@@ -134239,7 +134239,7 @@ var require_snapshot_recorder = __commonJS((exports, module) => {
134239
134239
  throw new InvalidArgumentError3("Snapshot path is required");
134240
134240
  }
134241
134241
  const resolvedPath = resolve9(path4);
134242
- await mkdir(dirname10(resolvedPath), { recursive: true });
134242
+ await mkdir(dirname11(resolvedPath), { recursive: true });
134243
134243
  const data2 = Array.from(this.#snapshots.entries()).map(([hash2, snapshot]) => ({
134244
134244
  hash: hash2,
134245
134245
  snapshot
@@ -155181,7 +155181,7 @@ var authMessages = {
155181
155181
  rejected403: (noun) => `QA Wolf API rejected the${noun ? ` ${noun}` : ""} request (HTTP 403). Check that your API key has access to this environment.`,
155182
155182
  notFound404: (noun) => `QA Wolf API could not find ${noun ? `${noun} for that environment` : "that environment"} (HTTP 404). Check the --env value.`,
155183
155183
  failedWithStatus: (status, noun) => `QA Wolf API${noun ? ` ${noun}` : ""} request failed (HTTP ${status}).`,
155184
- networkUnreachable: (baseUrl, noun) => `Could not reach the QA Wolf API at ${baseUrl}${noun ? ` to fetch ${noun}` : ""}. Check your network connection and QAWOLF_API_URL.`,
155184
+ networkUnreachable: (baseUrl, noun) => `Could not reach the QA Wolf API at ${baseUrl}${noun ? ` to fetch ${noun}` : ""}. Check your network connection and QAWOLF_HOST_URL.`,
155185
155185
  unexpectedResponse: (noun) => `Unexpected${noun ? ` ${noun}` : ""} response from the QA Wolf API.`
155186
155186
  },
155187
155187
  bundle: {
@@ -155273,6 +155273,10 @@ var flowsMessages = {
155273
155273
  title: "Flows",
155274
155274
  remoteTitle: "Remote Flows",
155275
155275
  flowCount: (count) => pluralize(count, "flow"),
155276
+ list: {
155277
+ remoteRequiresEnv: "--remote requires --env <env> to pick the environment to list",
155278
+ flagsRequireRemote: "--env and --include-drafts require --remote"
155279
+ },
155276
155280
  pull: {
155277
155281
  downloadingBundle: "Downloading flows bundle",
155278
155282
  fetchingEnvVars: "Fetching environment variables",
@@ -155622,6 +155626,15 @@ function makeDefaultFs() {
155622
155626
  };
155623
155627
  }
155624
155628
 
155629
+ // src/shell/resolveHostUrl.ts
155630
+ var defaultHostUrl = "https://app.qawolf.com";
155631
+ function resolveHostUrl(env2) {
155632
+ const configuredHostUrl = env2["QAWOLF_HOST_URL"]?.trim();
155633
+ if (!configuredHostUrl)
155634
+ return defaultHostUrl;
155635
+ return configuredHostUrl.replace(/\/+$/, "");
155636
+ }
155637
+
155625
155638
  // src/domains/auth/resolve.ts
155626
155639
  import { Entry as Entry3 } from "@napi-rs/keyring";
155627
155640
 
@@ -170022,7 +170035,8 @@ async function validateApiKey(deps) {
170022
170035
  }
170023
170036
  // src/shell/logger.ts
170024
170037
  var import_pino = __toESM(require_pino(), 1);
170025
- import { join as join4 } from "node:path";
170038
+ import { mkdirSync as mkdirSync2, openSync } from "node:fs";
170039
+ import { dirname, join as join4 } from "node:path";
170026
170040
  function defaultLogPath() {
170027
170041
  return join4(envPaths("qawolf", { suffix: "" }).log, "cli.log");
170028
170042
  }
@@ -170036,7 +170050,8 @@ function resolveStderrLevel(env2, verbose) {
170036
170050
  }
170037
170051
  function createLoggingSystem(opts) {
170038
170052
  const { stderrLevel, logPath } = opts;
170039
- const dest = import_pino.default.destination({ dest: logPath, mkdir: true, sync: false });
170053
+ mkdirSync2(dirname(logPath), { recursive: true });
170054
+ const dest = import_pino.default.destination({ fd: openSync(logPath, "a"), sync: false });
170040
170055
  const fileLogger = import_pino.default({ level: "debug" }, dest);
170041
170056
  const levelNums = {
170042
170057
  trace: 10,
@@ -171158,45 +171173,6 @@ async function getIdentity(apiKey, deps) {
171158
171173
  return { ok: true, data: parsed.data };
171159
171174
  }
171160
171175
 
171161
- // src/shell/platform/getRemoteFlows.ts
171162
- var remoteFlowSchema = exports_external.object({
171163
- executionTarget: exports_external.union([exports_external.string(), exports_external.record(exports_external.string(), exports_external.unknown())]),
171164
- id: exports_external.string(),
171165
- name: exports_external.string(),
171166
- path: exports_external.string(),
171167
- tags: exports_external.array(exports_external.string())
171168
- });
171169
- var remoteFlowsResponseSchema = exports_external.object({
171170
- flows: exports_external.array(remoteFlowSchema)
171171
- });
171172
- async function getRemoteFlows(apiKey, deps) {
171173
- const url2 = `${deps.baseUrl}/api/v0/flows?includeDrafts=false`;
171174
- let response;
171175
- try {
171176
- response = await deps.fetch(url2, {
171177
- headers: { Authorization: `Bearer ${apiKey}` },
171178
- signal: AbortSignal.timeout(15000)
171179
- });
171180
- } catch (error51) {
171181
- return { ok: false, error: { kind: "network", cause: toError(error51) } };
171182
- }
171183
- if (!response.ok) {
171184
- const body = await response.text().catch(() => "");
171185
- return {
171186
- ok: false,
171187
- error: { kind: "http", status: response.status, body }
171188
- };
171189
- }
171190
- const json2 = await response.json().catch(() => {
171191
- return;
171192
- });
171193
- const parsed = remoteFlowsResponseSchema.safeParse(json2);
171194
- if (!parsed.success) {
171195
- return { ok: false, error: { kind: "parse", cause: parsed.error } };
171196
- }
171197
- return { ok: true, data: parsed.data };
171198
- }
171199
-
171200
171176
  // src/shell/platform/types.ts
171201
171177
  var environmentListItemSchema = exports_external.object({
171202
171178
  envId: exports_external.string(),
@@ -171250,10 +171226,10 @@ async function listTeamStorageFiles(trpc, args, deps) {
171250
171226
  }
171251
171227
 
171252
171228
  // src/shell/platform/teamStorageAssets.ts
171253
- import { dirname as dirname2, join as join8 } from "node:path";
171229
+ import { dirname as dirname3, join as join8 } from "node:path";
171254
171230
 
171255
171231
  // src/shell/platform/teamStorageAssetManifest.ts
171256
- import { dirname } from "node:path";
171232
+ import { dirname as dirname2 } from "node:path";
171257
171233
  var assetManifestSchema = exports_external.object({
171258
171234
  version: exports_external.literal(1),
171259
171235
  files: exports_external.array(exports_external.object({
@@ -171297,7 +171273,7 @@ async function writeAssetManifest(assetsAbs, files, fs2) {
171297
171273
  }))
171298
171274
  };
171299
171275
  const path2 = assetManifestPath(assetsAbs);
171300
- await fs2.mkdir(dirname(path2), { recursive: true });
171276
+ await fs2.mkdir(dirname2(path2), { recursive: true });
171301
171277
  await fs2.writeFile(path2, `${JSON.stringify(manifest, undefined, 2)}
171302
171278
  `);
171303
171279
  }
@@ -171463,7 +171439,7 @@ async function writeAssetSnapshot(args) {
171463
171439
  await args.deps.fs.mkdir(args.tmpAssets, { recursive: true });
171464
171440
  for (const { file: file2, relativePath } of args.safeFiles) {
171465
171441
  const dest = join8(args.tmpAssets, relativePath);
171466
- await args.deps.fs.mkdir(dirname2(dest), { recursive: true });
171442
+ await args.deps.fs.mkdir(dirname3(dest), { recursive: true });
171467
171443
  if (args.reusable.has(relativePath)) {
171468
171444
  await args.deps.fs.copyFile(join8(args.assetsAbs, relativePath), dest);
171469
171445
  continue;
@@ -171502,14 +171478,6 @@ function createPlatformClient(apiKey, deps) {
171502
171478
  sleep: deps.sleep
171503
171479
  });
171504
171480
  },
171505
- async getRemoteFlows() {
171506
- return requestWithRetry({
171507
- call: () => getRemoteFlows(apiKey, deps),
171508
- backoffMs: requestBackoffMs2,
171509
- describe: (err) => describeRequestError(err, deps.baseUrl, "flows"),
171510
- sleep: deps.sleep
171511
- });
171512
- },
171513
171481
  getFlowsBundleUrl: getFlowsBundleUrlImpl,
171514
171482
  callPublicApi: makeCallPublicApiMethod(trpc, deps, requestBackoffMs2),
171515
171483
  async getEnvVars(envId) {
@@ -173418,7 +173386,7 @@ function buildBaseContext(command, signals) {
173418
173386
  logPath: defaultLogPath(),
173419
173387
  ...verboseWrite ? { verboseWrite } : {}
173420
173388
  });
173421
- const apiBaseUrl = env2["QAWOLF_API_URL"]?.replace(/\/+$/, "") || "https://app.qawolf.com";
173389
+ const apiBaseUrl = resolveHostUrl(env2);
173422
173390
  return {
173423
173391
  ctx: {
173424
173392
  ui: createUI(outputMode, {
@@ -173630,7 +173598,7 @@ function registerAuthCommand(program2, signals) {
173630
173598
  // package.json
173631
173599
  var package_default = {
173632
173600
  name: "@qawolf/cli",
173633
- version: "1.2.0",
173601
+ version: "1.3.2",
173634
173602
  description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
173635
173603
  keywords: [
173636
173604
  "automation",
@@ -173690,16 +173658,17 @@ var package_default = {
173690
173658
  "@napi-rs/keyring": "1.3.0",
173691
173659
  "@oxc-node/core": "0.1.0",
173692
173660
  "@playwright/test": "1.60.0",
173693
- "@qawolf/api-contracts": "0.1.0",
173661
+ "@qawolf/api-contracts": "0.2.0",
173694
173662
  "@qawolf/emails": "1.1.1",
173695
173663
  "@qawolf/flow-targets": "1.0.0",
173696
- "@qawolf/flows": "0.1.1",
173664
+ "@qawolf/flows": "0.1.4",
173697
173665
  "@qawolf/testkit": "1.1.1",
173698
173666
  appium: "2.11.3",
173699
173667
  "appium-uiautomator2-driver": "3.7.0",
173700
173668
  "appium-xcuitest-driver": "9.10.5",
173701
173669
  commander: "14.0.3",
173702
173670
  "env-paths": "4.0.0",
173671
+ "expect-webdriverio": "5.6.5",
173703
173672
  picomatch: "4.0.4",
173704
173673
  pino: "10.3.1",
173705
173674
  playwright: "1.58.2",
@@ -174217,13 +174186,13 @@ function renderResults(ui, results) {
174217
174186
  }
174218
174187
 
174219
174188
  // src/domains/flows/ensureDeps.ts
174220
- import { dirname as dirname3, join as join10 } from "node:path";
174189
+ import { dirname as dirname4, join as join10 } from "node:path";
174221
174190
  function findEnvDir(flowPath, fs2) {
174222
- let dir = dirname3(flowPath);
174191
+ let dir = dirname4(flowPath);
174223
174192
  while (true) {
174224
174193
  if (fs2.existsSync(join10(dir, "package.json")))
174225
174194
  return dir;
174226
- const parent = dirname3(dir);
174195
+ const parent = dirname4(dir);
174227
174196
  if (parent === dir)
174228
174197
  return;
174229
174198
  dir = parent;
@@ -174256,7 +174225,7 @@ import { fileURLToPath } from "url";
174256
174225
 
174257
174226
  // node_modules/fdir/dist/index.mjs
174258
174227
  import { createRequire as createRequire2 } from "module";
174259
- import { basename, dirname as dirname4, normalize as normalize2, relative as relative2, resolve, sep as sep3 } from "path";
174228
+ import { basename, dirname as dirname5, normalize as normalize2, relative as relative2, resolve, sep as sep3 } from "path";
174260
174229
  import * as nativeFs from "fs";
174261
174230
  var __require2 = /* @__PURE__ */ createRequire2(import.meta.url);
174262
174231
  function cleanPath(path2) {
@@ -174420,7 +174389,7 @@ function build$2(options, isSynchronous) {
174420
174389
  function isRecursive(path2, resolved, state) {
174421
174390
  if (state.options.useRealPaths)
174422
174391
  return isRecursiveUsingRealPaths(resolved, state);
174423
- let parent = dirname4(path2);
174392
+ let parent = dirname5(path2);
174424
174393
  let depth = 1;
174425
174394
  while (parent !== state.root && depth < 2) {
174426
174395
  const resolvedPath = state.symlinks.get(parent);
@@ -174428,7 +174397,7 @@ function isRecursive(path2, resolved, state) {
174428
174397
  if (isSameRoot)
174429
174398
  depth++;
174430
174399
  else
174431
- parent = dirname4(parent);
174400
+ parent = dirname5(parent);
174432
174401
  }
174433
174402
  state.symlinks.set(path2, resolved);
174434
174403
  return depth > 1;
@@ -174624,7 +174593,7 @@ var Walker = class {
174624
174593
  } else {
174625
174594
  resolvedPath = useRealPaths ? resolvedPath : path2;
174626
174595
  const filename = basename(resolvedPath);
174627
- const directoryPath$1 = normalizePath(dirname4(resolvedPath), this.state.options);
174596
+ const directoryPath$1 = normalizePath(dirname5(resolvedPath), this.state.options);
174628
174597
  resolvedPath = this.joinPath(filename, directoryPath$1);
174629
174598
  this.pushFile(resolvedPath, files, this.state.counts, filters);
174630
174599
  }
@@ -175173,13 +175142,14 @@ import { createHash } from "node:crypto";
175173
175142
  import { join as join12, resolve as resolve3 } from "node:path";
175174
175143
 
175175
175144
  // src/generated/dependencyVersions.ts
175176
- var flowsVersion = "0.1.1";
175145
+ var flowsVersion = "0.1.4";
175177
175146
  var playwrightVersion = "1.58.2";
175178
175147
  var emailsVersion = "1.1.1";
175179
175148
  var testkitVersion = "1.1.1";
175180
175149
  var appiumVersion = "2.11.3";
175181
175150
  var appiumXcuitestDriverVersion = "9.10.5";
175182
175151
  var appiumUiautomator2DriverVersion = "3.7.0";
175152
+ var expectWebdriverioVersion = "5.6.5";
175183
175153
 
175184
175154
  // src/domains/runtimeEnv/pinnedPackages.ts
175185
175155
  var pinnedPackages = [
@@ -175192,7 +175162,8 @@ var pinnedPackages = [
175192
175162
  {
175193
175163
  name: "appium-uiautomator2-driver",
175194
175164
  version: appiumUiautomator2DriverVersion
175195
- }
175165
+ },
175166
+ { name: "expect-webdriverio", version: expectWebdriverioVersion }
175196
175167
  ];
175197
175168
 
175198
175169
  // src/domains/runtimeEnv/managedEnvDir.ts
@@ -175702,14 +175673,80 @@ function handleFlowsList(ctx, pattern) {
175702
175673
 
175703
175674
  // src/domains/flows/listRemote.ts
175704
175675
  var import_picomatch2 = __toESM(require_picomatch2(), 1);
175705
- async function flowsListRemote(ctx, pattern) {
175706
- const result = await ctx.platform.getRemoteFlows();
175676
+
175677
+ // node_modules/@qawolf/api-contracts/dist/v1/ids.js
175678
+ var defaultIdSchemas = {
175679
+ environment: exports_external.string().min(1).describe("The id of the environment."),
175680
+ flow: exports_external.string().min(1).describe("The id of the flow."),
175681
+ run: exports_external.string().min(1).describe("The id of the run.")
175682
+ };
175683
+
175684
+ // node_modules/@qawolf/api-contracts/dist/v1/flow/index.js
175685
+ var makeListFlowsContract = (ids) => {
175686
+ const input = exports_external.object({
175687
+ environmentId: ids.environment,
175688
+ includeDrafts: exports_external.boolean().default(false).describe("Include draft flows in the listing.")
175689
+ });
175690
+ const executionTarget = exports_external.union([exports_external.string().min(1), exports_external.record(exports_external.string(), exports_external.unknown())]).describe("The target the flow executes against, as a preset or object.");
175691
+ const output = exports_external.object({
175692
+ flows: exports_external.array(exports_external.object({
175693
+ executionTarget,
175694
+ flowId: ids.flow,
175695
+ name: exports_external.string(),
175696
+ path: exports_external.string().describe("The flow file path, relative to the repo."),
175697
+ tags: exports_external.array(exports_external.string())
175698
+ }))
175699
+ });
175700
+ return {
175701
+ description: "List the flows of an environment at its latest reconciled commit.",
175702
+ input,
175703
+ kind: "read",
175704
+ name: "flow.list",
175705
+ output
175706
+ };
175707
+ };
175708
+
175709
+ // node_modules/@qawolf/api-contracts/dist/v1/run/index.js
175710
+ var makeCreateRunContract = (ids) => {
175711
+ const input = exports_external.object({
175712
+ environmentId: ids.environment,
175713
+ environmentVariables: exports_external.record(exports_external.string(), exports_external.string()).optional(),
175714
+ flowIds: exports_external.array(ids.flow).min(1),
175715
+ ignoreRules: exports_external.boolean().default(false)
175716
+ });
175717
+ const output = exports_external.object({ runId: ids.run });
175718
+ return {
175719
+ description: "Create a run for selected flows in an environment.",
175720
+ input,
175721
+ kind: "write",
175722
+ name: "run.create",
175723
+ output
175724
+ };
175725
+ };
175726
+
175727
+ // node_modules/@qawolf/api-contracts/dist/v1/index.js
175728
+ var makeContractsV1 = (ids) => ({
175729
+ flow: {
175730
+ list: makeListFlowsContract(ids)
175731
+ },
175732
+ run: {
175733
+ create: makeCreateRunContract(ids)
175734
+ }
175735
+ });
175736
+ var publicContractsV1 = makeContractsV1(defaultIdSchemas);
175737
+
175738
+ // src/domains/flows/listRemote.ts
175739
+ async function flowsListRemote(ctx, pattern, options) {
175740
+ const result = await ctx.platform.callPublicApi(publicContractsV1.flow.list, {
175741
+ environmentId: options.env,
175742
+ includeDrafts: options.includeDrafts
175743
+ });
175707
175744
  if (!result.ok) {
175708
175745
  return { error: result.error };
175709
175746
  }
175710
175747
  const matches = pattern ? import_picomatch2.default(pattern) : undefined;
175711
175748
  const items = result.value.flows.filter((f2) => !matches || matches(f2.path)).map((f2) => ({
175712
- id: f2.id,
175749
+ flowId: f2.flowId,
175713
175750
  file: f2.path,
175714
175751
  name: f2.name,
175715
175752
  tags: f2.tags,
@@ -175739,7 +175776,7 @@ async function flowsListRemote(ctx, pattern) {
175739
175776
  }
175740
175777
 
175741
175778
  // src/domains/flows/pull/handler.ts
175742
- import { dirname as dirname6, join as join23, resolve as resolve7 } from "node:path";
175779
+ import { dirname as dirname7, join as join23, resolve as resolve7 } from "node:path";
175743
175780
 
175744
175781
  // src/shell/manifest/io.ts
175745
175782
  import { createHash as createHash2 } from "node:crypto";
@@ -176130,7 +176167,7 @@ async function writeEnvFile(envDir, vars, fs2 = makeDefaultFs()) {
176130
176167
  }
176131
176168
 
176132
176169
  // src/domains/flows/pull/extract.ts
176133
- import { dirname as dirname5, resolve as resolve6 } from "node:path";
176170
+ import { dirname as dirname6, resolve as resolve6 } from "node:path";
176134
176171
  import { createGunzip } from "node:zlib";
176135
176172
 
176136
176173
  // node_modules/tar/dist/esm/index.min.js
@@ -179394,7 +179431,7 @@ async function handleEntry(args) {
179394
179431
  total: args.getTotal()
179395
179432
  });
179396
179433
  args.addTotal(size);
179397
- args.fs.mkdirSync(dirname5(target), { recursive: true });
179434
+ args.fs.mkdirSync(dirname6(target), { recursive: true });
179398
179435
  const chunks = [];
179399
179436
  await new Promise((res, rej) => {
179400
179437
  entry.on("data", (chunk) => chunks.push(chunk));
@@ -179498,7 +179535,7 @@ async function handleFlowsPull(ctx, opts, deps) {
179498
179535
  return { error: validation.error };
179499
179536
  }
179500
179537
  const destAbs = resolve7(opts.out ?? join23(".qawolf", opts.env));
179501
- const assetsAbs = join23(dirname6(destAbs), "assets");
179538
+ const assetsAbs = join23(dirname7(destAbs), "assets");
179502
179539
  await resolvedDeps.fs.mkdir(assetsAbs, { recursive: true });
179503
179540
  const yes = opts.yes ?? false;
179504
179541
  let archive;
@@ -180034,7 +180071,7 @@ async function defaultRunWebFlowDeps(cwd = process.cwd(), signals) {
180034
180071
 
180035
180072
  // src/domains/runtimeEnv/prepareRunDir.ts
180036
180073
  import { createHash as createHash3 } from "node:crypto";
180037
- import { basename as basename4, dirname as dirname9, join as join28, resolve as resolve8, sep as sep4 } from "node:path";
180074
+ import { basename as basename4, dirname as dirname10, join as join29, resolve as resolve8, sep as sep4 } from "node:path";
180038
180075
 
180039
180076
  // src/shell/copyDir.ts
180040
180077
  import { cp, readdir as readdir2 } from "node:fs/promises";
@@ -180075,7 +180112,10 @@ async function readPackageJson(pkgPath, fs3) {
180075
180112
  }
180076
180113
 
180077
180114
  // src/domains/runtimeEnv/innerHop.ts
180078
- import { dirname as dirname7, join as join26 } from "node:path";
180115
+ import { join as join27 } from "node:path";
180116
+
180117
+ // src/domains/runtimeEnv/linkPinnedPackages.ts
180118
+ import { dirname as dirname8, join as join26 } from "node:path";
180079
180119
 
180080
180120
  // src/domains/runtimeEnv/symlinkDir.ts
180081
180121
  import { symlink } from "node:fs/promises";
@@ -180084,25 +180124,35 @@ async function createDirSymlink(source, target) {
180084
180124
  await symlink(source, target, linkType);
180085
180125
  }
180086
180126
 
180087
- // src/domains/runtimeEnv/innerHop.ts
180088
- async function populateInnerHop(args) {
180089
- const { depsRoot, execDir, fs: fs3 } = args;
180127
+ // src/domains/runtimeEnv/linkPinnedPackages.ts
180128
+ async function linkPinnedPackages(args) {
180129
+ const { depsRoot, nodeModulesDir, fs: fs3 } = args;
180090
180130
  const managedModules = join26(depsRoot, "node_modules");
180091
- const innerModules = join26(execDir, "node_modules");
180092
- await fs3.mkdir(innerModules, { recursive: true });
180131
+ await fs3.mkdir(nodeModulesDir, { recursive: true });
180093
180132
  for (const { name } of pinnedPackages) {
180094
180133
  const segments = name.split("/");
180095
- const target = join26(innerModules, ...segments);
180134
+ const target = join26(nodeModulesDir, ...segments);
180096
180135
  if (segments.length > 1) {
180097
- await fs3.mkdir(dirname7(target), { recursive: true });
180136
+ await fs3.mkdir(dirname8(target), { recursive: true });
180098
180137
  }
180138
+ await fs3.rm(target, { recursive: true, force: true });
180099
180139
  await createDirSymlink(join26(managedModules, ...segments), target);
180100
180140
  }
180101
180141
  }
180102
180142
 
180143
+ // src/domains/runtimeEnv/innerHop.ts
180144
+ async function populateInnerHop(args) {
180145
+ const { depsRoot, execDir, fs: fs3 } = args;
180146
+ await linkPinnedPackages({
180147
+ depsRoot,
180148
+ nodeModulesDir: join27(execDir, "node_modules"),
180149
+ fs: fs3
180150
+ });
180151
+ }
180152
+
180103
180153
  // src/domains/runtimeEnv/outerHop.ts
180104
180154
  import { lstat } from "node:fs/promises";
180105
- import { dirname as dirname8, join as join27 } from "node:path";
180155
+ import { dirname as dirname9, join as join28 } from "node:path";
180106
180156
  var executorPackageNames = new Set(pinnedPackages.map((p3) => p3.name));
180107
180157
  async function populateOuterHop(args) {
180108
180158
  const { projectDir, runDir, fs: fs3 } = args;
@@ -180112,28 +180162,33 @@ async function populateOuterHop(args) {
180112
180162
  const depNames = Object.keys(installableDeps);
180113
180163
  const { satisfying, rejected } = await findSatisfyingNodeModulesDir(projectDir, depNames, fs3);
180114
180164
  if (satisfying !== undefined) {
180115
- await createDirSymlink(satisfying, join27(runDir, "node_modules"));
180165
+ await createDirSymlink(satisfying, join28(runDir, "node_modules"));
180116
180166
  return { mode: "symlink", nodeModulesDir: satisfying };
180117
180167
  }
180118
180168
  if (depNames.length === 0)
180119
180169
  return { mode: "none" };
180120
180170
  args.onInstallStart?.(depNames.length);
180121
180171
  await installOuterDeps(runDir, installableDeps, fs3, args.install ?? spawnNpmInstall);
180172
+ await linkPinnedPackages({
180173
+ depsRoot: args.depsRoot,
180174
+ nodeModulesDir: join28(runDir, "node_modules"),
180175
+ fs: fs3
180176
+ });
180122
180177
  return { mode: "install", depCount: depNames.length, rejected };
180123
180178
  }
180124
180179
  async function findSatisfyingNodeModulesDir(startDir, depNames, fs3) {
180125
180180
  const rejected = [];
180126
180181
  let dir = startDir;
180127
180182
  while (true) {
180128
- const candidate = join27(dir, "node_modules");
180183
+ const candidate = join28(dir, "node_modules");
180129
180184
  const stats = await lstatSafe(candidate);
180130
180185
  if (stats !== undefined && stats.isDirectory() && !stats.isSymbolicLink()) {
180131
- const missing = depNames.filter((name) => !fs3.existsSync(join27(candidate, name, "package.json")));
180186
+ const missing = depNames.filter((name) => !fs3.existsSync(join28(candidate, name, "package.json")));
180132
180187
  if (missing.length === 0)
180133
180188
  return { satisfying: candidate, rejected };
180134
180189
  rejected.push({ dir: candidate, missing });
180135
180190
  }
180136
- const parent = dirname8(dir);
180191
+ const parent = dirname9(dir);
180137
180192
  if (parent === dir)
180138
180193
  return { satisfying: undefined, rejected };
180139
180194
  dir = parent;
@@ -180149,7 +180204,7 @@ async function lstatSafe(path3) {
180149
180204
  async function readInstallableDeps(projectDir, fs3) {
180150
180205
  let content;
180151
180206
  try {
180152
- content = await fs3.readFile(join27(projectDir, "package.json"));
180207
+ content = await fs3.readFile(join28(projectDir, "package.json"));
180153
180208
  } catch {
180154
180209
  return {};
180155
180210
  }
@@ -180167,8 +180222,8 @@ async function readInstallableDeps(projectDir, fs3) {
180167
180222
  return Object.fromEntries(Object.entries(rawDeps).filter(([name]) => !executorPackageNames.has(name)).filter((entry) => typeof entry[1] === "string"));
180168
180223
  }
180169
180224
  async function installOuterDeps(runDir, deps, fs3, install) {
180170
- await fs3.writeFile(join27(runDir, "package.json"), JSON.stringify({ name: "qawolf-run", private: true, dependencies: deps }, undefined, 2));
180171
- await fs3.writeFile(join27(runDir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
180225
+ await fs3.writeFile(join28(runDir, "package.json"), JSON.stringify({ name: "qawolf-run", private: true, dependencies: deps }, undefined, 2));
180226
+ await fs3.writeFile(join28(runDir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
180172
180227
  `);
180173
180228
  const result = await install(runDir);
180174
180229
  if (result.exitCode !== 0) {
@@ -180182,10 +180237,10 @@ async function prepareRunDir(args) {
180182
180237
  const { files, projectDir, depsRoot, runRoot } = args;
180183
180238
  const fs3 = args.fs ?? makeDefaultFs();
180184
180239
  const runId = buildRunId(projectDir, files);
180185
- const runDir = join28(runRoot, runId);
180240
+ const runDir = join29(runRoot, runId);
180186
180241
  await fs3.rm(runDir, { recursive: true, force: true });
180187
180242
  await fs3.mkdir(runDir, { recursive: true });
180188
- const execDir = join28(runDir, "exec");
180243
+ const execDir = join29(runDir, "exec");
180189
180244
  await fs3.mkdir(execDir, { recursive: true });
180190
180245
  await populateInnerHop({ depsRoot, execDir, fs: fs3 });
180191
180246
  const stagedFiles = await stageFlowFiles({ files, projectDir, execDir, fs: fs3 });
@@ -180195,6 +180250,7 @@ async function prepareRunDir(args) {
180195
180250
  const outerHop = await populateOuterHop({
180196
180251
  projectDir,
180197
180252
  runDir,
180253
+ depsRoot,
180198
180254
  fs: fs3,
180199
180255
  ...args.onInstallStart !== undefined ? { onInstallStart: args.onInstallStart } : {}
180200
180256
  });
@@ -180221,38 +180277,38 @@ async function stageFlowFiles(args) {
180221
180277
  }
180222
180278
  if (files.length > 1) {
180223
180279
  return Promise.all(files.map(async (f3) => {
180224
- const dirHash = createHash3("sha256").update(dirname9(f3)).digest("hex").slice(0, 8);
180225
- const subDir = join28(execDir, dirHash);
180280
+ const dirHash = createHash3("sha256").update(dirname10(f3)).digest("hex").slice(0, 8);
180281
+ const subDir = join29(execDir, dirHash);
180226
180282
  await fs3.mkdir(subDir, { recursive: true });
180227
- const dest = join28(subDir, basename4(f3));
180283
+ const dest = join29(subDir, basename4(f3));
180228
180284
  await fs3.copyFile(f3, dest);
180229
180285
  return dest;
180230
180286
  }));
180231
180287
  }
180232
- await Promise.all(files.map((f3) => fs3.copyFile(f3, join28(execDir, basename4(f3)))));
180233
- return files.map((f3) => join28(execDir, basename4(f3)));
180288
+ await Promise.all(files.map((f3) => fs3.copyFile(f3, join29(execDir, basename4(f3)))));
180289
+ return files.map((f3) => join29(execDir, basename4(f3)));
180234
180290
  }
180235
180291
  function remapPath(file2, projectDir, execDir) {
180236
180292
  if (file2 === projectDir)
180237
180293
  return execDir;
180238
180294
  if (file2.startsWith(projectDir + sep4)) {
180239
- return join28(execDir, file2.slice(projectDir.length + 1));
180295
+ return join29(execDir, file2.slice(projectDir.length + 1));
180240
180296
  }
180241
180297
  return file2;
180242
180298
  }
180243
180299
 
180244
180300
  // src/shell/testkit.ts
180245
- import { join as join29 } from "node:path";
180301
+ import { join as join30 } from "node:path";
180246
180302
  function notAvailableLocally(name) {
180247
180303
  throw new Error(runnerMessages.notAvailableLocally(name));
180248
180304
  }
180249
180305
  async function loadSdkDeps(cwd) {
180250
- const testkitDir = join29(cwd, "node_modules", "@qawolf", "testkit");
180306
+ const testkitDir = join30(cwd, "node_modules", "@qawolf", "testkit");
180251
180307
  try {
180252
180308
  const clientMod = await import(resolveFromEnvDir(cwd, "@qawolf/testkit/client"));
180253
180309
  if (typeof clientMod.createTestkitClient !== "function")
180254
180310
  throw new Error("createTestkitClient is not a function");
180255
- const scopeMod = await import(join29(testkitDir, "dist", "clientScope.js"));
180311
+ const scopeMod = await import(join30(testkitDir, "dist", "clientScope.js"));
180256
180312
  if (typeof scopeMod.configureTestkitClient !== "function")
180257
180313
  throw new Error("configureTestkitClient is not a function");
180258
180314
  return {
@@ -180309,9 +180365,9 @@ async function registerLazyEmailsClient(getInbox, cwd, deps) {
180309
180365
  }
180310
180366
 
180311
180367
  // src/commands/flows/envVarDeps.ts
180312
- import { join as join30 } from "node:path";
180368
+ import { join as join31 } from "node:path";
180313
180369
  function envFilePath(envDir) {
180314
- return join30(envDir, ".env");
180370
+ return join31(envDir, ".env");
180315
180371
  }
180316
180372
  async function readEnv(envDir, fs3) {
180317
180373
  try {
@@ -180420,7 +180476,7 @@ async function createFlowRuntimeDeps({
180420
180476
  }
180421
180477
 
180422
180478
  // src/shell/appium/createAppiumServer.ts
180423
- import { join as join31 } from "node:path";
180479
+ import { join as join32 } from "node:path";
180424
180480
 
180425
180481
  // src/shell/appium/spawnAppium.ts
180426
180482
  import { spawn as spawn2 } from "node:child_process";
@@ -180495,7 +180551,7 @@ async function createAppiumServer(envDir, signals, params) {
180495
180551
  const spawnFn = params?.deps?.spawn ?? defaultSpawnAppium;
180496
180552
  const findFreePortFn = params?.deps?.findFreePort ?? findFreePort;
180497
180553
  const resolveAppiumBinFn = params?.deps?.resolveAppiumBin ?? resolveAppiumBin;
180498
- const appiumHome2 = params?.options?.appiumHome ?? join31(envPaths("qawolf").data, "appium");
180554
+ const appiumHome2 = params?.options?.appiumHome ?? join32(envPaths("qawolf").data, "appium");
180499
180555
  const timeoutMs3 = params?.options?.startTimeoutMs ?? defaultStartTimeoutMs;
180500
180556
  const bin = resolveAppiumBinFn(envDir);
180501
180557
  const port = await findFreePortFn();
@@ -180834,12 +180890,12 @@ Install Android Studio and open Tools > SDK Manager.`);
180834
180890
  }
180835
180891
 
180836
180892
  // src/shell/manifest/lookup.ts
180837
- import { basename as basename5, dirname as dirname10, relative as relative6 } from "node:path";
180893
+ import { basename as basename5, dirname as dirname11, relative as relative6 } from "node:path";
180838
180894
  async function findFlowStamp(flowAbsPath) {
180839
180895
  let envDir;
180840
- let current = dirname10(flowAbsPath);
180841
- while (current !== dirname10(current)) {
180842
- const parent2 = dirname10(current);
180896
+ let current = dirname11(flowAbsPath);
180897
+ while (current !== dirname11(current)) {
180898
+ const parent2 = dirname11(current);
180843
180899
  if (basename5(parent2) === ".qawolf") {
180844
180900
  envDir = current;
180845
180901
  break;
@@ -180914,7 +180970,7 @@ function formatError2(browser, result) {
180914
180970
  }
180915
180971
 
180916
180972
  // src/domains/runner/runAndroidFlow.ts
180917
- import path13 from "node:path";
180973
+ import path12 from "node:path";
180918
180974
 
180919
180975
  // src/shell/appium/createAndroidLaunchContext.ts
180920
180976
  import { writeFile } from "node:fs/promises";
@@ -181260,66 +181316,6 @@ function createRunner({
181260
181316
  }
181261
181317
 
181262
181318
  // src/domains/runner/runWebFlowUtils.ts
181263
- import crypto from "node:crypto";
181264
- import os6 from "node:os";
181265
- import path12 from "node:path";
181266
- function createLaunch({
181267
- browsers,
181268
- contextSetup,
181269
- launchBrowserOpts,
181270
- signals,
181271
- timeout,
181272
- traceMode = "off",
181273
- tracePath
181274
- }) {
181275
- const openBrowsers = [];
181276
- const openContexts = [];
181277
- const tracingEnabled = traceMode !== "off";
181278
- let closed = false;
181279
- const closeAll = async () => {
181280
- if (closed)
181281
- return;
181282
- closed = true;
181283
- if (tracingEnabled && tracePath !== undefined) {
181284
- await Promise.allSettled(openContexts.map((c2) => c2.tracing.stop({ path: tracePath })));
181285
- }
181286
- await Promise.allSettled(openContexts.map((c2) => c2.close()));
181287
- await Promise.allSettled(openBrowsers.map((b4) => b4.close()));
181288
- };
181289
- const unregister = signals.register(closeAll);
181290
- const trackContext = async (ctx) => {
181291
- openContexts.push(ctx);
181292
- if (tracingEnabled) {
181293
- await ctx.tracing.start({ screenshots: true, snapshots: true });
181294
- }
181295
- };
181296
- const launch = async (launchOpts) => {
181297
- const browserName = normalizeBrowserName(launchOpts?.browser);
181298
- const bt2 = browsers[browserName];
181299
- if (launchOpts?.persistentContext === true) {
181300
- const userDataDir = launchOpts.userDataDir ?? path12.join(os6.tmpdir(), `qawolf-${crypto.randomUUID()}`);
181301
- const context2 = await bt2.launchPersistentContext(userDataDir, {
181302
- ...launchBrowserOpts,
181303
- ...contextSetup
181304
- });
181305
- context2.setDefaultTimeout(timeout);
181306
- await trackContext(context2);
181307
- const page = await context2.newPage();
181308
- return { browserType: browserName, context: context2, page };
181309
- }
181310
- const browser = await bt2.launch(launchBrowserOpts);
181311
- openBrowsers.push(browser);
181312
- const context = await browser.newContext(contextSetup);
181313
- context.setDefaultTimeout(timeout);
181314
- await trackContext(context);
181315
- return { browser, browserType: browserName, context };
181316
- };
181317
- const cleanup = async () => {
181318
- unregister();
181319
- await closeAll();
181320
- };
181321
- return { launch, cleanup };
181322
- }
181323
181319
  function normalizeBrowserName(browser) {
181324
181320
  if (browser === "chrome" || browser === "msedge")
181325
181321
  return "chromium";
@@ -181359,7 +181355,7 @@ async function runAndroidFlow({
181359
181355
  throw new Error("runAndroidFlow: legacy flow functions are not supported; use flow() from @qawolf/flows/android");
181360
181356
  }
181361
181357
  } catch (err) {
181362
- const flowName2 = path13.basename(flowPath, path13.extname(flowPath));
181358
+ const flowName2 = path12.basename(flowPath, path12.extname(flowPath));
181363
181359
  return {
181364
181360
  passed: false,
181365
181361
  testCounts: { passed: 0, total: 0 },
@@ -181427,13 +181423,13 @@ async function runAndroidFlow({
181427
181423
  }
181428
181424
 
181429
181425
  // src/domains/runner/runWebFlow.ts
181430
- import path16 from "node:path";
181426
+ import path17 from "node:path";
181431
181427
 
181432
181428
  // src/domains/runner/initFlowRuntime.ts
181433
- import path14 from "node:path";
181429
+ import path13 from "node:path";
181434
181430
  import { pathToFileURL as pathToFileURL2 } from "node:url";
181435
181431
  async function runnerPathInDir(dir, fs9) {
181436
- const pkgPath = path14.join(dir, "node_modules", "@qawolf", "flows", "package.json");
181432
+ const pkgPath = path13.join(dir, "node_modules", "@qawolf", "flows", "package.json");
181437
181433
  try {
181438
181434
  const pkg = JSON.parse(await fs9.readFile(pkgPath));
181439
181435
  const entry = pkg.exports?.["./_runner"];
@@ -181441,7 +181437,7 @@ async function runnerPathInDir(dir, fs9) {
181441
181437
  if (typeof importPath !== "string") {
181442
181438
  throw new Error(`@qawolf/flows at ${pkgPath} does not export "./_runner" with an "import" condition`);
181443
181439
  }
181444
- return path14.resolve(path14.dirname(pkgPath), importPath);
181440
+ return path13.resolve(path13.dirname(pkgPath), importPath);
181445
181441
  } catch (err) {
181446
181442
  if (!isNoEntError(err))
181447
181443
  throw err;
@@ -181449,12 +181445,12 @@ async function runnerPathInDir(dir, fs9) {
181449
181445
  }
181450
181446
  }
181451
181447
  async function findFlowsRunnerPath(flowPath, fs9) {
181452
- let dir = path14.dirname(flowPath);
181448
+ let dir = path13.dirname(flowPath);
181453
181449
  while (true) {
181454
181450
  const result = await runnerPathInDir(dir, fs9);
181455
181451
  if (result !== undefined)
181456
181452
  return result;
181457
- const parent2 = path14.dirname(dir);
181453
+ const parent2 = path13.dirname(dir);
181458
181454
  if (parent2 === dir)
181459
181455
  throw new Error(`@qawolf/flows not found in node_modules above: ${flowPath}`);
181460
181456
  dir = parent2;
@@ -181486,7 +181482,7 @@ async function doInit(flowPath, timeout, fs9, depsRoot) {
181486
181482
  });
181487
181483
  }
181488
181484
  function initFlowRuntime(flowPath, options, fs9 = makeDefaultFs()) {
181489
- const startDir = path14.dirname(flowPath);
181485
+ const startDir = path13.dirname(flowPath);
181490
181486
  let p3 = initCache.get(startDir);
181491
181487
  if (!p3) {
181492
181488
  p3 = doInit(flowPath, options.timeout, fs9, options.depsRoot);
@@ -181495,13 +181491,137 @@ function initFlowRuntime(flowPath, options, fs9 = makeDefaultFs()) {
181495
181491
  return p3;
181496
181492
  }
181497
181493
 
181498
- // src/domains/runner/web/contextSetup.ts
181494
+ // src/domains/runner/createLaunch.ts
181495
+ import crypto from "node:crypto";
181496
+ import os6 from "node:os";
181499
181497
  import path15 from "node:path";
181498
+
181499
+ // src/domains/runner/web/artifactPaths.ts
181500
+ import path14 from "node:path";
181501
+ function indexedArtifactPath(basePath, index2) {
181502
+ if (index2 === 0)
181503
+ return basePath;
181504
+ const ext = path14.extname(basePath);
181505
+ const base = path14.basename(basePath, ext);
181506
+ return path14.join(path14.dirname(basePath), `${base}-${index2 + 1}${ext}`);
181507
+ }
181508
+ function createContextArtifacts(contextSetup, tracePath) {
181509
+ let contextIndex = 0;
181510
+ const harPaths = [];
181511
+ const tracePaths = [];
181512
+ const nextSetup = (overrides = {}) => {
181513
+ const index2 = contextIndex;
181514
+ contextIndex += 1;
181515
+ const setup = { ...contextSetup, ...overrides };
181516
+ if (contextSetup.recordHar !== undefined && overrides.recordHar === undefined) {
181517
+ setup.recordHar = {
181518
+ ...contextSetup.recordHar,
181519
+ path: indexedArtifactPath(contextSetup.recordHar.path, index2)
181520
+ };
181521
+ }
181522
+ if (setup.recordHar !== undefined)
181523
+ harPaths.push(setup.recordHar.path);
181524
+ return { setup, index: index2 };
181525
+ };
181526
+ const nextTracePath = (index2) => {
181527
+ if (tracePath === undefined)
181528
+ return;
181529
+ const contextTracePath = indexedArtifactPath(tracePath, index2);
181530
+ tracePaths.push(contextTracePath);
181531
+ return contextTracePath;
181532
+ };
181533
+ const artifactPaths = () => ({
181534
+ harPaths: [...harPaths],
181535
+ tracePaths: [...tracePaths]
181536
+ });
181537
+ return { nextSetup, nextTracePath, artifactPaths };
181538
+ }
181539
+
181540
+ // src/domains/runner/createLaunch.ts
181541
+ function createLaunch({
181542
+ browsers,
181543
+ contextSetup,
181544
+ launchBrowserOpts,
181545
+ signals,
181546
+ timeout,
181547
+ traceMode = "off",
181548
+ tracePath
181549
+ }) {
181550
+ const openBrowsers = [];
181551
+ const openContexts = [];
181552
+ const traceByContext = new Map;
181553
+ const tracingEnabled = traceMode !== "off";
181554
+ const { nextSetup, nextTracePath, artifactPaths } = createContextArtifacts(contextSetup, tracePath);
181555
+ let closePromise;
181556
+ const closeAll = () => {
181557
+ closePromise ??= (async () => {
181558
+ if (tracingEnabled) {
181559
+ await Promise.allSettled(openContexts.map((c2) => {
181560
+ const contextTracePath = traceByContext.get(c2);
181561
+ return contextTracePath === undefined ? Promise.resolve() : c2.tracing.stop({ path: contextTracePath });
181562
+ }));
181563
+ }
181564
+ await Promise.allSettled(openContexts.map((c2) => c2.close()));
181565
+ await Promise.allSettled(openBrowsers.map((b4) => b4.close()));
181566
+ })();
181567
+ return closePromise;
181568
+ };
181569
+ const unregister = signals.register(closeAll);
181570
+ const trackContext = async (ctx, index2) => {
181571
+ openContexts.push(ctx);
181572
+ if (tracingEnabled) {
181573
+ const contextTracePath = nextTracePath(index2);
181574
+ if (contextTracePath !== undefined) {
181575
+ traceByContext.set(ctx, contextTracePath);
181576
+ }
181577
+ await ctx.tracing.start({ screenshots: true, snapshots: true });
181578
+ }
181579
+ };
181580
+ const instrumentBrowser = (browser) => {
181581
+ const originalNewContext = browser.newContext.bind(browser);
181582
+ browser.newContext = async (opts) => {
181583
+ const { setup, index: index2 } = nextSetup(opts);
181584
+ const context = await originalNewContext(setup);
181585
+ context.setDefaultTimeout(timeout);
181586
+ await trackContext(context, index2);
181587
+ return context;
181588
+ };
181589
+ };
181590
+ const launch = async (launchOpts) => {
181591
+ const browserName = normalizeBrowserName(launchOpts?.browser);
181592
+ const bt2 = browsers[browserName];
181593
+ if (launchOpts?.persistentContext === true) {
181594
+ const userDataDir = launchOpts.userDataDir ?? path15.join(os6.tmpdir(), `qawolf-${crypto.randomUUID()}`);
181595
+ const { setup, index: index2 } = nextSetup();
181596
+ const context2 = await bt2.launchPersistentContext(userDataDir, {
181597
+ ...launchBrowserOpts,
181598
+ ...setup
181599
+ });
181600
+ context2.setDefaultTimeout(timeout);
181601
+ await trackContext(context2, index2);
181602
+ const page = await context2.newPage();
181603
+ return { browserType: browserName, context: context2, page };
181604
+ }
181605
+ const browser = await bt2.launch(launchBrowserOpts);
181606
+ openBrowsers.push(browser);
181607
+ instrumentBrowser(browser);
181608
+ const context = await browser.newContext({});
181609
+ return { browser, browserType: browserName, context };
181610
+ };
181611
+ const cleanup = async () => {
181612
+ unregister();
181613
+ await closeAll();
181614
+ };
181615
+ return { launch, cleanup, artifactPaths };
181616
+ }
181617
+
181618
+ // src/domains/runner/web/contextSetup.ts
181619
+ import path16 from "node:path";
181500
181620
  function harFlowPath(outputDir, flowName) {
181501
- return path15.join(outputDir, "har", `${flowName}.har`);
181621
+ return path16.join(outputDir, "har", `${flowName}.har`);
181502
181622
  }
181503
181623
  function traceFlowPath(outputDir, flowName) {
181504
- return path15.join(outputDir, "trace", `${flowName}.zip`);
181624
+ return path16.join(outputDir, "trace", `${flowName}.zip`);
181505
181625
  }
181506
181626
  function buildHarContextOpts(harPath, harContent) {
181507
181627
  return {
@@ -181517,7 +181637,7 @@ async function initHar(fs9, options, flowName) {
181517
181637
  if (harMode === "off")
181518
181638
  return { harMode, harPath: undefined };
181519
181639
  const harPath = harFlowPath(options.outputDir, flowName);
181520
- await fs9.mkdir(path15.dirname(harPath), { recursive: true });
181640
+ await fs9.mkdir(path16.dirname(harPath), { recursive: true });
181521
181641
  return { harMode, harPath };
181522
181642
  }
181523
181643
  async function initTrace(fs9, options, flowName) {
@@ -181525,7 +181645,7 @@ async function initTrace(fs9, options, flowName) {
181525
181645
  if (traceMode === "off")
181526
181646
  return { traceMode, tracePath: undefined };
181527
181647
  const tracePath = traceFlowPath(options.outputDir, flowName);
181528
- await fs9.mkdir(path15.dirname(tracePath), { recursive: true });
181648
+ await fs9.mkdir(path16.dirname(tracePath), { recursive: true });
181529
181649
  return { traceMode, tracePath };
181530
181650
  }
181531
181651
  async function maybeCleanupTrace(fs9, tracePath, passed, traceMode, warn) {
@@ -181538,7 +181658,7 @@ async function maybeCleanupTrace(fs9, tracePath, passed, traceMode, warn) {
181538
181658
  }
181539
181659
  }
181540
181660
  function buildContextSetup(videoSize, options, harPath) {
181541
- const videosDir = options.artifactDir ?? path15.join(options.outputDir, "videos");
181661
+ const videosDir = options.artifactDir ?? path16.join(options.outputDir, "videos");
181542
181662
  const harOpts = harPath !== undefined ? buildHarContextOpts(harPath, options.harContent ?? "omit") : {};
181543
181663
  return options.video !== "off" ? {
181544
181664
  viewport: videoSize,
@@ -181574,7 +181694,7 @@ async function runWebFlow({
181574
181694
  depsRoot: deps.depsRoot
181575
181695
  });
181576
181696
  } catch (err) {
181577
- const flowName2 = path16.basename(flowPath, path16.extname(flowPath));
181697
+ const flowName2 = path17.basename(flowPath, path17.extname(flowPath));
181578
181698
  return {
181579
181699
  passed: false,
181580
181700
  testCounts: { passed: 0, total: 0 },
@@ -181583,7 +181703,7 @@ async function runWebFlow({
181583
181703
  };
181584
181704
  }
181585
181705
  const isLegacy = typeof exported === "function";
181586
- const flowName = isLegacy ? path16.basename(flowPath, path16.extname(flowPath)) : exported.name;
181706
+ const flowName = isLegacy ? path17.basename(flowPath, path17.extname(flowPath)) : exported.name;
181587
181707
  const runFn = isLegacy ? exported : exported.run;
181588
181708
  const { harMode, harPath } = await initHar(deps.fs, options, flowName);
181589
181709
  const { traceMode, tracePath } = await initTrace(deps.fs, options, flowName);
@@ -181594,7 +181714,7 @@ async function runWebFlow({
181594
181714
  slowMo: options.slowMo,
181595
181715
  ...options.executablePath !== undefined ? { executablePath: options.executablePath } : {}
181596
181716
  };
181597
- const { launch, cleanup } = createLaunch({
181717
+ const { launch, cleanup, artifactPaths } = createLaunch({
181598
181718
  browsers: {
181599
181719
  chromium: deps.chromium,
181600
181720
  firefox: deps.firefox,
@@ -181636,28 +181756,29 @@ async function runWebFlow({
181636
181756
  } finally {
181637
181757
  await cleanup();
181638
181758
  const warn = (message) => deps.logger?.warn(message);
181639
- if (harPath !== undefined) {
181640
- await maybeCleanupHar(deps.fs, harPath, passed, harMode, warn);
181759
+ const assigned = artifactPaths();
181760
+ for (const contextHarPath of assigned.harPaths) {
181761
+ await maybeCleanupHar(deps.fs, contextHarPath, passed, harMode, warn);
181641
181762
  }
181642
- if (tracePath !== undefined) {
181643
- await maybeCleanupTrace(deps.fs, tracePath, passed, traceMode, warn);
181763
+ for (const contextTracePath of assigned.tracePaths) {
181764
+ await maybeCleanupTrace(deps.fs, contextTracePath, passed, traceMode, warn);
181644
181765
  }
181645
181766
  }
181646
181767
  }
181647
181768
 
181648
181769
  // src/shell/embeddedWorkerCli.ts
181649
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
181650
- import { basename as basename6, join as join32 } from "node:path";
181770
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
181771
+ import { basename as basename6, join as join33 } from "node:path";
181651
181772
  var embeddedCliPathEnv = "QAWOLF_EMBEDDED_CLI_PATH";
181652
181773
  function extractEmbeddedWorkerCli() {
181653
181774
  const assetPath = process.env[embeddedCliPathEnv];
181654
181775
  if (assetPath === undefined || assetPath === "")
181655
181776
  return;
181656
- const dir = join32(getDataDir(), "worker");
181657
- const dest = join32(dir, basename6(assetPath));
181777
+ const dir = join33(getDataDir(), "worker");
181778
+ const dest = join33(dir, basename6(assetPath));
181658
181779
  if (existsSync3(dest))
181659
181780
  return dest;
181660
- mkdirSync2(dir, { recursive: true });
181781
+ mkdirSync3(dir, { recursive: true });
181661
181782
  writeFileSync2(dest, readFileSync2(assetPath));
181662
181783
  return dest;
181663
181784
  }
@@ -181807,7 +181928,7 @@ function makePooledDispatch(resolvedDir) {
181807
181928
  }
181808
181929
 
181809
181930
  // src/commands/flows/buildRunReporter.ts
181810
- import { dirname as dirname11 } from "node:path";
181931
+ import { dirname as dirname12 } from "node:path";
181811
181932
 
181812
181933
  // src/shell/reporter/createConsoleReporter.ts
181813
181934
  import { styleText as styleText3 } from "node:util";
@@ -181957,7 +182078,7 @@ function createCompositeReporter(reporters) {
181957
182078
  };
181958
182079
  }
181959
182080
  // src/shell/reporter/createJUnitReporter.ts
181960
- import { join as join33 } from "node:path";
182081
+ import { join as join34 } from "node:path";
181961
182082
 
181962
182083
  // src/shell/reporter/junitXml.ts
181963
182084
  function escapeXml(str) {
@@ -181996,7 +182117,7 @@ ${suites}`;
181996
182117
 
181997
182118
  // src/shell/reporter/createJUnitReporter.ts
181998
182119
  function resolveJUnitOutputPath(junit, outputDir) {
181999
- return typeof junit === "string" && junit.trim() !== "" ? junit : join33(outputDir, "junit-report.xml");
182120
+ return typeof junit === "string" && junit.trim() !== "" ? junit : join34(outputDir, "junit-report.xml");
182000
182121
  }
182001
182122
  function createJUnitReporter(deps) {
182002
182123
  const flows = [];
@@ -182035,7 +182156,7 @@ function buildRunReporter(flags, deps) {
182035
182156
  const junit = createJUnitReporter({
182036
182157
  outputPath: resolveJUnitOutputPath(flags.junit, flags.outputDir),
182037
182158
  writeFile: (path9, content) => {
182038
- deps.fs.mkdirSync(dirname11(path9), { recursive: true });
182159
+ deps.fs.mkdirSync(dirname12(path9), { recursive: true });
182039
182160
  deps.fs.writeFileSync(path9, content);
182040
182161
  },
182041
182162
  stderr
@@ -182083,11 +182204,11 @@ function buildFlowsRunDeps(args) {
182083
182204
 
182084
182205
  // src/commands/flows/loadEnvFile.ts
182085
182206
  import { readFile } from "node:fs/promises";
182086
- import { join as join34 } from "node:path";
182207
+ import { join as join35 } from "node:path";
182087
182208
  async function loadEnvFile(envDir) {
182088
182209
  let content;
182089
182210
  try {
182090
- content = await readFile(join34(envDir, ".env"), "utf8");
182211
+ content = await readFile(join35(envDir, ".env"), "utf8");
182091
182212
  } catch (err) {
182092
182213
  if (isNoEntError(err))
182093
182214
  return;
@@ -182187,7 +182308,7 @@ async function handleFlowsRun(ctx, pattern, flags, deps) {
182187
182308
  }
182188
182309
 
182189
182310
  // src/commands/flows/hybridRunDefaults.ts
182190
- import { join as join35, resolve as resolve9 } from "node:path";
182311
+ import { join as join36, resolve as resolve9 } from "node:path";
182191
182312
  function makeDefaultHybridDeps(fs9) {
182192
182313
  return {
182193
182314
  expandPatterns: (patterns, cwd, logger) => expandPatterns(patterns, cwd, logger, fs9),
@@ -182206,7 +182327,7 @@ async function handleHybridFlowsRun(ctx, pattern, flags, deps) {
182206
182327
  if (validation !== "ok") {
182207
182328
  return { error: validation.error, exitCode: 2 };
182208
182329
  }
182209
- const envDir = resolve9(join35(".qawolf", flags.env));
182330
+ const envDir = resolve9(join36(".qawolf", flags.env));
182210
182331
  const patternArgs = buildPatternArgs(pattern);
182211
182332
  const globFlows = () => resolvedDeps.expandPatterns(patternArgs, envDir, ctx.log("flows"));
182212
182333
  let files = await globFlows();
@@ -182280,7 +182401,7 @@ async function runWorker(signals) {
182280
182401
  throw new Error("worker subprocess currently supports web flows only");
182281
182402
  await configureTestkit(input.resolvedDir);
182282
182403
  const fs9 = makeDefaultFs();
182283
- const apiBaseUrl = process.env["QAWOLF_API_URL"]?.replace(/\/+$/, "") || "https://app.qawolf.com";
182404
+ const apiBaseUrl = resolveHostUrl(process.env);
182284
182405
  const flowRuntimeDeps = await createFlowRuntimeDeps({
182285
182406
  envDir: input.resolvedDir,
182286
182407
  ctx: { apiBaseUrl, configDir: getConfigDir(), fs: fs9 }
@@ -182321,8 +182442,8 @@ var listExamples = `
182321
182442
  Examples:
182322
182443
  $ qawolf flows list
182323
182444
  $ qawolf flows list "flows/checkout/**"
182324
- $ qawolf flows list --remote
182325
- $ qawolf flows list "**/checkout/**" --remote`;
182445
+ $ qawolf flows list --remote --env staging
182446
+ $ qawolf flows list "**/checkout/**" --remote --env staging --include-drafts`;
182326
182447
  var pullExamples = `
182327
182448
  Examples:
182328
182449
  $ qawolf flows pull --env staging
@@ -182334,9 +182455,23 @@ function registerFlowsCommand(program2, signals) {
182334
182455
  registerRunWorkerCommand(flows, signals);
182335
182456
  declareCommandKind(flows.command("list [pattern]"), "local", {
182336
182457
  kindNote: "read with --remote"
182337
- }).description("List flows matching [pattern] from the local project, or from QA Wolf with --remote").option("--remote", "List flows from the QA Wolf platform instead of the local project", false).addHelpText("after", listExamples).action((pattern, opts, command) => {
182458
+ }).description("List flows matching [pattern] from the local project, or from a QA Wolf environment with --remote").option("--remote", "List flows from the QA Wolf platform instead of the local project", false).option("--env <env>", "Environment to list flows from (required with --remote)").option("--include-drafts", "Include draft flows in the listing (requires --remote)", false).addHelpText("after", listExamples).action((pattern, opts, command) => {
182338
182459
  if (opts.remote) {
182339
- return withAuthContext(signals, (ctx) => flowsListRemote(ctx, pattern))(opts, command);
182460
+ const env3 = opts.env;
182461
+ if (env3 === undefined) {
182462
+ return withContext(signals, async () => ({
182463
+ error: flowsMessages.list.remoteRequiresEnv
182464
+ }))(opts, command);
182465
+ }
182466
+ return withAuthContext(signals, (ctx) => flowsListRemote(ctx, pattern, {
182467
+ env: env3,
182468
+ includeDrafts: opts.includeDrafts
182469
+ }))(opts, command);
182470
+ }
182471
+ if (opts.env !== undefined || opts.includeDrafts) {
182472
+ return withContext(signals, async () => ({
182473
+ error: flowsMessages.list.flagsRequireRemote
182474
+ }))(opts, command);
182340
182475
  }
182341
182476
  return withContext(signals, (ctx) => handleFlowsList(ctx, pattern))(opts, command);
182342
182477
  });
@@ -182346,7 +182481,7 @@ function registerFlowsCommand(program2, signals) {
182346
182481
  }
182347
182482
 
182348
182483
  // src/domains/init/init.ts
182349
- import { dirname as dirname12, join as join36, relative as relative7 } from "node:path";
182484
+ import { dirname as dirname13, join as join37, relative as relative7 } from "node:path";
182350
182485
 
182351
182486
  // src/domains/init/templates.ts
182352
182487
  var qawolfConfigTs = `export default {
@@ -182384,9 +182519,9 @@ function makeDefaultInitDeps() {
182384
182519
  async function handleInit(ctx, opts, deps = makeDefaultInitDeps()) {
182385
182520
  ctx.ui.gap();
182386
182521
  ctx.ui.intro(initMessages.title);
182387
- const configPath = join36(deps.cwd, "qawolf.config.ts");
182388
- const flowPath = join36(deps.cwd, "src", "flows", "example.flow.ts");
182389
- const gitignorePath = join36(deps.cwd, ".qawolf", ".gitignore");
182522
+ const configPath = join37(deps.cwd, "qawolf.config.ts");
182523
+ const flowPath = join37(deps.cwd, "src", "flows", "example.flow.ts");
182524
+ const gitignorePath = join37(deps.cwd, ".qawolf", ".gitignore");
182390
182525
  await writeWithPrompt(ctx, configPath, qawolfConfigTs, opts.yes, deps);
182391
182526
  await writeWithPrompt(ctx, flowPath, exampleFlowTs, opts.yes, deps);
182392
182527
  await writeWithPrompt(ctx, gitignorePath, qawolfGitignore, opts.yes, deps);
@@ -182405,12 +182540,12 @@ async function writeWithPrompt(ctx, filePath, content, yes, deps) {
182405
182540
  return;
182406
182541
  }
182407
182542
  }
182408
- await deps.fs.mkdir(dirname12(filePath), { recursive: true });
182543
+ await deps.fs.mkdir(dirname13(filePath), { recursive: true });
182409
182544
  await deps.fs.writeFile(filePath, content);
182410
182545
  ctx.ui.step(initMessages.createdFile(relPath));
182411
182546
  }
182412
182547
  async function ensurePackageJson(ctx, yes, deps) {
182413
- const pkgPath = join36(deps.cwd, "package.json");
182548
+ const pkgPath = join37(deps.cwd, "package.json");
182414
182549
  if (!await deps.fs.pathExists(pkgPath)) {
182415
182550
  const confirmed2 = await ctx.ui.confirm(initMessages.createPackageJsonPrompt, { yes });
182416
182551
  if (!confirmed2.ok || !confirmed2.value) {
@@ -182469,19 +182604,19 @@ Examples:
182469
182604
  }
182470
182605
 
182471
182606
  // src/commands/install/android.ts
182472
- import { join as join39 } from "node:path";
182607
+ import { join as join40 } from "node:path";
182473
182608
 
182474
182609
  // src/domains/install/android/index.ts
182475
182610
  import { parseExecutionTarget as parseExecutionTarget4 } from "@qawolf/flow-targets";
182476
182611
 
182477
182612
  // src/domains/install/android/avd.ts
182478
- import { join as join37 } from "node:path";
182613
+ import { join as join38 } from "node:path";
182479
182614
  async function installAvds(ctx, specs, deps) {
182480
182615
  const versionResult = await deps.spawn(deps.sdkManagerPath, ["--version"]);
182481
182616
  if (versionResult.exitCode !== 0) {
182482
182617
  throw new Error(installMessages.android.sdkmanagerNotFound(deps.sdkManagerPath));
182483
182618
  }
182484
- const licenseFile = join37(deps.androidHome, "licenses", "android-sdk-license");
182619
+ const licenseFile = join38(deps.androidHome, "licenses", "android-sdk-license");
182485
182620
  if (deps.checkExists(licenseFile)) {
182486
182621
  ctx.ui.info(installMessages.android.licensesAlreadyAccepted);
182487
182622
  } else {
@@ -182495,7 +182630,7 @@ async function installAvds(ctx, specs, deps) {
182495
182630
  for (const spec of specs) {
182496
182631
  if (installedImages.has(spec.systemImage))
182497
182632
  continue;
182498
- const systemImg = join37(deps.androidHome, ...spec.systemImage.split(";"), "system.img");
182633
+ const systemImg = join38(deps.androidHome, ...spec.systemImage.split(";"), "system.img");
182499
182634
  if (deps.checkExists(systemImg)) {
182500
182635
  ctx.ui.info(installMessages.android.systemImageAlreadyInstalled(spec.systemImage));
182501
182636
  installedImages.add(spec.systemImage);
@@ -182543,9 +182678,9 @@ async function installAvds(ctx, specs, deps) {
182543
182678
  }
182544
182679
 
182545
182680
  // src/domains/install/android/driver.ts
182546
- import { join as join38 } from "node:path";
182681
+ import { join as join39 } from "node:path";
182547
182682
  var appiumEnv = {
182548
- APPIUM_HOME: join38(envPaths("qawolf").data, "appium")
182683
+ APPIUM_HOME: join39(envPaths("qawolf").data, "appium")
182549
182684
  };
182550
182685
  async function installUiautomator2Driver(ctx, deps) {
182551
182686
  const listResult = await deps.spawn(deps.appiumBinPath, ["driver", "list", "--installed"], { env: appiumEnv });
@@ -182632,8 +182767,8 @@ async function handleInstallAndroid(ctx, pattern, envDir) {
182632
182767
  arch: process.arch,
182633
182768
  androidHome: androidHome2,
182634
182769
  checkExists: (path9) => fs9.existsSync(path9),
182635
- sdkManagerPath: join39(androidHome2, "cmdline-tools", "latest", "bin", "sdkmanager"),
182636
- avdManagerPath: join39(androidHome2, "cmdline-tools", "latest", "bin", "avdmanager"),
182770
+ sdkManagerPath: join40(androidHome2, "cmdline-tools", "latest", "bin", "sdkmanager"),
182771
+ avdManagerPath: join40(androidHome2, "cmdline-tools", "latest", "bin", "avdmanager"),
182637
182772
  expandPatterns: (patterns, cwd) => expandPatterns(patterns, cwd ?? process.cwd(), undefined, fs9),
182638
182773
  peekFlowMeta: makePeekFlowMeta(fs9),
182639
182774
  resolveDepsRoot: async (files) => envDir ?? (await resolveDepsRoot({ files, fs: fs9 })).depsRoot,
@@ -182779,39 +182914,6 @@ Examples:
182779
182914
  });
182780
182915
  }
182781
182916
 
182782
- // node_modules/@qawolf/api-contracts/dist/v1/ids.js
182783
- var defaultIdSchemas = {
182784
- environment: exports_external.string().min(1).describe("The id of the environment."),
182785
- flow: exports_external.string().min(1).describe("The id of the flow."),
182786
- run: exports_external.string().min(1).describe("The id of the run.")
182787
- };
182788
-
182789
- // node_modules/@qawolf/api-contracts/dist/v1/run/index.js
182790
- var makeCreateRunContract = (ids) => {
182791
- const input = exports_external.object({
182792
- environmentId: ids.environment,
182793
- environmentVariables: exports_external.record(exports_external.string(), exports_external.string()).optional(),
182794
- flowIds: exports_external.array(ids.flow).min(1),
182795
- ignoreRules: exports_external.boolean().default(false)
182796
- });
182797
- const output = exports_external.object({ runId: ids.run });
182798
- return {
182799
- description: "Create a run for selected flows in an environment.",
182800
- input,
182801
- kind: "write",
182802
- name: "run.create",
182803
- output
182804
- };
182805
- };
182806
-
182807
- // node_modules/@qawolf/api-contracts/dist/v1/index.js
182808
- var makeContractsV1 = (ids) => ({
182809
- run: {
182810
- create: makeCreateRunContract(ids)
182811
- }
182812
- });
182813
- var publicContractsV1 = makeContractsV1(defaultIdSchemas);
182814
-
182815
182917
  // src/core/publicApi/flagSpecs.ts
182816
182918
  var kebabCase = (field) => field.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
182817
182919
  function flagKind(schema) {
@@ -182908,8 +183010,16 @@ function buildSpec(contract, path9) {
182908
183010
  contract
182909
183011
  };
182910
183012
  }
182911
- function buildCommandSpecs(tree) {
182912
- const walk3 = (node2, path9) => Object.entries(node2).flatMap(([key, value]) => isContract(value) ? [buildSpec(value, [...path9, key])] : walk3(value, [...path9, key]));
183013
+ function buildCommandSpecs(tree, options = {}) {
183014
+ const skip = options.skipContractNames;
183015
+ const walk3 = (node2, path9) => Object.entries(node2).flatMap(([key, value]) => {
183016
+ const childPath = [...path9, key];
183017
+ if (!isContract(value))
183018
+ return walk3(value, childPath);
183019
+ if (skip?.has(childPath.join(".")))
183020
+ return [];
183021
+ return [buildSpec(value, childPath)];
183022
+ });
182913
183023
  return walk3(tree, []);
182914
183024
  }
182915
183025
 
@@ -182980,6 +183090,7 @@ async function handlePublicApiCommand(ctx, spec, options) {
182980
183090
  var groupDescriptions = {
182981
183091
  run: "Trigger and manage QA Wolf runs on the platform"
182982
183092
  };
183093
+ var handWrittenContractNames = new Set(["flow.list"]);
182983
183094
  function resolveGroup(parent2, segment) {
182984
183095
  const existing = parent2.commands.find((command) => command.name() === segment);
182985
183096
  return existing ?? parent2.command(segment).description(groupDescriptions[segment] ?? `QA Wolf public API ${segment} commands`);
@@ -183005,7 +183116,9 @@ function registerSpec(program2, signals, spec, authDeps) {
183005
183116
  command.action((options, leaf) => withAuthContext(signals, (ctx) => handlePublicApiCommand(ctx, spec, options), authDeps ?? {})(options, leaf));
183006
183117
  }
183007
183118
  function registerPublicApiCommands(program2, signals, options = {}) {
183008
- const specs = buildCommandSpecs(options.contracts ?? publicContractsV1);
183119
+ const specs = buildCommandSpecs(options.contracts ?? publicContractsV1, {
183120
+ skipContractNames: handWrittenContractNames
183121
+ });
183009
183122
  for (const spec of specs) {
183010
183123
  registerSpec(program2, signals, spec, options.authDeps);
183011
183124
  }
@@ -183046,4 +183159,4 @@ createProgram({ signals }).parseAsync().catch(() => {
183046
183159
  process.exitCode = 1;
183047
183160
  }).finally(() => flushAndExit(typeof process.exitCode === "number" ? process.exitCode : 0));
183048
183161
 
183049
- //# debugId=2C8E0221ECEF946764756E2164756E21
183162
+ //# debugId=724E44C8F9B9A9E264756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qawolf/cli",
3
- "version": "1.2.0",
3
+ "version": "1.3.2",
4
4
  "description": "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
5
5
  "keywords": [
6
6
  "automation",
@@ -60,16 +60,17 @@
60
60
  "@napi-rs/keyring": "1.3.0",
61
61
  "@oxc-node/core": "0.1.0",
62
62
  "@playwright/test": "1.60.0",
63
- "@qawolf/api-contracts": "0.1.0",
63
+ "@qawolf/api-contracts": "0.2.0",
64
64
  "@qawolf/emails": "1.1.1",
65
65
  "@qawolf/flow-targets": "1.0.0",
66
- "@qawolf/flows": "0.1.1",
66
+ "@qawolf/flows": "0.1.4",
67
67
  "@qawolf/testkit": "1.1.1",
68
68
  "appium": "2.11.3",
69
69
  "appium-uiautomator2-driver": "3.7.0",
70
70
  "appium-xcuitest-driver": "9.10.5",
71
71
  "commander": "14.0.3",
72
72
  "env-paths": "4.0.0",
73
+ "expect-webdriverio": "5.6.5",
73
74
  "picomatch": "4.0.4",
74
75
  "pino": "10.3.1",
75
76
  "playwright": "1.58.2",
@@ -19,6 +19,11 @@ environment variable (or stored credentials from `qawolf auth login`).
19
19
  their table entry notes a flag that switches them to `read`.
20
20
  Verify with `qawolf auth whoami`. Never print or log the key.
21
21
 
22
+ Commands use `https://app.qawolf.com` by default. Set `QAWOLF_HOST_URL` to
23
+ target another deployment host, for example
24
+ `https://app.staging.example.com`. `QAWOLF_API_URL` is a separate API endpoint
25
+ and does not select the deployment host used by CLI commands.
26
+
22
27
  ## Output
23
28
 
24
29
  When consuming output programmatically, always pass `--json` (or `--agent`).
@@ -41,7 +46,7 @@ write on timeout: it may have reached the server the first time.
41
46
  | `qawolf auth logout` | local | Remove stored credentials |
42
47
  | `qawolf auth whoami` | read | Show authentication status |
43
48
  | `qawolf doctor` | local | Diagnose problems running flows locally |
44
- | `qawolf flows list` | local (read with --remote) | List flows matching [pattern] from the local project, or from QA Wolf with --remote |
49
+ | `qawolf flows list` | local (read with --remote) | List flows matching [pattern] from the local project, or from a QA Wolf environment with --remote |
45
50
  | `qawolf flows pull` | read | Download an environment's flows into the local .qawolf/<env>/ cache |
46
51
  | `qawolf flows run` | local (read with --env) | Run flows matching [pattern], or every flow when omitted; with --env, pull missing flows from that QA Wolf environment |
47
52
  | `qawolf init` | local | Scaffold a QA Wolf project in the current directory |