@qawolf/cli 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +479 -189
- package/package.json +4 -3
- package/skills/qawolf-cli/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Try it without installing:
|
|
|
23
23
|
npx @qawolf/cli --help
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Supported Node versions:
|
|
26
|
+
Supported Node versions: Node 20.19+. Node 20 reached [end-of-life](https://endoflife.date/nodejs) on 2026-04-30 and no longer receives security updates; it remains supported here only for environments still pinned to Node 20. Prefer Node 22+ where possible.
|
|
27
27
|
|
|
28
28
|
### Standalone binaries
|
|
29
29
|
|
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
|
|
8517
|
+
BufferList.prototype.join = function join32(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
|
|
23073
|
+
BufferList.prototype.join = function join32(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:
|
|
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(
|
|
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
|
|
@@ -155130,6 +155130,15 @@ function errorCode(err) {
|
|
|
155130
155130
|
function isNoEntError(err) {
|
|
155131
155131
|
return errorCode(err) === "ENOENT";
|
|
155132
155132
|
}
|
|
155133
|
+
var missingPackagePattern = /Cannot find (?:package|module) '([^']+)'/;
|
|
155134
|
+
var pathLikeSpecifierPattern = /^(?:\.|\/|[A-Za-z]:[\\/])/;
|
|
155135
|
+
function extractMissingPackage(text) {
|
|
155136
|
+
const specifier = missingPackagePattern.exec(text)?.[1];
|
|
155137
|
+
if (specifier === undefined || pathLikeSpecifierPattern.test(specifier)) {
|
|
155138
|
+
return;
|
|
155139
|
+
}
|
|
155140
|
+
return specifier;
|
|
155141
|
+
}
|
|
155133
155142
|
|
|
155134
155143
|
// src/core/messages/auth.ts
|
|
155135
155144
|
var authMessages = {
|
|
@@ -155264,6 +155273,10 @@ var flowsMessages = {
|
|
|
155264
155273
|
title: "Flows",
|
|
155265
155274
|
remoteTitle: "Remote Flows",
|
|
155266
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
|
+
},
|
|
155267
155280
|
pull: {
|
|
155268
155281
|
downloadingBundle: "Downloading flows bundle",
|
|
155269
155282
|
fetchingEnvVars: "Fetching environment variables",
|
|
@@ -155391,7 +155404,10 @@ var runnerMessages = {
|
|
|
155391
155404
|
retrying: (attempt, maxAttempts) => `Retrying (${attempt} of ${maxAttempts})...`,
|
|
155392
155405
|
screenshot: (path) => `Screenshot: ${path}`,
|
|
155393
155406
|
managedRuntimeNote: (dir) => `Using managed runtime — override with --deps <dir> or QAWOLF_RUNTIME_DIR:
|
|
155394
|
-
${dir}
|
|
155407
|
+
${dir}`,
|
|
155408
|
+
installingProjectDeps: (count) => `Installing ${pluralize(count, "project dependency", "project dependencies")}…`,
|
|
155409
|
+
outerHopCandidateRejected: (dir, missing) => `outer-hop candidate rejected: ${dir} missing ${missing.join(", ")}`,
|
|
155410
|
+
moduleNotFoundHint: (pkg, projectDir) => projectDir === undefined ? `Hint: '${pkg}' could not be resolved. Run from within your flows project so its dependencies can be found.` : `Hint: '${pkg}' could not be resolved. Ensure it is declared in ${projectDir}/package.json "dependencies" and run npm install in that project.`
|
|
155395
155411
|
};
|
|
155396
155412
|
// node_modules/env-paths/index.js
|
|
155397
155413
|
import path from "node:path";
|
|
@@ -170010,7 +170026,8 @@ async function validateApiKey(deps) {
|
|
|
170010
170026
|
}
|
|
170011
170027
|
// src/shell/logger.ts
|
|
170012
170028
|
var import_pino = __toESM(require_pino(), 1);
|
|
170013
|
-
import {
|
|
170029
|
+
import { mkdirSync as mkdirSync2, openSync } from "node:fs";
|
|
170030
|
+
import { dirname, join as join4 } from "node:path";
|
|
170014
170031
|
function defaultLogPath() {
|
|
170015
170032
|
return join4(envPaths("qawolf", { suffix: "" }).log, "cli.log");
|
|
170016
170033
|
}
|
|
@@ -170024,7 +170041,8 @@ function resolveStderrLevel(env2, verbose) {
|
|
|
170024
170041
|
}
|
|
170025
170042
|
function createLoggingSystem(opts) {
|
|
170026
170043
|
const { stderrLevel, logPath } = opts;
|
|
170027
|
-
|
|
170044
|
+
mkdirSync2(dirname(logPath), { recursive: true });
|
|
170045
|
+
const dest = import_pino.default.destination({ fd: openSync(logPath, "a"), sync: false });
|
|
170028
170046
|
const fileLogger = import_pino.default({ level: "debug" }, dest);
|
|
170029
170047
|
const levelNums = {
|
|
170030
170048
|
trace: 10,
|
|
@@ -171146,45 +171164,6 @@ async function getIdentity(apiKey, deps) {
|
|
|
171146
171164
|
return { ok: true, data: parsed.data };
|
|
171147
171165
|
}
|
|
171148
171166
|
|
|
171149
|
-
// src/shell/platform/getRemoteFlows.ts
|
|
171150
|
-
var remoteFlowSchema = exports_external.object({
|
|
171151
|
-
executionTarget: exports_external.union([exports_external.string(), exports_external.record(exports_external.string(), exports_external.unknown())]),
|
|
171152
|
-
id: exports_external.string(),
|
|
171153
|
-
name: exports_external.string(),
|
|
171154
|
-
path: exports_external.string(),
|
|
171155
|
-
tags: exports_external.array(exports_external.string())
|
|
171156
|
-
});
|
|
171157
|
-
var remoteFlowsResponseSchema = exports_external.object({
|
|
171158
|
-
flows: exports_external.array(remoteFlowSchema)
|
|
171159
|
-
});
|
|
171160
|
-
async function getRemoteFlows(apiKey, deps) {
|
|
171161
|
-
const url2 = `${deps.baseUrl}/api/v0/flows?includeDrafts=false`;
|
|
171162
|
-
let response;
|
|
171163
|
-
try {
|
|
171164
|
-
response = await deps.fetch(url2, {
|
|
171165
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
171166
|
-
signal: AbortSignal.timeout(15000)
|
|
171167
|
-
});
|
|
171168
|
-
} catch (error51) {
|
|
171169
|
-
return { ok: false, error: { kind: "network", cause: toError(error51) } };
|
|
171170
|
-
}
|
|
171171
|
-
if (!response.ok) {
|
|
171172
|
-
const body = await response.text().catch(() => "");
|
|
171173
|
-
return {
|
|
171174
|
-
ok: false,
|
|
171175
|
-
error: { kind: "http", status: response.status, body }
|
|
171176
|
-
};
|
|
171177
|
-
}
|
|
171178
|
-
const json2 = await response.json().catch(() => {
|
|
171179
|
-
return;
|
|
171180
|
-
});
|
|
171181
|
-
const parsed = remoteFlowsResponseSchema.safeParse(json2);
|
|
171182
|
-
if (!parsed.success) {
|
|
171183
|
-
return { ok: false, error: { kind: "parse", cause: parsed.error } };
|
|
171184
|
-
}
|
|
171185
|
-
return { ok: true, data: parsed.data };
|
|
171186
|
-
}
|
|
171187
|
-
|
|
171188
171167
|
// src/shell/platform/types.ts
|
|
171189
171168
|
var environmentListItemSchema = exports_external.object({
|
|
171190
171169
|
envId: exports_external.string(),
|
|
@@ -171238,10 +171217,10 @@ async function listTeamStorageFiles(trpc, args, deps) {
|
|
|
171238
171217
|
}
|
|
171239
171218
|
|
|
171240
171219
|
// src/shell/platform/teamStorageAssets.ts
|
|
171241
|
-
import { dirname as
|
|
171220
|
+
import { dirname as dirname3, join as join8 } from "node:path";
|
|
171242
171221
|
|
|
171243
171222
|
// src/shell/platform/teamStorageAssetManifest.ts
|
|
171244
|
-
import { dirname } from "node:path";
|
|
171223
|
+
import { dirname as dirname2 } from "node:path";
|
|
171245
171224
|
var assetManifestSchema = exports_external.object({
|
|
171246
171225
|
version: exports_external.literal(1),
|
|
171247
171226
|
files: exports_external.array(exports_external.object({
|
|
@@ -171285,7 +171264,7 @@ async function writeAssetManifest(assetsAbs, files, fs2) {
|
|
|
171285
171264
|
}))
|
|
171286
171265
|
};
|
|
171287
171266
|
const path2 = assetManifestPath(assetsAbs);
|
|
171288
|
-
await fs2.mkdir(
|
|
171267
|
+
await fs2.mkdir(dirname2(path2), { recursive: true });
|
|
171289
171268
|
await fs2.writeFile(path2, `${JSON.stringify(manifest, undefined, 2)}
|
|
171290
171269
|
`);
|
|
171291
171270
|
}
|
|
@@ -171451,7 +171430,7 @@ async function writeAssetSnapshot(args) {
|
|
|
171451
171430
|
await args.deps.fs.mkdir(args.tmpAssets, { recursive: true });
|
|
171452
171431
|
for (const { file: file2, relativePath } of args.safeFiles) {
|
|
171453
171432
|
const dest = join8(args.tmpAssets, relativePath);
|
|
171454
|
-
await args.deps.fs.mkdir(
|
|
171433
|
+
await args.deps.fs.mkdir(dirname3(dest), { recursive: true });
|
|
171455
171434
|
if (args.reusable.has(relativePath)) {
|
|
171456
171435
|
await args.deps.fs.copyFile(join8(args.assetsAbs, relativePath), dest);
|
|
171457
171436
|
continue;
|
|
@@ -171490,14 +171469,6 @@ function createPlatformClient(apiKey, deps) {
|
|
|
171490
171469
|
sleep: deps.sleep
|
|
171491
171470
|
});
|
|
171492
171471
|
},
|
|
171493
|
-
async getRemoteFlows() {
|
|
171494
|
-
return requestWithRetry({
|
|
171495
|
-
call: () => getRemoteFlows(apiKey, deps),
|
|
171496
|
-
backoffMs: requestBackoffMs2,
|
|
171497
|
-
describe: (err) => describeRequestError(err, deps.baseUrl, "flows"),
|
|
171498
|
-
sleep: deps.sleep
|
|
171499
|
-
});
|
|
171500
|
-
},
|
|
171501
171472
|
getFlowsBundleUrl: getFlowsBundleUrlImpl,
|
|
171502
171473
|
callPublicApi: makeCallPublicApiMethod(trpc, deps, requestBackoffMs2),
|
|
171503
171474
|
async getEnvVars(envId) {
|
|
@@ -173618,7 +173589,7 @@ function registerAuthCommand(program2, signals) {
|
|
|
173618
173589
|
// package.json
|
|
173619
173590
|
var package_default = {
|
|
173620
173591
|
name: "@qawolf/cli",
|
|
173621
|
-
version: "1.
|
|
173592
|
+
version: "1.3.0",
|
|
173622
173593
|
description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
173623
173594
|
keywords: [
|
|
173624
173595
|
"automation",
|
|
@@ -173676,8 +173647,9 @@ var package_default = {
|
|
|
173676
173647
|
dependencies: {
|
|
173677
173648
|
"@clack/prompts": "1.5.1",
|
|
173678
173649
|
"@napi-rs/keyring": "1.3.0",
|
|
173650
|
+
"@oxc-node/core": "0.1.0",
|
|
173679
173651
|
"@playwright/test": "1.60.0",
|
|
173680
|
-
"@qawolf/api-contracts": "0.
|
|
173652
|
+
"@qawolf/api-contracts": "0.2.0",
|
|
173681
173653
|
"@qawolf/emails": "1.1.1",
|
|
173682
173654
|
"@qawolf/flow-targets": "1.0.0",
|
|
173683
173655
|
"@qawolf/flows": "0.1.1",
|
|
@@ -173709,7 +173681,7 @@ var package_default = {
|
|
|
173709
173681
|
typescript: "6.0.3"
|
|
173710
173682
|
},
|
|
173711
173683
|
engines: {
|
|
173712
|
-
node: ">=
|
|
173684
|
+
node: ">=20.19.0"
|
|
173713
173685
|
},
|
|
173714
173686
|
packageManager: "bun@1.3.13"
|
|
173715
173687
|
};
|
|
@@ -174035,15 +174007,15 @@ async function checkFileAssets(deps) {
|
|
|
174035
174007
|
|
|
174036
174008
|
// src/domains/doctor/checks/nodeVersion.ts
|
|
174037
174009
|
async function checkNodeVersion(deps) {
|
|
174038
|
-
const
|
|
174039
|
-
if (
|
|
174010
|
+
const min = parseVersion(deps.enginesNode, /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
|
|
174011
|
+
if (min === undefined) {
|
|
174040
174012
|
return {
|
|
174041
174013
|
name: "node-version",
|
|
174042
174014
|
status: "fail",
|
|
174043
174015
|
detail: doctorMessages.nodeVersion.couldNotParseEngines(deps.enginesNode)
|
|
174044
174016
|
};
|
|
174045
174017
|
}
|
|
174046
|
-
const actual =
|
|
174018
|
+
const actual = parseVersion(deps.processVersion, /^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
|
|
174047
174019
|
if (actual === undefined) {
|
|
174048
174020
|
return {
|
|
174049
174021
|
name: "node-version",
|
|
@@ -174051,7 +174023,7 @@ async function checkNodeVersion(deps) {
|
|
|
174051
174023
|
detail: doctorMessages.nodeVersion.couldNotParseVersion(deps.processVersion)
|
|
174052
174024
|
};
|
|
174053
174025
|
}
|
|
174054
|
-
if (actual <
|
|
174026
|
+
if (compareVersions(actual, min) < 0) {
|
|
174055
174027
|
return {
|
|
174056
174028
|
name: "node-version",
|
|
174057
174029
|
status: "fail",
|
|
@@ -174065,9 +174037,18 @@ async function checkNodeVersion(deps) {
|
|
|
174065
174037
|
version: deps.processVersion.replace(/^v/, "")
|
|
174066
174038
|
};
|
|
174067
174039
|
}
|
|
174068
|
-
function
|
|
174069
|
-
const
|
|
174070
|
-
|
|
174040
|
+
function parseVersion(input, pattern) {
|
|
174041
|
+
const match = input.trim().match(pattern);
|
|
174042
|
+
if (match?.[1] === undefined)
|
|
174043
|
+
return;
|
|
174044
|
+
return [
|
|
174045
|
+
Number.parseInt(match[1], 10),
|
|
174046
|
+
match[2] === undefined ? 0 : Number.parseInt(match[2], 10),
|
|
174047
|
+
match[3] === undefined ? 0 : Number.parseInt(match[3], 10)
|
|
174048
|
+
];
|
|
174049
|
+
}
|
|
174050
|
+
function compareVersions(a2, b2) {
|
|
174051
|
+
return a2[0] - b2[0] || a2[1] - b2[1] || a2[2] - b2[2];
|
|
174071
174052
|
}
|
|
174072
174053
|
|
|
174073
174054
|
// src/domains/doctor/checks/npmRegistry.ts
|
|
@@ -174195,13 +174176,13 @@ function renderResults(ui, results) {
|
|
|
174195
174176
|
}
|
|
174196
174177
|
|
|
174197
174178
|
// src/domains/flows/ensureDeps.ts
|
|
174198
|
-
import { dirname as
|
|
174179
|
+
import { dirname as dirname4, join as join10 } from "node:path";
|
|
174199
174180
|
function findEnvDir(flowPath, fs2) {
|
|
174200
|
-
let dir =
|
|
174181
|
+
let dir = dirname4(flowPath);
|
|
174201
174182
|
while (true) {
|
|
174202
174183
|
if (fs2.existsSync(join10(dir, "package.json")))
|
|
174203
174184
|
return dir;
|
|
174204
|
-
const parent =
|
|
174185
|
+
const parent = dirname4(dir);
|
|
174205
174186
|
if (parent === dir)
|
|
174206
174187
|
return;
|
|
174207
174188
|
dir = parent;
|
|
@@ -174234,7 +174215,7 @@ import { fileURLToPath } from "url";
|
|
|
174234
174215
|
|
|
174235
174216
|
// node_modules/fdir/dist/index.mjs
|
|
174236
174217
|
import { createRequire as createRequire2 } from "module";
|
|
174237
|
-
import { basename, dirname as
|
|
174218
|
+
import { basename, dirname as dirname5, normalize as normalize2, relative as relative2, resolve, sep as sep3 } from "path";
|
|
174238
174219
|
import * as nativeFs from "fs";
|
|
174239
174220
|
var __require2 = /* @__PURE__ */ createRequire2(import.meta.url);
|
|
174240
174221
|
function cleanPath(path2) {
|
|
@@ -174398,7 +174379,7 @@ function build$2(options, isSynchronous) {
|
|
|
174398
174379
|
function isRecursive(path2, resolved, state) {
|
|
174399
174380
|
if (state.options.useRealPaths)
|
|
174400
174381
|
return isRecursiveUsingRealPaths(resolved, state);
|
|
174401
|
-
let parent =
|
|
174382
|
+
let parent = dirname5(path2);
|
|
174402
174383
|
let depth = 1;
|
|
174403
174384
|
while (parent !== state.root && depth < 2) {
|
|
174404
174385
|
const resolvedPath = state.symlinks.get(parent);
|
|
@@ -174406,7 +174387,7 @@ function isRecursive(path2, resolved, state) {
|
|
|
174406
174387
|
if (isSameRoot)
|
|
174407
174388
|
depth++;
|
|
174408
174389
|
else
|
|
174409
|
-
parent =
|
|
174390
|
+
parent = dirname5(parent);
|
|
174410
174391
|
}
|
|
174411
174392
|
state.symlinks.set(path2, resolved);
|
|
174412
174393
|
return depth > 1;
|
|
@@ -174602,7 +174583,7 @@ var Walker = class {
|
|
|
174602
174583
|
} else {
|
|
174603
174584
|
resolvedPath = useRealPaths ? resolvedPath : path2;
|
|
174604
174585
|
const filename = basename(resolvedPath);
|
|
174605
|
-
const directoryPath$1 = normalizePath(
|
|
174586
|
+
const directoryPath$1 = normalizePath(dirname5(resolvedPath), this.state.options);
|
|
174606
174587
|
resolvedPath = this.joinPath(filename, directoryPath$1);
|
|
174607
174588
|
this.pushFile(resolvedPath, files, this.state.counts, filters);
|
|
174608
174589
|
}
|
|
@@ -175680,14 +175661,80 @@ function handleFlowsList(ctx, pattern) {
|
|
|
175680
175661
|
|
|
175681
175662
|
// src/domains/flows/listRemote.ts
|
|
175682
175663
|
var import_picomatch2 = __toESM(require_picomatch2(), 1);
|
|
175683
|
-
|
|
175684
|
-
|
|
175664
|
+
|
|
175665
|
+
// node_modules/@qawolf/api-contracts/dist/v1/ids.js
|
|
175666
|
+
var defaultIdSchemas = {
|
|
175667
|
+
environment: exports_external.string().min(1).describe("The id of the environment."),
|
|
175668
|
+
flow: exports_external.string().min(1).describe("The id of the flow."),
|
|
175669
|
+
run: exports_external.string().min(1).describe("The id of the run.")
|
|
175670
|
+
};
|
|
175671
|
+
|
|
175672
|
+
// node_modules/@qawolf/api-contracts/dist/v1/flow/index.js
|
|
175673
|
+
var makeListFlowsContract = (ids) => {
|
|
175674
|
+
const input = exports_external.object({
|
|
175675
|
+
environmentId: ids.environment,
|
|
175676
|
+
includeDrafts: exports_external.boolean().default(false).describe("Include draft flows in the listing.")
|
|
175677
|
+
});
|
|
175678
|
+
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.");
|
|
175679
|
+
const output = exports_external.object({
|
|
175680
|
+
flows: exports_external.array(exports_external.object({
|
|
175681
|
+
executionTarget,
|
|
175682
|
+
flowId: ids.flow,
|
|
175683
|
+
name: exports_external.string(),
|
|
175684
|
+
path: exports_external.string().describe("The flow file path, relative to the repo."),
|
|
175685
|
+
tags: exports_external.array(exports_external.string())
|
|
175686
|
+
}))
|
|
175687
|
+
});
|
|
175688
|
+
return {
|
|
175689
|
+
description: "List the flows of an environment at its latest reconciled commit.",
|
|
175690
|
+
input,
|
|
175691
|
+
kind: "read",
|
|
175692
|
+
name: "flow.list",
|
|
175693
|
+
output
|
|
175694
|
+
};
|
|
175695
|
+
};
|
|
175696
|
+
|
|
175697
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/index.js
|
|
175698
|
+
var makeCreateRunContract = (ids) => {
|
|
175699
|
+
const input = exports_external.object({
|
|
175700
|
+
environmentId: ids.environment,
|
|
175701
|
+
environmentVariables: exports_external.record(exports_external.string(), exports_external.string()).optional(),
|
|
175702
|
+
flowIds: exports_external.array(ids.flow).min(1),
|
|
175703
|
+
ignoreRules: exports_external.boolean().default(false)
|
|
175704
|
+
});
|
|
175705
|
+
const output = exports_external.object({ runId: ids.run });
|
|
175706
|
+
return {
|
|
175707
|
+
description: "Create a run for selected flows in an environment.",
|
|
175708
|
+
input,
|
|
175709
|
+
kind: "write",
|
|
175710
|
+
name: "run.create",
|
|
175711
|
+
output
|
|
175712
|
+
};
|
|
175713
|
+
};
|
|
175714
|
+
|
|
175715
|
+
// node_modules/@qawolf/api-contracts/dist/v1/index.js
|
|
175716
|
+
var makeContractsV1 = (ids) => ({
|
|
175717
|
+
flow: {
|
|
175718
|
+
list: makeListFlowsContract(ids)
|
|
175719
|
+
},
|
|
175720
|
+
run: {
|
|
175721
|
+
create: makeCreateRunContract(ids)
|
|
175722
|
+
}
|
|
175723
|
+
});
|
|
175724
|
+
var publicContractsV1 = makeContractsV1(defaultIdSchemas);
|
|
175725
|
+
|
|
175726
|
+
// src/domains/flows/listRemote.ts
|
|
175727
|
+
async function flowsListRemote(ctx, pattern, options) {
|
|
175728
|
+
const result = await ctx.platform.callPublicApi(publicContractsV1.flow.list, {
|
|
175729
|
+
environmentId: options.env,
|
|
175730
|
+
includeDrafts: options.includeDrafts
|
|
175731
|
+
});
|
|
175685
175732
|
if (!result.ok) {
|
|
175686
175733
|
return { error: result.error };
|
|
175687
175734
|
}
|
|
175688
175735
|
const matches = pattern ? import_picomatch2.default(pattern) : undefined;
|
|
175689
175736
|
const items = result.value.flows.filter((f2) => !matches || matches(f2.path)).map((f2) => ({
|
|
175690
|
-
|
|
175737
|
+
flowId: f2.flowId,
|
|
175691
175738
|
file: f2.path,
|
|
175692
175739
|
name: f2.name,
|
|
175693
175740
|
tags: f2.tags,
|
|
@@ -175717,7 +175764,7 @@ async function flowsListRemote(ctx, pattern) {
|
|
|
175717
175764
|
}
|
|
175718
175765
|
|
|
175719
175766
|
// src/domains/flows/pull/handler.ts
|
|
175720
|
-
import { dirname as
|
|
175767
|
+
import { dirname as dirname7, join as join23, resolve as resolve7 } from "node:path";
|
|
175721
175768
|
|
|
175722
175769
|
// src/shell/manifest/io.ts
|
|
175723
175770
|
import { createHash as createHash2 } from "node:crypto";
|
|
@@ -176108,7 +176155,7 @@ async function writeEnvFile(envDir, vars, fs2 = makeDefaultFs()) {
|
|
|
176108
176155
|
}
|
|
176109
176156
|
|
|
176110
176157
|
// src/domains/flows/pull/extract.ts
|
|
176111
|
-
import { dirname as
|
|
176158
|
+
import { dirname as dirname6, resolve as resolve6 } from "node:path";
|
|
176112
176159
|
import { createGunzip } from "node:zlib";
|
|
176113
176160
|
|
|
176114
176161
|
// node_modules/tar/dist/esm/index.min.js
|
|
@@ -179372,7 +179419,7 @@ async function handleEntry(args) {
|
|
|
179372
179419
|
total: args.getTotal()
|
|
179373
179420
|
});
|
|
179374
179421
|
args.addTotal(size);
|
|
179375
|
-
args.fs.mkdirSync(
|
|
179422
|
+
args.fs.mkdirSync(dirname6(target), { recursive: true });
|
|
179376
179423
|
const chunks = [];
|
|
179377
179424
|
await new Promise((res, rej) => {
|
|
179378
179425
|
entry.on("data", (chunk) => chunks.push(chunk));
|
|
@@ -179476,7 +179523,7 @@ async function handleFlowsPull(ctx, opts, deps) {
|
|
|
179476
179523
|
return { error: validation.error };
|
|
179477
179524
|
}
|
|
179478
179525
|
const destAbs = resolve7(opts.out ?? join23(".qawolf", opts.env));
|
|
179479
|
-
const assetsAbs = join23(
|
|
179526
|
+
const assetsAbs = join23(dirname7(destAbs), "assets");
|
|
179480
179527
|
await resolvedDeps.fs.mkdir(assetsAbs, { recursive: true });
|
|
179481
179528
|
const yes = opts.yes ?? false;
|
|
179482
179529
|
let archive;
|
|
@@ -180012,7 +180059,7 @@ async function defaultRunWebFlowDeps(cwd = process.cwd(), signals) {
|
|
|
180012
180059
|
|
|
180013
180060
|
// src/domains/runtimeEnv/prepareRunDir.ts
|
|
180014
180061
|
import { createHash as createHash3 } from "node:crypto";
|
|
180015
|
-
import { basename as basename4, dirname as
|
|
180062
|
+
import { basename as basename4, dirname as dirname10, join as join28, resolve as resolve8, sep as sep4 } from "node:path";
|
|
180016
180063
|
|
|
180017
180064
|
// src/shell/copyDir.ts
|
|
180018
180065
|
import { cp, readdir as readdir2 } from "node:fs/promises";
|
|
@@ -180053,7 +180100,7 @@ async function readPackageJson(pkgPath, fs3) {
|
|
|
180053
180100
|
}
|
|
180054
180101
|
|
|
180055
180102
|
// src/domains/runtimeEnv/innerHop.ts
|
|
180056
|
-
import { dirname as
|
|
180103
|
+
import { dirname as dirname8, join as join26 } from "node:path";
|
|
180057
180104
|
|
|
180058
180105
|
// src/domains/runtimeEnv/symlinkDir.ts
|
|
180059
180106
|
import { symlink } from "node:fs/promises";
|
|
@@ -180072,7 +180119,7 @@ async function populateInnerHop(args) {
|
|
|
180072
180119
|
const segments = name.split("/");
|
|
180073
180120
|
const target = join26(innerModules, ...segments);
|
|
180074
180121
|
if (segments.length > 1) {
|
|
180075
|
-
await fs3.mkdir(
|
|
180122
|
+
await fs3.mkdir(dirname8(target), { recursive: true });
|
|
180076
180123
|
}
|
|
180077
180124
|
await createDirSymlink(join26(managedModules, ...segments), target);
|
|
180078
180125
|
}
|
|
@@ -180080,33 +180127,40 @@ async function populateInnerHop(args) {
|
|
|
180080
180127
|
|
|
180081
180128
|
// src/domains/runtimeEnv/outerHop.ts
|
|
180082
180129
|
import { lstat } from "node:fs/promises";
|
|
180083
|
-
import { dirname as
|
|
180130
|
+
import { dirname as dirname9, join as join27 } from "node:path";
|
|
180084
180131
|
var executorPackageNames = new Set(pinnedPackages.map((p3) => p3.name));
|
|
180085
180132
|
async function populateOuterHop(args) {
|
|
180086
180133
|
const { projectDir, runDir, fs: fs3 } = args;
|
|
180087
180134
|
if (projectDir === undefined)
|
|
180088
|
-
return;
|
|
180089
|
-
const nearestNm = await findNearestNodeModulesDir(projectDir);
|
|
180090
|
-
if (nearestNm !== undefined) {
|
|
180091
|
-
await createDirSymlink(nearestNm, join27(runDir, "node_modules"));
|
|
180092
|
-
return;
|
|
180093
|
-
}
|
|
180135
|
+
return { mode: "none" };
|
|
180094
180136
|
const installableDeps = await readInstallableDeps(projectDir, fs3);
|
|
180095
|
-
|
|
180096
|
-
|
|
180097
|
-
|
|
180098
|
-
|
|
180099
|
-
|
|
180137
|
+
const depNames = Object.keys(installableDeps);
|
|
180138
|
+
const { satisfying, rejected } = await findSatisfyingNodeModulesDir(projectDir, depNames, fs3);
|
|
180139
|
+
if (satisfying !== undefined) {
|
|
180140
|
+
await createDirSymlink(satisfying, join27(runDir, "node_modules"));
|
|
180141
|
+
return { mode: "symlink", nodeModulesDir: satisfying };
|
|
180142
|
+
}
|
|
180143
|
+
if (depNames.length === 0)
|
|
180144
|
+
return { mode: "none" };
|
|
180145
|
+
args.onInstallStart?.(depNames.length);
|
|
180146
|
+
await installOuterDeps(runDir, installableDeps, fs3, args.install ?? spawnNpmInstall);
|
|
180147
|
+
return { mode: "install", depCount: depNames.length, rejected };
|
|
180148
|
+
}
|
|
180149
|
+
async function findSatisfyingNodeModulesDir(startDir, depNames, fs3) {
|
|
180150
|
+
const rejected = [];
|
|
180100
180151
|
let dir = startDir;
|
|
180101
180152
|
while (true) {
|
|
180102
180153
|
const candidate = join27(dir, "node_modules");
|
|
180103
180154
|
const stats = await lstatSafe(candidate);
|
|
180104
180155
|
if (stats !== undefined && stats.isDirectory() && !stats.isSymbolicLink()) {
|
|
180105
|
-
|
|
180156
|
+
const missing = depNames.filter((name) => !fs3.existsSync(join27(candidate, name, "package.json")));
|
|
180157
|
+
if (missing.length === 0)
|
|
180158
|
+
return { satisfying: candidate, rejected };
|
|
180159
|
+
rejected.push({ dir: candidate, missing });
|
|
180106
180160
|
}
|
|
180107
|
-
const parent =
|
|
180161
|
+
const parent = dirname9(dir);
|
|
180108
180162
|
if (parent === dir)
|
|
180109
|
-
return;
|
|
180163
|
+
return { satisfying: undefined, rejected };
|
|
180110
180164
|
dir = parent;
|
|
180111
180165
|
}
|
|
180112
180166
|
}
|
|
@@ -180124,7 +180178,12 @@ async function readInstallableDeps(projectDir, fs3) {
|
|
|
180124
180178
|
} catch {
|
|
180125
180179
|
return {};
|
|
180126
180180
|
}
|
|
180127
|
-
|
|
180181
|
+
let parsed;
|
|
180182
|
+
try {
|
|
180183
|
+
parsed = JSON.parse(content);
|
|
180184
|
+
} catch {
|
|
180185
|
+
return {};
|
|
180186
|
+
}
|
|
180128
180187
|
if (typeof parsed !== "object" || parsed === null)
|
|
180129
180188
|
return {};
|
|
180130
180189
|
const rawDeps = parsed["dependencies"];
|
|
@@ -180132,11 +180191,11 @@ async function readInstallableDeps(projectDir, fs3) {
|
|
|
180132
180191
|
return {};
|
|
180133
180192
|
return Object.fromEntries(Object.entries(rawDeps).filter(([name]) => !executorPackageNames.has(name)).filter((entry) => typeof entry[1] === "string"));
|
|
180134
180193
|
}
|
|
180135
|
-
async function installOuterDeps(runDir, deps, fs3) {
|
|
180194
|
+
async function installOuterDeps(runDir, deps, fs3, install) {
|
|
180136
180195
|
await fs3.writeFile(join27(runDir, "package.json"), JSON.stringify({ name: "qawolf-run", private: true, dependencies: deps }, undefined, 2));
|
|
180137
180196
|
await fs3.writeFile(join27(runDir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
|
|
180138
180197
|
`);
|
|
180139
|
-
const result = await
|
|
180198
|
+
const result = await install(runDir);
|
|
180140
180199
|
if (result.exitCode !== 0) {
|
|
180141
180200
|
throw new Error(`Failed to install project dependencies into ${runDir}: ${result.stderr.trim()}`);
|
|
180142
180201
|
}
|
|
@@ -180158,10 +180217,16 @@ async function prepareRunDir(args) {
|
|
|
180158
180217
|
if (projectDir !== undefined) {
|
|
180159
180218
|
await writeExecSubpathImports({ execDir, fs: fs3 });
|
|
180160
180219
|
}
|
|
180161
|
-
await populateOuterHop({
|
|
180220
|
+
const outerHop = await populateOuterHop({
|
|
180221
|
+
projectDir,
|
|
180222
|
+
runDir,
|
|
180223
|
+
fs: fs3,
|
|
180224
|
+
...args.onInstallStart !== undefined ? { onInstallStart: args.onInstallStart } : {}
|
|
180225
|
+
});
|
|
180162
180226
|
return {
|
|
180163
180227
|
files: stagedFiles,
|
|
180164
180228
|
runDir,
|
|
180229
|
+
outerHop,
|
|
180165
180230
|
cleanup: () => fs3.rm(runDir, { recursive: true, force: true })
|
|
180166
180231
|
};
|
|
180167
180232
|
}
|
|
@@ -180181,7 +180246,7 @@ async function stageFlowFiles(args) {
|
|
|
180181
180246
|
}
|
|
180182
180247
|
if (files.length > 1) {
|
|
180183
180248
|
return Promise.all(files.map(async (f3) => {
|
|
180184
|
-
const dirHash = createHash3("sha256").update(
|
|
180249
|
+
const dirHash = createHash3("sha256").update(dirname10(f3)).digest("hex").slice(0, 8);
|
|
180185
180250
|
const subDir = join28(execDir, dirHash);
|
|
180186
180251
|
await fs3.mkdir(subDir, { recursive: true });
|
|
180187
180252
|
const dest = join28(subDir, basename4(f3));
|
|
@@ -180244,8 +180309,143 @@ function resolveDepsRoot(args) {
|
|
|
180244
180309
|
}, { fs: fs3 });
|
|
180245
180310
|
}
|
|
180246
180311
|
|
|
180247
|
-
// src/
|
|
180312
|
+
// src/domains/emails/configureEmails.ts
|
|
180313
|
+
async function loadSdkDeps2(cwd) {
|
|
180314
|
+
try {
|
|
180315
|
+
const emailsPath = resolveFromEnvDir(cwd, "@qawolf/emails");
|
|
180316
|
+
return await import(emailsPath);
|
|
180317
|
+
} catch (err) {
|
|
180318
|
+
throw new Error("Could not load @qawolf/emails. Install it in your project: `npm install @qawolf/emails` or `bun add @qawolf/emails`.", { cause: err });
|
|
180319
|
+
}
|
|
180320
|
+
}
|
|
180321
|
+
async function configureEmails(config2, cwd, deps) {
|
|
180322
|
+
const { createEmailsClient, configureEmailsClient } = deps ?? await loadSdkDeps2(cwd);
|
|
180323
|
+
const client = await createEmailsClient({
|
|
180324
|
+
...config2,
|
|
180325
|
+
pollForEmailsDefaultTimeoutMs: 60000,
|
|
180326
|
+
waitForMessagesDefaultDelayMs: 1000
|
|
180327
|
+
});
|
|
180328
|
+
configureEmailsClient(client);
|
|
180329
|
+
return client;
|
|
180330
|
+
}
|
|
180331
|
+
async function registerLazyEmailsClient(getInbox, cwd, deps) {
|
|
180332
|
+
const { configureEmailsClient } = deps ?? await loadSdkDeps2(cwd);
|
|
180333
|
+
configureEmailsClient({ getInbox });
|
|
180334
|
+
}
|
|
180335
|
+
|
|
180336
|
+
// src/commands/flows/envVarDeps.ts
|
|
180248
180337
|
import { join as join30 } from "node:path";
|
|
180338
|
+
function envFilePath(envDir) {
|
|
180339
|
+
return join30(envDir, ".env");
|
|
180340
|
+
}
|
|
180341
|
+
async function readEnv(envDir, fs3) {
|
|
180342
|
+
try {
|
|
180343
|
+
return parseDotenv(await fs3.readFile(envFilePath(envDir)));
|
|
180344
|
+
} catch (err) {
|
|
180345
|
+
if (isNoEntError(err))
|
|
180346
|
+
return {};
|
|
180347
|
+
throw err;
|
|
180348
|
+
}
|
|
180349
|
+
}
|
|
180350
|
+
async function writeEnv(envDir, fs3, vars) {
|
|
180351
|
+
const target = envFilePath(envDir);
|
|
180352
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
180353
|
+
await fs3.writeFile(tmp, serializeDotenv(vars), { mode: 384 });
|
|
180354
|
+
await fs3.rename(tmp, target);
|
|
180355
|
+
}
|
|
180356
|
+
function makeEnvVarDeps(envDir, fs3) {
|
|
180357
|
+
return {
|
|
180358
|
+
setEnvironmentVariable: async (key, value) => {
|
|
180359
|
+
const vars = await readEnv(envDir, fs3);
|
|
180360
|
+
vars[key] = value;
|
|
180361
|
+
await writeEnv(envDir, fs3, vars);
|
|
180362
|
+
process.env[key] = value;
|
|
180363
|
+
},
|
|
180364
|
+
fetchLatestEnvironmentVariables: async () => {
|
|
180365
|
+
const vars = await readEnv(envDir, fs3);
|
|
180366
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
180367
|
+
process.env[key] = value;
|
|
180368
|
+
}
|
|
180369
|
+
}
|
|
180370
|
+
};
|
|
180371
|
+
}
|
|
180372
|
+
|
|
180373
|
+
// src/commands/flows/flowRuntimeDeps.ts
|
|
180374
|
+
async function maybeGetIdentityTeamId(platform) {
|
|
180375
|
+
const identity = await platform.getIdentity();
|
|
180376
|
+
return identity.ok ? identity.value.team.id : undefined;
|
|
180377
|
+
}
|
|
180378
|
+
function lazyGetInbox({
|
|
180379
|
+
envDir,
|
|
180380
|
+
ctx,
|
|
180381
|
+
platform,
|
|
180382
|
+
env: env2,
|
|
180383
|
+
resolveApiKeyFn,
|
|
180384
|
+
configureEmailsFn,
|
|
180385
|
+
createPlatform
|
|
180386
|
+
}) {
|
|
180387
|
+
let getInboxPromise;
|
|
180388
|
+
async function configure() {
|
|
180389
|
+
const explicitEmailerUrl = env2["EMAILER_URL"];
|
|
180390
|
+
let teamId = env2["CLOUD_AGENTS_INBOX_TEAM_ID"];
|
|
180391
|
+
const apiKeyResult = explicitEmailerUrl === undefined ? await resolveApiKeyFn(ctx.configDir, ctx.fs) : undefined;
|
|
180392
|
+
if (teamId === undefined && platform !== undefined) {
|
|
180393
|
+
teamId = await maybeGetIdentityTeamId(platform);
|
|
180394
|
+
}
|
|
180395
|
+
if (teamId === undefined && apiKeyResult !== undefined) {
|
|
180396
|
+
const identityPlatform = createPlatform(apiKeyResult.key, {
|
|
180397
|
+
baseUrl: ctx.apiBaseUrl,
|
|
180398
|
+
fetch: globalThis.fetch,
|
|
180399
|
+
...ctx.log ? { logger: ctx.log("trpc") } : {}
|
|
180400
|
+
});
|
|
180401
|
+
teamId = await maybeGetIdentityTeamId(identityPlatform);
|
|
180402
|
+
}
|
|
180403
|
+
const teamIdPart = teamId !== undefined ? { teamId } : {};
|
|
180404
|
+
if (explicitEmailerUrl !== undefined) {
|
|
180405
|
+
const client2 = await configureEmailsFn({ emailerUrl: explicitEmailerUrl, ...teamIdPart }, envDir);
|
|
180406
|
+
return client2.getInbox;
|
|
180407
|
+
}
|
|
180408
|
+
if (apiKeyResult === undefined) {
|
|
180409
|
+
throw new Error("getInbox requires EMAILER_URL, QAWOLF_API_KEY, or stored QA Wolf credentials. Run 'qawolf auth login'.");
|
|
180410
|
+
}
|
|
180411
|
+
const client = await configureEmailsFn({ apiKey: apiKeyResult.key, url: `${ctx.apiBaseUrl}/api`, ...teamIdPart }, envDir);
|
|
180412
|
+
return client.getInbox;
|
|
180413
|
+
}
|
|
180414
|
+
return async (...args) => {
|
|
180415
|
+
getInboxPromise ??= configure();
|
|
180416
|
+
const getInbox = await getInboxPromise;
|
|
180417
|
+
return getInbox(...args);
|
|
180418
|
+
};
|
|
180419
|
+
}
|
|
180420
|
+
async function createFlowRuntimeDeps({
|
|
180421
|
+
envDir,
|
|
180422
|
+
ctx,
|
|
180423
|
+
platform,
|
|
180424
|
+
env: env2 = process.env,
|
|
180425
|
+
resolveApiKeyFn = resolveApiKey,
|
|
180426
|
+
configureEmailsFn = configureEmails,
|
|
180427
|
+
createPlatform = createPlatformClient
|
|
180428
|
+
}) {
|
|
180429
|
+
const getInbox = lazyGetInbox({
|
|
180430
|
+
envDir,
|
|
180431
|
+
ctx,
|
|
180432
|
+
platform,
|
|
180433
|
+
env: env2,
|
|
180434
|
+
resolveApiKeyFn,
|
|
180435
|
+
configureEmailsFn,
|
|
180436
|
+
createPlatform
|
|
180437
|
+
});
|
|
180438
|
+
try {
|
|
180439
|
+
await registerLazyEmailsClient(getInbox, envDir);
|
|
180440
|
+
} catch {}
|
|
180441
|
+
return {
|
|
180442
|
+
...makeEnvVarDeps(envDir, ctx.fs),
|
|
180443
|
+
getInbox
|
|
180444
|
+
};
|
|
180445
|
+
}
|
|
180446
|
+
|
|
180447
|
+
// src/shell/appium/createAppiumServer.ts
|
|
180448
|
+
import { join as join31 } from "node:path";
|
|
180249
180449
|
|
|
180250
180450
|
// src/shell/appium/spawnAppium.ts
|
|
180251
180451
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -180320,7 +180520,7 @@ async function createAppiumServer(envDir, signals, params) {
|
|
|
180320
180520
|
const spawnFn = params?.deps?.spawn ?? defaultSpawnAppium;
|
|
180321
180521
|
const findFreePortFn = params?.deps?.findFreePort ?? findFreePort;
|
|
180322
180522
|
const resolveAppiumBinFn = params?.deps?.resolveAppiumBin ?? resolveAppiumBin;
|
|
180323
|
-
const appiumHome2 = params?.options?.appiumHome ??
|
|
180523
|
+
const appiumHome2 = params?.options?.appiumHome ?? join31(envPaths("qawolf").data, "appium");
|
|
180324
180524
|
const timeoutMs3 = params?.options?.startTimeoutMs ?? defaultStartTimeoutMs;
|
|
180325
180525
|
const bin = resolveAppiumBinFn(envDir);
|
|
180326
180526
|
const port = await findFreePortFn();
|
|
@@ -180659,12 +180859,12 @@ Install Android Studio and open Tools > SDK Manager.`);
|
|
|
180659
180859
|
}
|
|
180660
180860
|
|
|
180661
180861
|
// src/shell/manifest/lookup.ts
|
|
180662
|
-
import { basename as basename5, dirname as
|
|
180862
|
+
import { basename as basename5, dirname as dirname11, relative as relative6 } from "node:path";
|
|
180663
180863
|
async function findFlowStamp(flowAbsPath) {
|
|
180664
180864
|
let envDir;
|
|
180665
|
-
let current =
|
|
180666
|
-
while (current !==
|
|
180667
|
-
const parent2 =
|
|
180865
|
+
let current = dirname11(flowAbsPath);
|
|
180866
|
+
while (current !== dirname11(current)) {
|
|
180867
|
+
const parent2 = dirname11(current);
|
|
180668
180868
|
if (basename5(parent2) === ".qawolf") {
|
|
180669
180869
|
envDir = current;
|
|
180670
180870
|
break;
|
|
@@ -180806,6 +181006,9 @@ function createAndroidLaunchContext({
|
|
|
180806
181006
|
import path11 from "node:path";
|
|
180807
181007
|
import { pathToFileURL } from "node:url";
|
|
180808
181008
|
|
|
181009
|
+
// src/shell/resolver/detectRuntimeCapabilities.ts
|
|
181010
|
+
import nodeModule2 from "node:module";
|
|
181011
|
+
|
|
180809
181012
|
// src/shell/resolver/registerFlowModuleResolver.ts
|
|
180810
181013
|
import nodeModule from "node:module";
|
|
180811
181014
|
|
|
@@ -180872,6 +181075,50 @@ function registerFlowModuleResolver() {
|
|
|
180872
181075
|
registerHooks({ resolve: flowResolveHook });
|
|
180873
181076
|
}
|
|
180874
181077
|
|
|
181078
|
+
// src/shell/resolver/detectRuntimeCapabilities.ts
|
|
181079
|
+
function detectRuntimeCapabilities() {
|
|
181080
|
+
return {
|
|
181081
|
+
isBun: globalThis.Bun !== undefined,
|
|
181082
|
+
hasSyncHooks: resolveRegisterHooks(nodeModule2) !== undefined,
|
|
181083
|
+
hasNativeTypeScript: Boolean(process.features.typescript),
|
|
181084
|
+
hasAsyncRegister: typeof nodeModule2.register === "function"
|
|
181085
|
+
};
|
|
181086
|
+
}
|
|
181087
|
+
|
|
181088
|
+
// src/shell/resolver/selectFlowLoaderStrategy.ts
|
|
181089
|
+
function selectFlowLoaderStrategy(caps) {
|
|
181090
|
+
if (caps.isBun)
|
|
181091
|
+
return "none";
|
|
181092
|
+
if (caps.hasNativeTypeScript) {
|
|
181093
|
+
return caps.hasSyncHooks ? "sync-alias" : "none";
|
|
181094
|
+
}
|
|
181095
|
+
return caps.hasAsyncRegister ? "oxc-transpile" : "unsupported";
|
|
181096
|
+
}
|
|
181097
|
+
|
|
181098
|
+
// src/shell/resolver/registerFlowLoader.ts
|
|
181099
|
+
async function defaultRegisterOxcLoader() {
|
|
181100
|
+
try {
|
|
181101
|
+
await import("@oxc-node/core/register");
|
|
181102
|
+
} catch (cause) {
|
|
181103
|
+
throw new Error(`Failed to load the @oxc-node/core TypeScript loader needed to run flows on Node ${process.version}. Ensure @oxc-node/core is installed for this platform (${process.platform}-${process.arch}).`, { cause });
|
|
181104
|
+
}
|
|
181105
|
+
}
|
|
181106
|
+
async function registerFlowLoader(deps = {}) {
|
|
181107
|
+
const {
|
|
181108
|
+
capabilities = detectRuntimeCapabilities(),
|
|
181109
|
+
registerSyncAlias = registerFlowModuleResolver,
|
|
181110
|
+
registerOxcLoader = defaultRegisterOxcLoader
|
|
181111
|
+
} = deps;
|
|
181112
|
+
const strategy = selectFlowLoaderStrategy(capabilities);
|
|
181113
|
+
if (strategy === "sync-alias") {
|
|
181114
|
+
registerSyncAlias();
|
|
181115
|
+
} else if (strategy === "oxc-transpile") {
|
|
181116
|
+
await registerOxcLoader();
|
|
181117
|
+
} else if (strategy === "unsupported") {
|
|
181118
|
+
throw new Error(`Running TypeScript flows requires Node 20.6+ (for module.register) or a runtime with native TypeScript support. Current runtime: ${process.version}. Upgrade Node to 20.6 or newer.`);
|
|
181119
|
+
}
|
|
181120
|
+
}
|
|
181121
|
+
|
|
180875
181122
|
// src/domains/runner/executorPlugin.ts
|
|
180876
181123
|
import path10 from "node:path";
|
|
180877
181124
|
var executorImportDetect = /from ['"]@qawolf\/(?:flows|testkit|emails)/;
|
|
@@ -180971,7 +181218,7 @@ async function loadFlowDefault(args) {
|
|
|
180971
181218
|
depsRoot = ""
|
|
180972
181219
|
} = args;
|
|
180973
181220
|
if (bundleFlow === undefined) {
|
|
180974
|
-
|
|
181221
|
+
await registerFlowLoader();
|
|
180975
181222
|
return importDefaultExport(pathToFileURL(flowPath).href, flowPath);
|
|
180976
181223
|
}
|
|
180977
181224
|
const code = await bundleFlow(flowPath, depsRoot);
|
|
@@ -181180,6 +181427,7 @@ async function runAndroidFlow({
|
|
|
181180
181427
|
callback: async (flowDeps) => {
|
|
181181
181428
|
const androidDeps = {
|
|
181182
181429
|
...Object.fromEntries(unsupportedAndroidDepNames.map((name) => [name, notSupported(name)])),
|
|
181430
|
+
...deps.flowRuntimeDeps,
|
|
181183
181431
|
inputs: flowDeps.flowInputs,
|
|
181184
181432
|
workflowInputs: flowDeps.flowInputs,
|
|
181185
181433
|
setOutput: flowDeps.setOutput,
|
|
@@ -181390,6 +181638,7 @@ async function runWebFlow({
|
|
|
181390
181638
|
callback: async (flowDeps) => {
|
|
181391
181639
|
const webDeps = {
|
|
181392
181640
|
...Object.fromEntries(unsupportedWebDepNames.map((name) => [name, notSupported(name)])),
|
|
181641
|
+
...deps.flowRuntimeDeps,
|
|
181393
181642
|
launch,
|
|
181394
181643
|
launchWithGpu: launch,
|
|
181395
181644
|
isGpuAvailable: () => false,
|
|
@@ -181422,18 +181671,18 @@ async function runWebFlow({
|
|
|
181422
181671
|
}
|
|
181423
181672
|
|
|
181424
181673
|
// src/shell/embeddedWorkerCli.ts
|
|
181425
|
-
import { existsSync as existsSync3, mkdirSync as
|
|
181426
|
-
import { basename as basename6, join as
|
|
181674
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
181675
|
+
import { basename as basename6, join as join32 } from "node:path";
|
|
181427
181676
|
var embeddedCliPathEnv = "QAWOLF_EMBEDDED_CLI_PATH";
|
|
181428
181677
|
function extractEmbeddedWorkerCli() {
|
|
181429
181678
|
const assetPath = process.env[embeddedCliPathEnv];
|
|
181430
181679
|
if (assetPath === undefined || assetPath === "")
|
|
181431
181680
|
return;
|
|
181432
|
-
const dir =
|
|
181433
|
-
const dest =
|
|
181681
|
+
const dir = join32(getDataDir(), "worker");
|
|
181682
|
+
const dest = join32(dir, basename6(assetPath));
|
|
181434
181683
|
if (existsSync3(dest))
|
|
181435
181684
|
return dest;
|
|
181436
|
-
|
|
181685
|
+
mkdirSync3(dir, { recursive: true });
|
|
181437
181686
|
writeFileSync2(dest, readFileSync2(assetPath));
|
|
181438
181687
|
return dest;
|
|
181439
181688
|
}
|
|
@@ -181583,7 +181832,7 @@ function makePooledDispatch(resolvedDir) {
|
|
|
181583
181832
|
}
|
|
181584
181833
|
|
|
181585
181834
|
// src/commands/flows/buildRunReporter.ts
|
|
181586
|
-
import { dirname as
|
|
181835
|
+
import { dirname as dirname12 } from "node:path";
|
|
181587
181836
|
|
|
181588
181837
|
// src/shell/reporter/createConsoleReporter.ts
|
|
181589
181838
|
import { styleText as styleText3 } from "node:util";
|
|
@@ -181675,6 +181924,12 @@ function createConsoleReporter(deps) {
|
|
|
181675
181924
|
deps.stderr.write(`${formatted}
|
|
181676
181925
|
|
|
181677
181926
|
`);
|
|
181927
|
+
const missingPackage = extractMissingPackage(errStr);
|
|
181928
|
+
if (missingPackage !== undefined) {
|
|
181929
|
+
deps.stderr.write(`${styleText3("yellow", `${indent}${runnerMessages.moduleNotFoundHint(missingPackage, deps.projectDir)}`)}
|
|
181930
|
+
|
|
181931
|
+
`);
|
|
181932
|
+
}
|
|
181678
181933
|
const counts = tests.total > 0 ? `${tests.passed}/${tests.total} tests ` : "";
|
|
181679
181934
|
deps.stdout.write(` ${styleText3("red", "✗")} ${counts}passed ${styleText3("dim", fmtDuration(durationMs))}
|
|
181680
181935
|
`);
|
|
@@ -181727,7 +181982,7 @@ function createCompositeReporter(reporters) {
|
|
|
181727
181982
|
};
|
|
181728
181983
|
}
|
|
181729
181984
|
// src/shell/reporter/createJUnitReporter.ts
|
|
181730
|
-
import { join as
|
|
181985
|
+
import { join as join33 } from "node:path";
|
|
181731
181986
|
|
|
181732
181987
|
// src/shell/reporter/junitXml.ts
|
|
181733
181988
|
function escapeXml(str) {
|
|
@@ -181766,7 +182021,7 @@ ${suites}`;
|
|
|
181766
182021
|
|
|
181767
182022
|
// src/shell/reporter/createJUnitReporter.ts
|
|
181768
182023
|
function resolveJUnitOutputPath(junit, outputDir) {
|
|
181769
|
-
return typeof junit === "string" && junit.trim() !== "" ? junit :
|
|
182024
|
+
return typeof junit === "string" && junit.trim() !== "" ? junit : join33(outputDir, "junit-report.xml");
|
|
181770
182025
|
}
|
|
181771
182026
|
function createJUnitReporter(deps) {
|
|
181772
182027
|
const flows = [];
|
|
@@ -181795,13 +182050,17 @@ function createJUnitReporter(deps) {
|
|
|
181795
182050
|
function buildRunReporter(flags, deps) {
|
|
181796
182051
|
const stdout2 = deps.stdout ?? process.stdout;
|
|
181797
182052
|
const stderr = deps.stderr ?? process.stderr;
|
|
181798
|
-
const console2 = createConsoleReporter({
|
|
182053
|
+
const console2 = createConsoleReporter({
|
|
182054
|
+
stdout: stdout2,
|
|
182055
|
+
stderr,
|
|
182056
|
+
...deps.projectDir !== undefined ? { projectDir: deps.projectDir } : {}
|
|
182057
|
+
});
|
|
181799
182058
|
if (flags.junit === undefined)
|
|
181800
182059
|
return console2;
|
|
181801
182060
|
const junit = createJUnitReporter({
|
|
181802
182061
|
outputPath: resolveJUnitOutputPath(flags.junit, flags.outputDir),
|
|
181803
182062
|
writeFile: (path9, content) => {
|
|
181804
|
-
deps.fs.mkdirSync(
|
|
182063
|
+
deps.fs.mkdirSync(dirname12(path9), { recursive: true });
|
|
181805
182064
|
deps.fs.writeFileSync(path9, content);
|
|
181806
182065
|
},
|
|
181807
182066
|
stderr
|
|
@@ -181811,7 +182070,15 @@ function buildRunReporter(flags, deps) {
|
|
|
181811
182070
|
|
|
181812
182071
|
// src/commands/flows/buildFlowsRunDeps.ts
|
|
181813
182072
|
function buildFlowsRunDeps(args) {
|
|
181814
|
-
const {
|
|
182073
|
+
const {
|
|
182074
|
+
ctx,
|
|
182075
|
+
resolvedDir,
|
|
182076
|
+
android,
|
|
182077
|
+
runWebFlowDeps,
|
|
182078
|
+
flowRuntimeDeps,
|
|
182079
|
+
flags,
|
|
182080
|
+
projectDir
|
|
182081
|
+
} = args;
|
|
181815
182082
|
return {
|
|
181816
182083
|
peekFlowMeta: makePeekFlowMeta(ctx.fs),
|
|
181817
182084
|
installBrowsers: (innerCtx, browsers) => installBrowserList(innerCtx, browsers, {
|
|
@@ -181820,9 +182087,9 @@ function buildFlowsRunDeps(args) {
|
|
|
181820
182087
|
playwrightCliPath: resolvePlaywrightCli(resolvedDir, process.platform)
|
|
181821
182088
|
}),
|
|
181822
182089
|
runWebFlow,
|
|
181823
|
-
runWebFlowDeps,
|
|
182090
|
+
runWebFlowDeps: { ...runWebFlowDeps, flowRuntimeDeps },
|
|
181824
182091
|
runAndroidFlow,
|
|
181825
|
-
runAndroidFlowDeps: android.deps,
|
|
182092
|
+
runAndroidFlowDeps: { ...android.deps, flowRuntimeDeps },
|
|
181826
182093
|
bootAndroid: android.boot,
|
|
181827
182094
|
shutdownAndroid: android.shutdown,
|
|
181828
182095
|
createPooledDispatch: makePooledDispatch(resolvedDir),
|
|
@@ -181832,7 +182099,8 @@ function buildFlowsRunDeps(args) {
|
|
|
181832
182099
|
reporter: buildRunReporter(flags, {
|
|
181833
182100
|
fs: ctx.fs,
|
|
181834
182101
|
stdout: { write: (text3) => ctx.ui.write(text3) },
|
|
181835
|
-
stderr: { write: (text3) => ctx.ui.write(text3) }
|
|
182102
|
+
stderr: { write: (text3) => ctx.ui.write(text3) },
|
|
182103
|
+
...projectDir !== undefined ? { projectDir } : {}
|
|
181836
182104
|
}),
|
|
181837
182105
|
now: () => Date.now()
|
|
181838
182106
|
};
|
|
@@ -181840,11 +182108,11 @@ function buildFlowsRunDeps(args) {
|
|
|
181840
182108
|
|
|
181841
182109
|
// src/commands/flows/loadEnvFile.ts
|
|
181842
182110
|
import { readFile } from "node:fs/promises";
|
|
181843
|
-
import { join as
|
|
182111
|
+
import { join as join34 } from "node:path";
|
|
181844
182112
|
async function loadEnvFile(envDir) {
|
|
181845
182113
|
let content;
|
|
181846
182114
|
try {
|
|
181847
|
-
content = await readFile(
|
|
182115
|
+
content = await readFile(join34(envDir, ".env"), "utf8");
|
|
181848
182116
|
} catch (err) {
|
|
181849
182117
|
if (isNoEntError(err))
|
|
181850
182118
|
return;
|
|
@@ -181880,15 +182148,34 @@ async function runStagedFlows(args) {
|
|
|
181880
182148
|
files,
|
|
181881
182149
|
projectDir,
|
|
181882
182150
|
depsRoot: runtimeEnv.depsRoot,
|
|
181883
|
-
runRoot: runStagingRoot()
|
|
182151
|
+
runRoot: runStagingRoot(),
|
|
182152
|
+
onInstallStart: (depCount) => ctx.ui.info(runnerMessages.installingProjectDeps(depCount))
|
|
181884
182153
|
});
|
|
182154
|
+
if (staged.outerHop.mode === "install") {
|
|
182155
|
+
const log31 = ctx.log("runner");
|
|
182156
|
+
for (const candidate of staged.outerHop.rejected) {
|
|
182157
|
+
log31.debug(runnerMessages.outerHopCandidateRejected(candidate.dir, candidate.missing));
|
|
182158
|
+
}
|
|
182159
|
+
}
|
|
181885
182160
|
const unregisterCleanup = ctx.signals.register(staged.cleanup);
|
|
181886
182161
|
try {
|
|
181887
182162
|
const resolvedDir = runtimeEnv.depsRoot;
|
|
181888
182163
|
await deps.configureTestkit(resolvedDir);
|
|
181889
182164
|
const android = createAndroidDeps(resolvedDir, ctx.signals);
|
|
181890
182165
|
const runWebFlowDeps = await deps.runWebFlowDeps(resolvedDir, ctx.signals);
|
|
181891
|
-
|
|
182166
|
+
const flowRuntimeDeps = await deps.createFlowRuntimeDeps({
|
|
182167
|
+
envDir: resolvedDir,
|
|
182168
|
+
ctx
|
|
182169
|
+
});
|
|
182170
|
+
return await deps.flowsRun(ctx, staged.files, flags, buildFlowsRunDeps({
|
|
182171
|
+
ctx,
|
|
182172
|
+
resolvedDir,
|
|
182173
|
+
android,
|
|
182174
|
+
runWebFlowDeps,
|
|
182175
|
+
flowRuntimeDeps,
|
|
182176
|
+
flags,
|
|
182177
|
+
projectDir
|
|
182178
|
+
}));
|
|
181892
182179
|
} finally {
|
|
181893
182180
|
unregisterCleanup();
|
|
181894
182181
|
await staged.cleanup();
|
|
@@ -181903,6 +182190,7 @@ function makeDefaultDeps2(fs9) {
|
|
|
181903
182190
|
prepareRunDir: (args) => prepareRunDir({ ...args, fs: fs9 }),
|
|
181904
182191
|
configureTestkit,
|
|
181905
182192
|
runWebFlowDeps: defaultRunWebFlowDeps,
|
|
182193
|
+
createFlowRuntimeDeps,
|
|
181906
182194
|
flowsRun
|
|
181907
182195
|
};
|
|
181908
182196
|
}
|
|
@@ -181924,7 +182212,7 @@ async function handleFlowsRun(ctx, pattern, flags, deps) {
|
|
|
181924
182212
|
}
|
|
181925
182213
|
|
|
181926
182214
|
// src/commands/flows/hybridRunDefaults.ts
|
|
181927
|
-
import { join as
|
|
182215
|
+
import { join as join35, resolve as resolve9 } from "node:path";
|
|
181928
182216
|
function makeDefaultHybridDeps(fs9) {
|
|
181929
182217
|
return {
|
|
181930
182218
|
expandPatterns: (patterns, cwd, logger) => expandPatterns(patterns, cwd, logger, fs9),
|
|
@@ -181933,7 +182221,8 @@ function makeDefaultHybridDeps(fs9) {
|
|
|
181933
182221
|
prepareRunDir: (args) => prepareRunDir({ ...args, fs: fs9 }),
|
|
181934
182222
|
configureTestkit,
|
|
181935
182223
|
flowsRun,
|
|
181936
|
-
runWebFlowDeps: defaultRunWebFlowDeps
|
|
182224
|
+
runWebFlowDeps: defaultRunWebFlowDeps,
|
|
182225
|
+
createFlowRuntimeDeps
|
|
181937
182226
|
};
|
|
181938
182227
|
}
|
|
181939
182228
|
async function handleHybridFlowsRun(ctx, pattern, flags, deps) {
|
|
@@ -181942,7 +182231,7 @@ async function handleHybridFlowsRun(ctx, pattern, flags, deps) {
|
|
|
181942
182231
|
if (validation !== "ok") {
|
|
181943
182232
|
return { error: validation.error, exitCode: 2 };
|
|
181944
182233
|
}
|
|
181945
|
-
const envDir = resolve9(
|
|
182234
|
+
const envDir = resolve9(join35(".qawolf", flags.env));
|
|
181946
182235
|
const patternArgs = buildPatternArgs(pattern);
|
|
181947
182236
|
const globFlows = () => resolvedDeps.expandPatterns(patternArgs, envDir, ctx.log("flows"));
|
|
181948
182237
|
let files = await globFlows();
|
|
@@ -182015,7 +182304,16 @@ async function runWorker(signals) {
|
|
|
182015
182304
|
if (input.flow.kind !== "web")
|
|
182016
182305
|
throw new Error("worker subprocess currently supports web flows only");
|
|
182017
182306
|
await configureTestkit(input.resolvedDir);
|
|
182018
|
-
const
|
|
182307
|
+
const fs9 = makeDefaultFs();
|
|
182308
|
+
const apiBaseUrl = process.env["QAWOLF_API_URL"]?.replace(/\/+$/, "") || "https://app.qawolf.com";
|
|
182309
|
+
const flowRuntimeDeps = await createFlowRuntimeDeps({
|
|
182310
|
+
envDir: input.resolvedDir,
|
|
182311
|
+
ctx: { apiBaseUrl, configDir: getConfigDir(), fs: fs9 }
|
|
182312
|
+
});
|
|
182313
|
+
const runWebFlowDeps = {
|
|
182314
|
+
...await defaultRunWebFlowDeps(input.resolvedDir, signals),
|
|
182315
|
+
flowRuntimeDeps
|
|
182316
|
+
};
|
|
182019
182317
|
const reporter = {};
|
|
182020
182318
|
const deps = {
|
|
182021
182319
|
peekFlowMeta: unavailable("peekFlowMeta"),
|
|
@@ -182048,8 +182346,8 @@ var listExamples = `
|
|
|
182048
182346
|
Examples:
|
|
182049
182347
|
$ qawolf flows list
|
|
182050
182348
|
$ qawolf flows list "flows/checkout/**"
|
|
182051
|
-
$ qawolf flows list --remote
|
|
182052
|
-
$ qawolf flows list "**/checkout/**" --remote`;
|
|
182349
|
+
$ qawolf flows list --remote --env staging
|
|
182350
|
+
$ qawolf flows list "**/checkout/**" --remote --env staging --include-drafts`;
|
|
182053
182351
|
var pullExamples = `
|
|
182054
182352
|
Examples:
|
|
182055
182353
|
$ qawolf flows pull --env staging
|
|
@@ -182061,9 +182359,23 @@ function registerFlowsCommand(program2, signals) {
|
|
|
182061
182359
|
registerRunWorkerCommand(flows, signals);
|
|
182062
182360
|
declareCommandKind(flows.command("list [pattern]"), "local", {
|
|
182063
182361
|
kindNote: "read with --remote"
|
|
182064
|
-
}).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) => {
|
|
182362
|
+
}).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) => {
|
|
182065
182363
|
if (opts.remote) {
|
|
182066
|
-
|
|
182364
|
+
const env3 = opts.env;
|
|
182365
|
+
if (env3 === undefined) {
|
|
182366
|
+
return withContext(signals, async () => ({
|
|
182367
|
+
error: flowsMessages.list.remoteRequiresEnv
|
|
182368
|
+
}))(opts, command);
|
|
182369
|
+
}
|
|
182370
|
+
return withAuthContext(signals, (ctx) => flowsListRemote(ctx, pattern, {
|
|
182371
|
+
env: env3,
|
|
182372
|
+
includeDrafts: opts.includeDrafts
|
|
182373
|
+
}))(opts, command);
|
|
182374
|
+
}
|
|
182375
|
+
if (opts.env !== undefined || opts.includeDrafts) {
|
|
182376
|
+
return withContext(signals, async () => ({
|
|
182377
|
+
error: flowsMessages.list.flagsRequireRemote
|
|
182378
|
+
}))(opts, command);
|
|
182067
182379
|
}
|
|
182068
182380
|
return withContext(signals, (ctx) => handleFlowsList(ctx, pattern))(opts, command);
|
|
182069
182381
|
});
|
|
@@ -182073,7 +182385,7 @@ function registerFlowsCommand(program2, signals) {
|
|
|
182073
182385
|
}
|
|
182074
182386
|
|
|
182075
182387
|
// src/domains/init/init.ts
|
|
182076
|
-
import { dirname as
|
|
182388
|
+
import { dirname as dirname13, join as join36, relative as relative7 } from "node:path";
|
|
182077
182389
|
|
|
182078
182390
|
// src/domains/init/templates.ts
|
|
182079
182391
|
var qawolfConfigTs = `export default {
|
|
@@ -182111,9 +182423,9 @@ function makeDefaultInitDeps() {
|
|
|
182111
182423
|
async function handleInit(ctx, opts, deps = makeDefaultInitDeps()) {
|
|
182112
182424
|
ctx.ui.gap();
|
|
182113
182425
|
ctx.ui.intro(initMessages.title);
|
|
182114
|
-
const configPath =
|
|
182115
|
-
const flowPath =
|
|
182116
|
-
const gitignorePath =
|
|
182426
|
+
const configPath = join36(deps.cwd, "qawolf.config.ts");
|
|
182427
|
+
const flowPath = join36(deps.cwd, "src", "flows", "example.flow.ts");
|
|
182428
|
+
const gitignorePath = join36(deps.cwd, ".qawolf", ".gitignore");
|
|
182117
182429
|
await writeWithPrompt(ctx, configPath, qawolfConfigTs, opts.yes, deps);
|
|
182118
182430
|
await writeWithPrompt(ctx, flowPath, exampleFlowTs, opts.yes, deps);
|
|
182119
182431
|
await writeWithPrompt(ctx, gitignorePath, qawolfGitignore, opts.yes, deps);
|
|
@@ -182132,12 +182444,12 @@ async function writeWithPrompt(ctx, filePath, content, yes, deps) {
|
|
|
182132
182444
|
return;
|
|
182133
182445
|
}
|
|
182134
182446
|
}
|
|
182135
|
-
await deps.fs.mkdir(
|
|
182447
|
+
await deps.fs.mkdir(dirname13(filePath), { recursive: true });
|
|
182136
182448
|
await deps.fs.writeFile(filePath, content);
|
|
182137
182449
|
ctx.ui.step(initMessages.createdFile(relPath));
|
|
182138
182450
|
}
|
|
182139
182451
|
async function ensurePackageJson(ctx, yes, deps) {
|
|
182140
|
-
const pkgPath =
|
|
182452
|
+
const pkgPath = join36(deps.cwd, "package.json");
|
|
182141
182453
|
if (!await deps.fs.pathExists(pkgPath)) {
|
|
182142
182454
|
const confirmed2 = await ctx.ui.confirm(initMessages.createPackageJsonPrompt, { yes });
|
|
182143
182455
|
if (!confirmed2.ok || !confirmed2.value) {
|
|
@@ -182196,19 +182508,19 @@ Examples:
|
|
|
182196
182508
|
}
|
|
182197
182509
|
|
|
182198
182510
|
// src/commands/install/android.ts
|
|
182199
|
-
import { join as
|
|
182511
|
+
import { join as join39 } from "node:path";
|
|
182200
182512
|
|
|
182201
182513
|
// src/domains/install/android/index.ts
|
|
182202
182514
|
import { parseExecutionTarget as parseExecutionTarget4 } from "@qawolf/flow-targets";
|
|
182203
182515
|
|
|
182204
182516
|
// src/domains/install/android/avd.ts
|
|
182205
|
-
import { join as
|
|
182517
|
+
import { join as join37 } from "node:path";
|
|
182206
182518
|
async function installAvds(ctx, specs, deps) {
|
|
182207
182519
|
const versionResult = await deps.spawn(deps.sdkManagerPath, ["--version"]);
|
|
182208
182520
|
if (versionResult.exitCode !== 0) {
|
|
182209
182521
|
throw new Error(installMessages.android.sdkmanagerNotFound(deps.sdkManagerPath));
|
|
182210
182522
|
}
|
|
182211
|
-
const licenseFile =
|
|
182523
|
+
const licenseFile = join37(deps.androidHome, "licenses", "android-sdk-license");
|
|
182212
182524
|
if (deps.checkExists(licenseFile)) {
|
|
182213
182525
|
ctx.ui.info(installMessages.android.licensesAlreadyAccepted);
|
|
182214
182526
|
} else {
|
|
@@ -182222,7 +182534,7 @@ async function installAvds(ctx, specs, deps) {
|
|
|
182222
182534
|
for (const spec of specs) {
|
|
182223
182535
|
if (installedImages.has(spec.systemImage))
|
|
182224
182536
|
continue;
|
|
182225
|
-
const systemImg =
|
|
182537
|
+
const systemImg = join37(deps.androidHome, ...spec.systemImage.split(";"), "system.img");
|
|
182226
182538
|
if (deps.checkExists(systemImg)) {
|
|
182227
182539
|
ctx.ui.info(installMessages.android.systemImageAlreadyInstalled(spec.systemImage));
|
|
182228
182540
|
installedImages.add(spec.systemImage);
|
|
@@ -182270,9 +182582,9 @@ async function installAvds(ctx, specs, deps) {
|
|
|
182270
182582
|
}
|
|
182271
182583
|
|
|
182272
182584
|
// src/domains/install/android/driver.ts
|
|
182273
|
-
import { join as
|
|
182585
|
+
import { join as join38 } from "node:path";
|
|
182274
182586
|
var appiumEnv = {
|
|
182275
|
-
APPIUM_HOME:
|
|
182587
|
+
APPIUM_HOME: join38(envPaths("qawolf").data, "appium")
|
|
182276
182588
|
};
|
|
182277
182589
|
async function installUiautomator2Driver(ctx, deps) {
|
|
182278
182590
|
const listResult = await deps.spawn(deps.appiumBinPath, ["driver", "list", "--installed"], { env: appiumEnv });
|
|
@@ -182359,8 +182671,8 @@ async function handleInstallAndroid(ctx, pattern, envDir) {
|
|
|
182359
182671
|
arch: process.arch,
|
|
182360
182672
|
androidHome: androidHome2,
|
|
182361
182673
|
checkExists: (path9) => fs9.existsSync(path9),
|
|
182362
|
-
sdkManagerPath:
|
|
182363
|
-
avdManagerPath:
|
|
182674
|
+
sdkManagerPath: join39(androidHome2, "cmdline-tools", "latest", "bin", "sdkmanager"),
|
|
182675
|
+
avdManagerPath: join39(androidHome2, "cmdline-tools", "latest", "bin", "avdmanager"),
|
|
182364
182676
|
expandPatterns: (patterns, cwd) => expandPatterns(patterns, cwd ?? process.cwd(), undefined, fs9),
|
|
182365
182677
|
peekFlowMeta: makePeekFlowMeta(fs9),
|
|
182366
182678
|
resolveDepsRoot: async (files) => envDir ?? (await resolveDepsRoot({ files, fs: fs9 })).depsRoot,
|
|
@@ -182506,39 +182818,6 @@ Examples:
|
|
|
182506
182818
|
});
|
|
182507
182819
|
}
|
|
182508
182820
|
|
|
182509
|
-
// node_modules/@qawolf/api-contracts/dist/v1/ids.js
|
|
182510
|
-
var defaultIdSchemas = {
|
|
182511
|
-
environment: exports_external.string().min(1).describe("The id of the environment."),
|
|
182512
|
-
flow: exports_external.string().min(1).describe("The id of the flow."),
|
|
182513
|
-
run: exports_external.string().min(1).describe("The id of the run.")
|
|
182514
|
-
};
|
|
182515
|
-
|
|
182516
|
-
// node_modules/@qawolf/api-contracts/dist/v1/run/index.js
|
|
182517
|
-
var makeCreateRunContract = (ids) => {
|
|
182518
|
-
const input = exports_external.object({
|
|
182519
|
-
environmentId: ids.environment,
|
|
182520
|
-
environmentVariables: exports_external.record(exports_external.string(), exports_external.string()).optional(),
|
|
182521
|
-
flowIds: exports_external.array(ids.flow).min(1),
|
|
182522
|
-
ignoreRules: exports_external.boolean().default(false)
|
|
182523
|
-
});
|
|
182524
|
-
const output = exports_external.object({ runId: ids.run });
|
|
182525
|
-
return {
|
|
182526
|
-
description: "Create a run for selected flows in an environment.",
|
|
182527
|
-
input,
|
|
182528
|
-
kind: "write",
|
|
182529
|
-
name: "run.create",
|
|
182530
|
-
output
|
|
182531
|
-
};
|
|
182532
|
-
};
|
|
182533
|
-
|
|
182534
|
-
// node_modules/@qawolf/api-contracts/dist/v1/index.js
|
|
182535
|
-
var makeContractsV1 = (ids) => ({
|
|
182536
|
-
run: {
|
|
182537
|
-
create: makeCreateRunContract(ids)
|
|
182538
|
-
}
|
|
182539
|
-
});
|
|
182540
|
-
var publicContractsV1 = makeContractsV1(defaultIdSchemas);
|
|
182541
|
-
|
|
182542
182821
|
// src/core/publicApi/flagSpecs.ts
|
|
182543
182822
|
var kebabCase = (field) => field.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
|
|
182544
182823
|
function flagKind(schema) {
|
|
@@ -182635,8 +182914,16 @@ function buildSpec(contract, path9) {
|
|
|
182635
182914
|
contract
|
|
182636
182915
|
};
|
|
182637
182916
|
}
|
|
182638
|
-
function buildCommandSpecs(tree) {
|
|
182639
|
-
const
|
|
182917
|
+
function buildCommandSpecs(tree, options = {}) {
|
|
182918
|
+
const skip = options.skipContractNames;
|
|
182919
|
+
const walk3 = (node2, path9) => Object.entries(node2).flatMap(([key, value]) => {
|
|
182920
|
+
const childPath = [...path9, key];
|
|
182921
|
+
if (!isContract(value))
|
|
182922
|
+
return walk3(value, childPath);
|
|
182923
|
+
if (skip?.has(childPath.join(".")))
|
|
182924
|
+
return [];
|
|
182925
|
+
return [buildSpec(value, childPath)];
|
|
182926
|
+
});
|
|
182640
182927
|
return walk3(tree, []);
|
|
182641
182928
|
}
|
|
182642
182929
|
|
|
@@ -182707,6 +182994,7 @@ async function handlePublicApiCommand(ctx, spec, options) {
|
|
|
182707
182994
|
var groupDescriptions = {
|
|
182708
182995
|
run: "Trigger and manage QA Wolf runs on the platform"
|
|
182709
182996
|
};
|
|
182997
|
+
var handWrittenContractNames = new Set(["flow.list"]);
|
|
182710
182998
|
function resolveGroup(parent2, segment) {
|
|
182711
182999
|
const existing = parent2.commands.find((command) => command.name() === segment);
|
|
182712
183000
|
return existing ?? parent2.command(segment).description(groupDescriptions[segment] ?? `QA Wolf public API ${segment} commands`);
|
|
@@ -182732,7 +183020,9 @@ function registerSpec(program2, signals, spec, authDeps) {
|
|
|
182732
183020
|
command.action((options, leaf) => withAuthContext(signals, (ctx) => handlePublicApiCommand(ctx, spec, options), authDeps ?? {})(options, leaf));
|
|
182733
183021
|
}
|
|
182734
183022
|
function registerPublicApiCommands(program2, signals, options = {}) {
|
|
182735
|
-
const specs = buildCommandSpecs(options.contracts ?? publicContractsV1
|
|
183023
|
+
const specs = buildCommandSpecs(options.contracts ?? publicContractsV1, {
|
|
183024
|
+
skipContractNames: handWrittenContractNames
|
|
183025
|
+
});
|
|
182736
183026
|
for (const spec of specs) {
|
|
182737
183027
|
registerSpec(program2, signals, spec, options.authDeps);
|
|
182738
183028
|
}
|
|
@@ -182773,4 +183063,4 @@ createProgram({ signals }).parseAsync().catch(() => {
|
|
|
182773
183063
|
process.exitCode = 1;
|
|
182774
183064
|
}).finally(() => flushAndExit(typeof process.exitCode === "number" ? process.exitCode : 0));
|
|
182775
183065
|
|
|
182776
|
-
//# debugId=
|
|
183066
|
+
//# debugId=64AB4032821CC8E864756E2164756E21
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qawolf/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"automation",
|
|
@@ -58,8 +58,9 @@
|
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"@clack/prompts": "1.5.1",
|
|
60
60
|
"@napi-rs/keyring": "1.3.0",
|
|
61
|
+
"@oxc-node/core": "0.1.0",
|
|
61
62
|
"@playwright/test": "1.60.0",
|
|
62
|
-
"@qawolf/api-contracts": "0.
|
|
63
|
+
"@qawolf/api-contracts": "0.2.0",
|
|
63
64
|
"@qawolf/emails": "1.1.1",
|
|
64
65
|
"@qawolf/flow-targets": "1.0.0",
|
|
65
66
|
"@qawolf/flows": "0.1.1",
|
|
@@ -91,7 +92,7 @@
|
|
|
91
92
|
"typescript": "6.0.3"
|
|
92
93
|
},
|
|
93
94
|
"engines": {
|
|
94
|
-
"node": ">=
|
|
95
|
+
"node": ">=20.19.0"
|
|
95
96
|
},
|
|
96
97
|
"packageManager": "bun@1.3.13"
|
|
97
98
|
}
|
|
@@ -41,7 +41,7 @@ write on timeout: it may have reached the server the first time.
|
|
|
41
41
|
| `qawolf auth logout` | local | Remove stored credentials |
|
|
42
42
|
| `qawolf auth whoami` | read | Show authentication status |
|
|
43
43
|
| `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
|
|
44
|
+
| `qawolf flows list` | local (read with --remote) | List flows matching [pattern] from the local project, or from a QA Wolf environment with --remote |
|
|
45
45
|
| `qawolf flows pull` | read | Download an environment's flows into the local .qawolf/<env>/ cache |
|
|
46
46
|
| `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
47
|
| `qawolf init` | local | Scaffold a QA Wolf project in the current directory |
|