@qawolf/cli 1.7.0 → 1.7.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 +393 -25
- package/package.json +2 -2
- package/skills/qawolf-cli/SKILL.md +70 -41
package/dist/cli.js
CHANGED
|
@@ -155313,6 +155313,8 @@ var environmentsMessages = {
|
|
|
155313
155313
|
static: "Static environments",
|
|
155314
155314
|
preview: "Preview (PR) environments"
|
|
155315
155315
|
},
|
|
155316
|
+
resolvedAlias: (ref, id) => `Environment ${ref} resolved to ${id}.`,
|
|
155317
|
+
couldNotResolve: (ref, detail) => `Could not resolve environment ${ref}: ${detail} If ${ref} is an alias, note that aliases require a team API key.`,
|
|
155316
155318
|
aborted: "Aborted; no environment selected.",
|
|
155317
155319
|
tooManyPages: (pages) => `Stopped listing environments after ${pages} pages. Pass --env explicitly.`
|
|
155318
155320
|
};
|
|
@@ -155330,6 +155332,9 @@ var flowsMessages = {
|
|
|
155330
155332
|
remoteRequiresEnv: "--remote requires an environment. Pass --env <env> or set QAWOLF_ENVIRONMENT.",
|
|
155331
155333
|
flagsRequireRemote: "--env and --include-drafts require --remote"
|
|
155332
155334
|
},
|
|
155335
|
+
run: {
|
|
155336
|
+
requiresEnv: "An environment is required. Pass --env <env> or set QAWOLF_ENVIRONMENT."
|
|
155337
|
+
},
|
|
155333
155338
|
pull: {
|
|
155334
155339
|
requiresEnv: "An environment is required. Pass --env <env> or set QAWOLF_ENVIRONMENT.",
|
|
155335
155340
|
downloadingBundle: "Downloading flows bundle",
|
|
@@ -171314,16 +171319,149 @@ var makeCreateRunContract = (ids) => {
|
|
|
171314
171319
|
};
|
|
171315
171320
|
};
|
|
171316
171321
|
|
|
171322
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/environment.js
|
|
171323
|
+
var reservedRunEnvironmentVariablePrefix = "QAWOLF_";
|
|
171324
|
+
function refusalForRunEnvironmentVariableName(name) {
|
|
171325
|
+
if (name.length < 1 || name.length > 256)
|
|
171326
|
+
return "An environment variable name must be between 1 and 256 characters.";
|
|
171327
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
|
|
171328
|
+
return "An environment variable name may contain only letters, digits and underscores, and must not start with a digit.";
|
|
171329
|
+
if (name.toUpperCase().startsWith(reservedRunEnvironmentVariablePrefix))
|
|
171330
|
+
return `Environment variable names beginning with ${reservedRunEnvironmentVariablePrefix} are reserved by QA Wolf and set for you.`;
|
|
171331
|
+
return;
|
|
171332
|
+
}
|
|
171333
|
+
var maxRunEnvironmentVariables = 100;
|
|
171334
|
+
var maxRunEnvironmentValueLength = 8 * 1024;
|
|
171335
|
+
var runEnvironmentSchema = exports_external.record(exports_external.string(), exports_external.string().max(maxRunEnvironmentValueLength)).superRefine((environment, refinementContext) => {
|
|
171336
|
+
for (const name of Object.keys(environment)) {
|
|
171337
|
+
const refusal = refusalForRunEnvironmentVariableName(name);
|
|
171338
|
+
if (refusal === undefined)
|
|
171339
|
+
continue;
|
|
171340
|
+
refinementContext.addIssue({
|
|
171341
|
+
code: "custom",
|
|
171342
|
+
message: refusal,
|
|
171343
|
+
path: [name]
|
|
171344
|
+
});
|
|
171345
|
+
}
|
|
171346
|
+
}).refine((environment) => Object.keys(environment).length <= maxRunEnvironmentVariables, {
|
|
171347
|
+
error: `At most ${maxRunEnvironmentVariables} environment variables may be supplied.`
|
|
171348
|
+
});
|
|
171349
|
+
|
|
171350
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/files.js
|
|
171351
|
+
var shippableRunFileExtensions = [
|
|
171352
|
+
".cjs",
|
|
171353
|
+
".js",
|
|
171354
|
+
".json",
|
|
171355
|
+
".mjs",
|
|
171356
|
+
".ts",
|
|
171357
|
+
".tsx"
|
|
171358
|
+
];
|
|
171359
|
+
var maxRunFilePathLength = 1024;
|
|
171360
|
+
function isShippableRunFilePathSegment(segment) {
|
|
171361
|
+
if (segment === "" || segment === "." || segment === "..")
|
|
171362
|
+
return false;
|
|
171363
|
+
if (segment.startsWith("."))
|
|
171364
|
+
return false;
|
|
171365
|
+
if (segment.includes("\\"))
|
|
171366
|
+
return false;
|
|
171367
|
+
return !/[\u0000-\u001f\u007f]/.test(segment);
|
|
171368
|
+
}
|
|
171369
|
+
var neverShippedPathSegments = new Set(["node_modules"]);
|
|
171370
|
+
function isShippableRunFilePath(path2) {
|
|
171371
|
+
if (path2.length < 1 || path2.length > maxRunFilePathLength)
|
|
171372
|
+
return false;
|
|
171373
|
+
if (!shippableRunFileExtensions.some((extension) => path2.endsWith(extension)))
|
|
171374
|
+
return false;
|
|
171375
|
+
return path2.split("/").every((segment) => isShippableRunFilePathSegment(segment) && !neverShippedPathSegments.has(segment));
|
|
171376
|
+
}
|
|
171377
|
+
var runFilePathRefusal = `A run file path must end in one of ${shippableRunFileExtensions.join(", ")}, be at most ${maxRunFilePathLength} characters, and be a relative path whose segments are none of: empty, "." or "..", dot-prefixed, "node_modules", or containing a backslash or a control character.`;
|
|
171378
|
+
var runFilePathSchema = exports_external.string().refine(isShippableRunFilePath, {
|
|
171379
|
+
error: runFilePathRefusal
|
|
171380
|
+
});
|
|
171381
|
+
var runPackageJsonPath = "package.json";
|
|
171382
|
+
|
|
171383
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/identity.js
|
|
171384
|
+
var runnerNameSchema = exports_external.enum([
|
|
171385
|
+
"node20Basic",
|
|
171386
|
+
"node20WithAndroid",
|
|
171387
|
+
"node20WithIos",
|
|
171388
|
+
"node20WithPlaywright"
|
|
171389
|
+
]);
|
|
171390
|
+
var runnerIdSchema = exports_external.string().min(1).max(63).regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, "A runner id may contain only lowercase letters, digits and dashes, and must start and end with a letter or digit.");
|
|
171391
|
+
var makeRunnerSchema = () => exports_external.object({
|
|
171392
|
+
gpuAccelerated: exports_external.boolean().describe("Whether the runner was placed on GPU hardware. Chosen by QA Wolf from the team's configuration and what the fleet can provide, not requested."),
|
|
171393
|
+
id: runnerIdSchema.describe("The runner's id, as supplied when launching it."),
|
|
171394
|
+
runnerName: runnerNameSchema.describe("The image requested when the runner was launched. QA Wolf may place it on faster hardware, reported in gpuAccelerated.")
|
|
171395
|
+
});
|
|
171396
|
+
|
|
171397
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/journal.js
|
|
171398
|
+
var journalEntrySchema = exports_external.object({
|
|
171399
|
+
payload: exports_external.unknown(),
|
|
171400
|
+
recordedAt: exports_external.iso.datetime(),
|
|
171401
|
+
sequence: exports_external.number().int().positive()
|
|
171402
|
+
}).refine((entry) => ("payload" in entry), {
|
|
171403
|
+
error: "A journal entry must carry a payload key.",
|
|
171404
|
+
path: ["payload"]
|
|
171405
|
+
}).transform((entry) => ({
|
|
171406
|
+
payload: entry.payload,
|
|
171407
|
+
recordedAt: entry.recordedAt,
|
|
171408
|
+
sequence: entry.sequence
|
|
171409
|
+
}));
|
|
171410
|
+
var knownJournalStreams = [
|
|
171411
|
+
"console",
|
|
171412
|
+
"recorder",
|
|
171413
|
+
"run-events",
|
|
171414
|
+
"run-logs",
|
|
171415
|
+
"run-status"
|
|
171416
|
+
];
|
|
171417
|
+
var maxJournalStreamNameLength = 64;
|
|
171418
|
+
var journalStreamSchema = exports_external.string().max(maxJournalStreamNameLength).regex(/^[a-z][a-z0-9-]{0,63}$/);
|
|
171419
|
+
var readJournalRequestSchema = exports_external.object({
|
|
171420
|
+
runId: exports_external.string().min(1).max(128).optional(),
|
|
171421
|
+
sinceSequence: exports_external.number().int().nonnegative().optional(),
|
|
171422
|
+
stream: journalStreamSchema,
|
|
171423
|
+
tail: exports_external.number().int().positive().optional()
|
|
171424
|
+
});
|
|
171425
|
+
var readJournalResponseSchema = exports_external.object({
|
|
171426
|
+
entries: exports_external.array(journalEntrySchema),
|
|
171427
|
+
hasUnsearchedHistory: exports_external.boolean(),
|
|
171428
|
+
nextSequence: exports_external.number().int().nonnegative(),
|
|
171429
|
+
oldestAvailableSequence: exports_external.number().int().nonnegative()
|
|
171430
|
+
});
|
|
171431
|
+
|
|
171432
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/payloadSize.js
|
|
171433
|
+
var maxRunFilesByteLength = 4 * 1024 * 1024;
|
|
171434
|
+
var maxRunnerRequestEncodedByteLength = 9 * 1024 * 1024;
|
|
171435
|
+
function runFilesByteLength(files) {
|
|
171436
|
+
return Object.entries(files).reduce((total, [path2, content]) => total + utf8ByteLength(content) + utf8ByteLength(path2), 0);
|
|
171437
|
+
}
|
|
171438
|
+
function utf8ByteLength(text) {
|
|
171439
|
+
return Buffer.byteLength(text, "utf8");
|
|
171440
|
+
}
|
|
171441
|
+
var runFilesSchema = exports_external.record(exports_external.string(), exports_external.string()).superRefine((files, refinementContext) => {
|
|
171442
|
+
for (const path2 of Object.keys(files)) {
|
|
171443
|
+
if (isShippableRunFilePath(path2))
|
|
171444
|
+
continue;
|
|
171445
|
+
refinementContext.addIssue({
|
|
171446
|
+
code: "custom",
|
|
171447
|
+
message: runFilePathRefusal,
|
|
171448
|
+
path: [path2]
|
|
171449
|
+
});
|
|
171450
|
+
}
|
|
171451
|
+
}).refine((files) => runFilesByteLength(files) <= maxRunFilesByteLength, {
|
|
171452
|
+
error: `The files in one request may carry at most ${maxRunFilesByteLength} bytes in total.`
|
|
171453
|
+
});
|
|
171454
|
+
|
|
171317
171455
|
// node_modules/@qawolf/api-contracts/dist/v1/automate/index.js
|
|
171318
171456
|
var makeAutomateContract = (ids) => {
|
|
171319
171457
|
const input = exports_external.object({
|
|
171320
171458
|
environmentId: ids.environmentRef,
|
|
171321
|
-
flowIds: exports_external.array(ids.flow).min(1).max(300).refine((flowIds) => new Set(flowIds).size === flowIds.length, "flowIds must be unique").describe("
|
|
171322
|
-
patch: exports_external.string().min(1).max(1e6).optional().describe("Unified diff applied to the automation's working branch before it runs.")
|
|
171459
|
+
flowIds: exports_external.array(ids.flow).min(1).max(300).refine((flowIds) => new Set(flowIds).size === flowIds.length, "flowIds must be unique").describe("IDs of every requested draft flow the automation builds. Reuse an existing draft only when its name matches the requested journey; a generic starter or placeholder is not a match. After publishing, resolve the IDs with `qawolf --json flows list --remote --env <environment-id-or-alias> --include-drafts`."),
|
|
171460
|
+
patch: exports_external.string().min(1).max(1e6).optional().describe("Unified diff applied to the automation's working branch before it runs. It cannot create or rename a selected flow; publish selected flows with Git first.")
|
|
171323
171461
|
});
|
|
171324
171462
|
const output = resource({ automationId: ids.automation }, { urlFieldDescription: "Absolute URL of the automation review page." });
|
|
171325
171463
|
return {
|
|
171326
|
-
description: "Request
|
|
171464
|
+
description: "Request automation for draft flows. First create a named local .flow.ts draft for every requested journey that does not already have a matching draft; never reuse a generic starter or placeholder. Each new draft must start with a JSDoc Goal: description, import flow from @qawolf/flows/web, and use export default flow(...); a comment-only file or direct test(...) call is not a valid draft. Commit and push all changes with Git to publish them, then list remote drafts to resolve every selected ID. Do not use patch to create or rename a selected flow. Finally make one automation request containing all requested flow IDs.",
|
|
171327
171465
|
input,
|
|
171328
171466
|
kind: "write",
|
|
171329
171467
|
name: "automate",
|
|
@@ -171373,6 +171511,7 @@ var makeEnvironmentResourceSchema = (ids) => resource({
|
|
|
171373
171511
|
id: ids.environment,
|
|
171374
171512
|
kind: exports_external.enum(["static", "preview"]).describe("Whether the environment is static or a preview."),
|
|
171375
171513
|
name: exports_external.string(),
|
|
171514
|
+
runConcurrencyLimit: exports_external.string().describe('Maximum number of concurrent runs: a whole number, "unlimited", or a concurrency expression.'),
|
|
171376
171515
|
status: exports_external.enum(["blocked", "needs-investigation", "ready", "running"]).describe("Standing health across the environment's runs: blocked (open blocking bugs), needs-investigation (failures awaiting triage), running (flows executing), ready (none of the above — the greenlit state)."),
|
|
171377
171516
|
terminatedAt: exports_external.iso.datetime().optional().describe("When the environment was terminated, if it has been.")
|
|
171378
171517
|
}, { urlFieldDescription: "Absolute URL of the environment page." });
|
|
@@ -171397,6 +171536,28 @@ var makeFindEnvironmentsContract = (ids) => {
|
|
|
171397
171536
|
};
|
|
171398
171537
|
};
|
|
171399
171538
|
|
|
171539
|
+
// node_modules/@qawolf/api-contracts/dist/v1/environment/getVariable.js
|
|
171540
|
+
var makeGetEnvironmentVariableContract = (ids) => {
|
|
171541
|
+
const input = exports_external.object({
|
|
171542
|
+
environmentId: ids.environmentRef,
|
|
171543
|
+
variableNames: exports_external.array(exports_external.string().min(1).max(255)).min(1).max(100).describe("Names of the environment variables to read. Names match exactly as environment.listVariableNames returns them. Request every name you need in a single call, up to 100 names per call.")
|
|
171544
|
+
});
|
|
171545
|
+
const output = exports_external.object({
|
|
171546
|
+
missingNames: exports_external.array(exports_external.string()).describe("Requested names the environment does not have, sorted alphabetically."),
|
|
171547
|
+
variables: exports_external.array(exports_external.object({
|
|
171548
|
+
name: exports_external.string().describe("The stored variable name."),
|
|
171549
|
+
value: exports_external.string().describe("The variable's secret value. Handle as a secret.")
|
|
171550
|
+
})).describe("The found variables, sorted by name.")
|
|
171551
|
+
});
|
|
171552
|
+
return {
|
|
171553
|
+
description: "Read the values of named environment variables in one call. Values are secrets. Names that do not exist go to missingNames and do not fail the call.",
|
|
171554
|
+
input,
|
|
171555
|
+
kind: "read",
|
|
171556
|
+
name: "environment.getVariable",
|
|
171557
|
+
output
|
|
171558
|
+
};
|
|
171559
|
+
};
|
|
171560
|
+
|
|
171400
171561
|
// node_modules/@qawolf/api-contracts/dist/v1/environment/index.js
|
|
171401
171562
|
var makeGetEnvironmentContract = (ids) => {
|
|
171402
171563
|
const input = exports_external.object({
|
|
@@ -171404,7 +171565,7 @@ var makeGetEnvironmentContract = (ids) => {
|
|
|
171404
171565
|
});
|
|
171405
171566
|
const output = makeEnvironmentResourceSchema(ids);
|
|
171406
171567
|
return {
|
|
171407
|
-
description: "Read a single environment's name, kind, health status, and termination state.",
|
|
171568
|
+
description: "Read a single environment's name, kind, health status, run concurrency limit, and termination state.",
|
|
171408
171569
|
input,
|
|
171409
171570
|
kind: "read",
|
|
171410
171571
|
name: "environment.get",
|
|
@@ -171458,6 +171619,27 @@ var makeCreateEnvironmentContract = (ids) => {
|
|
|
171458
171619
|
};
|
|
171459
171620
|
};
|
|
171460
171621
|
|
|
171622
|
+
// node_modules/@qawolf/api-contracts/dist/v1/environment/update.js
|
|
171623
|
+
var makeUpdateEnvironmentContract = (ids) => {
|
|
171624
|
+
const nameSchema = exports_external.string().trim().min(1).max(255).describe("New display name. Must be unique within the team.");
|
|
171625
|
+
const runConcurrencyLimitSchema = exports_external.string().trim().min(1).max(255).describe('New run concurrency limit: a whole number, "unlimited", or a concurrency expression. The server validates the expression.');
|
|
171626
|
+
const input = exports_external.object({
|
|
171627
|
+
environmentId: ids.environmentRef,
|
|
171628
|
+
name: nameSchema.optional(),
|
|
171629
|
+
runConcurrencyLimit: runConcurrencyLimitSchema.optional()
|
|
171630
|
+
}).refine((update) => update.name !== undefined || update.runConcurrencyLimit !== undefined, {
|
|
171631
|
+
message: "At least one of name or runConcurrencyLimit is required."
|
|
171632
|
+
});
|
|
171633
|
+
const output = makeEnvironmentResourceSchema(ids);
|
|
171634
|
+
return {
|
|
171635
|
+
description: "Update an environment owned by the caller's team and return it in the environment.get shape. Omitted fields remain unchanged.",
|
|
171636
|
+
input,
|
|
171637
|
+
kind: "write",
|
|
171638
|
+
name: "environment.update",
|
|
171639
|
+
output
|
|
171640
|
+
};
|
|
171641
|
+
};
|
|
171642
|
+
|
|
171461
171643
|
// node_modules/@qawolf/api-contracts/dist/v1/tag/index.js
|
|
171462
171644
|
var makeTagResourceSchema = () => resource({
|
|
171463
171645
|
color: exports_external.string().regex(/^#[0-9a-fA-F]{6}$/).describe("The tag's display color as a hex code, assigned by QA Wolf."),
|
|
@@ -171495,6 +171677,9 @@ var makeAddTagToFlowsContract = (ids) => {
|
|
|
171495
171677
|
};
|
|
171496
171678
|
};
|
|
171497
171679
|
|
|
171680
|
+
// node_modules/@qawolf/api-contracts/dist/v1/flow/readiness.js
|
|
171681
|
+
var flowReadinessSchema = exports_external.enum(["draft", "active"]);
|
|
171682
|
+
|
|
171498
171683
|
// node_modules/@qawolf/api-contracts/dist/v1/flow/index.js
|
|
171499
171684
|
var makeListFlowsContract = (ids) => {
|
|
171500
171685
|
const input = exports_external.object({
|
|
@@ -171508,6 +171693,7 @@ var makeListFlowsContract = (ids) => {
|
|
|
171508
171693
|
flowId: ids.flow,
|
|
171509
171694
|
name: exports_external.string(),
|
|
171510
171695
|
path: exports_external.string().describe("The flow file path, relative to the repo."),
|
|
171696
|
+
readiness: flowReadinessSchema.describe("Whether the flow is a draft. Set it with flow.update."),
|
|
171511
171697
|
tags: exports_external.array(exports_external.string())
|
|
171512
171698
|
}, {
|
|
171513
171699
|
urlFieldDescription: "Absolute URL of the flow page in its environment."
|
|
@@ -171522,6 +171708,30 @@ var makeListFlowsContract = (ids) => {
|
|
|
171522
171708
|
};
|
|
171523
171709
|
};
|
|
171524
171710
|
|
|
171711
|
+
// node_modules/@qawolf/api-contracts/dist/v1/flow/update.js
|
|
171712
|
+
var makeUpdateFlowContract = (ids) => {
|
|
171713
|
+
const input = exports_external.object({
|
|
171714
|
+
flowId: ids.flow,
|
|
171715
|
+
readiness: flowReadinessSchema.describe("The readiness to set. Draft flows do not run in triggered suites.")
|
|
171716
|
+
});
|
|
171717
|
+
const output = exports_external.object({
|
|
171718
|
+
flow: resource({
|
|
171719
|
+
flowId: ids.flow,
|
|
171720
|
+
name: exports_external.string(),
|
|
171721
|
+
readiness: flowReadinessSchema.describe("The flow readiness after the update.")
|
|
171722
|
+
}, {
|
|
171723
|
+
urlFieldDescription: "Absolute URL of the flow page."
|
|
171724
|
+
})
|
|
171725
|
+
});
|
|
171726
|
+
return {
|
|
171727
|
+
description: "Move a flow between draft and active readiness. The other statuses shown in the app are derived and cannot be set.",
|
|
171728
|
+
input,
|
|
171729
|
+
kind: "write",
|
|
171730
|
+
name: "flow.update",
|
|
171731
|
+
output
|
|
171732
|
+
};
|
|
171733
|
+
};
|
|
171734
|
+
|
|
171525
171735
|
// node_modules/@qawolf/api-contracts/dist/v1/issue/index.js
|
|
171526
171736
|
var issuePrioritySchema = exports_external.enum([
|
|
171527
171737
|
"unprioritized",
|
|
@@ -171705,7 +171915,8 @@ var makeGetRunContract = (ids) => {
|
|
|
171705
171915
|
const flows = exports_external.array(exports_external.discriminatedUnion("status", [failedFlow, nonFailedFlow])).describe("The run's flows, ordered alphabetically by name.");
|
|
171706
171916
|
const output = resource({
|
|
171707
171917
|
...makeRunSummaryFields(ids),
|
|
171708
|
-
flows
|
|
171918
|
+
flows,
|
|
171919
|
+
runId: ids.run.describe("The run this response describes. Treat it as canonical: it can differ from the id you asked for. A deploy notification returns a run id before the run exists, and if a second notification for the same commit is folded into an earlier run, that id resolves to the earlier run instead.")
|
|
171709
171920
|
}, { urlFieldDescription: "Absolute URL of the run page." });
|
|
171710
171921
|
return {
|
|
171711
171922
|
description: "Get a run's status, per-flow results, and links.",
|
|
@@ -171716,6 +171927,129 @@ var makeGetRunContract = (ids) => {
|
|
|
171716
171927
|
};
|
|
171717
171928
|
};
|
|
171718
171929
|
|
|
171930
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/unreachable.js
|
|
171931
|
+
var runnerUnreachableSchema = exports_external.object({
|
|
171932
|
+
outcome: exports_external.literal("runner-unreachable")
|
|
171933
|
+
});
|
|
171934
|
+
var retryableRunnerUnreachableDescription = "`runner-unreachable` if the runner could not be reached: it may still be starting, it may have terminated after inactivity, or it may be too busy to answer. Nothing was changed, so retrying is safe; if it persists, launch the runner again.";
|
|
171935
|
+
|
|
171936
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/evaluateSnippet.js
|
|
171937
|
+
var maxSnippetCodeLength = 64 * 1024;
|
|
171938
|
+
var makeEvaluateSnippetOnRunnerContract = () => {
|
|
171939
|
+
const input = exports_external.object({
|
|
171940
|
+
code: exports_external.string().min(1).max(maxSnippetCodeLength).describe("The code to evaluate against the runner's live page, in the scope of `filePath` when one is given."),
|
|
171941
|
+
filePath: runFilePathSchema.optional().describe("Path of the file the snippet is evaluated in the scope of. Omit to evaluate a snippet that imports nothing of yours."),
|
|
171942
|
+
files: runFilesSchema.optional().describe("The file named by `filePath` and everything it imports. A runner holds no copy of your project, so a snippet's scope has to travel with it."),
|
|
171943
|
+
id: runnerIdSchema.describe("Id of the runner to evaluate the snippet on.")
|
|
171944
|
+
}).refine((request) => request.filePath === undefined || Object.hasOwn(request.files ?? {}, request.filePath), {
|
|
171945
|
+
error: "A snippet naming a file must carry that file in files.",
|
|
171946
|
+
path: ["files"]
|
|
171947
|
+
});
|
|
171948
|
+
const output = exports_external.discriminatedUnion("outcome", [
|
|
171949
|
+
exports_external.object({
|
|
171950
|
+
errorMessage: exports_external.string().optional().describe("Present when `result` is `error`."),
|
|
171951
|
+
outcome: exports_external.literal("evaluated"),
|
|
171952
|
+
result: exports_external.enum(["success", "stopped", "error"]).describe("`success` if the snippet ran to completion, `stopped` if it was interrupted, `error` if it threw. Whether it ran, not what it evaluated to.")
|
|
171953
|
+
}),
|
|
171954
|
+
runnerUnreachableSchema
|
|
171955
|
+
]);
|
|
171956
|
+
return {
|
|
171957
|
+
description: "Evaluate a snippet against whatever the runner's browser is showing right now. Answers whether the snippet ran, and its error if it threw — not the value it evaluated to, so read anything you want back out of the runner's journal (a snippet's `console.log` lands in the `console` stream). A snippet starts no run and leaves no run-scoped journal entries of its own. `runner-unreachable` if the runner could not be reached or could not evaluate: as well as a runner that is still starting, has terminated after inactivity, or is busy, this covers a runner with no live page to evaluate against — which will never clear, so check that the runner you launched is one that runs a browser before retrying. It is NOT proof the snippet did not run: a snippet that outlives the answer window is still executing when you read this, so do not blindly resubmit one that mutates what the page is looking at.",
|
|
171958
|
+
input,
|
|
171959
|
+
kind: "write",
|
|
171960
|
+
name: "runner.evaluateSnippet",
|
|
171961
|
+
output
|
|
171962
|
+
};
|
|
171963
|
+
};
|
|
171964
|
+
|
|
171965
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/index.js
|
|
171966
|
+
var makeLaunchRunnerContract = () => {
|
|
171967
|
+
const input = exports_external.object({
|
|
171968
|
+
id: runnerIdSchema.describe("Id to launch the runner under, chosen by the caller. Launching the same id again returns the runner already running under it."),
|
|
171969
|
+
runnerName: runnerNameSchema.optional().describe("Image to run. Defaults to the standard web image, node20WithPlaywright.")
|
|
171970
|
+
});
|
|
171971
|
+
const output = makeRunnerSchema().extend({
|
|
171972
|
+
outcome: exports_external.enum(["launched", "already-running"]).describe("`launched` if this call started the runner. `already-running` if a runner was already running under this id and was returned unchanged — no second runner was started.")
|
|
171973
|
+
});
|
|
171974
|
+
return {
|
|
171975
|
+
description: "Launch an interactive runner on the caller's team under an id the caller chooses. Launching the same id again returns the runner already running rather than starting a second one, and the same id with a different runnerName is refused. A runner is not permanent: it terminates on its own after a period of inactivity, and launching the same id after that starts and bills a new runner — so read `outcome` to tell which happened.",
|
|
171976
|
+
input,
|
|
171977
|
+
kind: "write",
|
|
171978
|
+
name: "runner.launch",
|
|
171979
|
+
output
|
|
171980
|
+
};
|
|
171981
|
+
};
|
|
171982
|
+
var makeStopRunnerContract = () => {
|
|
171983
|
+
const input = exports_external.object({
|
|
171984
|
+
id: runnerIdSchema.describe("Id of the runner to stop.")
|
|
171985
|
+
});
|
|
171986
|
+
const output = exports_external.object({
|
|
171987
|
+
id: runnerIdSchema.describe("Id of the runner the call addressed."),
|
|
171988
|
+
outcome: exports_external.enum(["stopped", "not-running"]).describe("`stopped` if the runner was running and has been stopped. `not-running` if no running runner has this id — it was already stopped, was never launched, or terminated on its own after inactivity; these are not distinguished, because the run system keeps no record of a runner once it is gone.")
|
|
171989
|
+
});
|
|
171990
|
+
return {
|
|
171991
|
+
description: "Stop an interactive runner on the caller's team. Stopping a runner that is not running succeeds and reports `not-running`, so a retry needs no special handling.",
|
|
171992
|
+
input,
|
|
171993
|
+
kind: "write",
|
|
171994
|
+
name: "runner.stop",
|
|
171995
|
+
output
|
|
171996
|
+
};
|
|
171997
|
+
};
|
|
171998
|
+
|
|
171999
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/readJournal.js
|
|
172000
|
+
var makeReadRunnerJournalContract = () => {
|
|
172001
|
+
const input = readJournalRequestSchema.extend({
|
|
172002
|
+
id: runnerIdSchema.describe("Id of the runner to read the journal of."),
|
|
172003
|
+
stream: journalStreamSchema.describe(`The stream to read. QA Wolf writes ${knownJournalStreams.join(", ")}; an unwritten stream reads as empty rather than as an error.`)
|
|
172004
|
+
});
|
|
172005
|
+
const output = exports_external.discriminatedUnion("outcome", [
|
|
172006
|
+
readJournalResponseSchema.extend({ outcome: exports_external.literal("read") }),
|
|
172007
|
+
runnerUnreachableSchema
|
|
172008
|
+
]);
|
|
172009
|
+
return {
|
|
172010
|
+
description: `Read a window of one of an interactive runner's journal streams — the newest few, everything after a cursor, or everything belonging to one run. This is how a flow run's outcome and output are followed: \`run-status\` settles it, \`run-logs\` and \`run-events\` carry what it produced, and \`recorder\` carries the browser actions the runner recorded. A read counts as activity, so working through history does not get the runner reaped underneath you. ${retryableRunnerUnreachableDescription}`,
|
|
172011
|
+
input,
|
|
172012
|
+
kind: "read",
|
|
172013
|
+
name: "runner.readJournal",
|
|
172014
|
+
output
|
|
172015
|
+
};
|
|
172016
|
+
};
|
|
172017
|
+
|
|
172018
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/runFlow.js
|
|
172019
|
+
var makeRunFlowOnRunnerContract = () => {
|
|
172020
|
+
const input = exports_external.object({
|
|
172021
|
+
entryPointPath: runFilePathSchema.describe("Path of the flow file to run, as it appears in `files`."),
|
|
172022
|
+
env: runEnvironmentSchema.optional().describe("Environment variables to make available to the run."),
|
|
172023
|
+
files: runFilesSchema.describe("Every file the run needs keyed by its path — the flow file, everything it imports, package.json and tsconfig.json. A runner holds no copy of your project, so what runs is exactly what is sent here."),
|
|
172024
|
+
id: runnerIdSchema.describe("Id of the runner to run the flow on.")
|
|
172025
|
+
}).refine((request) => Object.hasOwn(request.files, request.entryPointPath), {
|
|
172026
|
+
error: "The entry point must be one of the files.",
|
|
172027
|
+
path: ["files"]
|
|
172028
|
+
}).refine((request) => Object.hasOwn(request.files, runPackageJsonPath), {
|
|
172029
|
+
error: `The files must include ${runPackageJsonPath}; the run's npm dependencies are read from it.`,
|
|
172030
|
+
path: ["files"]
|
|
172031
|
+
});
|
|
172032
|
+
const output = exports_external.discriminatedUnion("outcome", [
|
|
172033
|
+
exports_external.object({
|
|
172034
|
+
outcome: exports_external.literal("submitted"),
|
|
172035
|
+
runId: exports_external.string().describe("Id of the run, to follow it by. Read the `run-status` stream of the runner's journal for its outcome, and `run-logs` and `run-events` for its output.")
|
|
172036
|
+
}),
|
|
172037
|
+
runnerUnreachableSchema,
|
|
172038
|
+
exports_external.object({
|
|
172039
|
+
outcome: exports_external.literal("runner-target-mismatch"),
|
|
172040
|
+
requiredRunnerName: runnerNameSchema.describe("The image this flow's execution target needs."),
|
|
172041
|
+
runnerName: runnerNameSchema.describe("The image the runner is actually running.")
|
|
172042
|
+
})
|
|
172043
|
+
]);
|
|
172044
|
+
return {
|
|
172045
|
+
description: "Run a flow on an interactive runner. Answers as soon as the run is accepted, with the id to follow it by — nothing waits for the run to finish. Which browser or device the run needs is read from the flow file's own execution target, so it is not supplied here; when it does not match what the runner is, the call answers `runner-target-mismatch` rather than failing partway through the run. `runner-unreachable` means the answer did not arrive, which is NOT the same as the run not having started: the runner may have accepted it and been too slow to say so, and resubmitting would start a second run that is billed and journalled alongside the first. Read the runner's `run-status` journal stream before resubmitting, and use the newest run id there if one appeared.",
|
|
172046
|
+
input,
|
|
172047
|
+
kind: "write",
|
|
172048
|
+
name: "runner.runFlow",
|
|
172049
|
+
output
|
|
172050
|
+
};
|
|
172051
|
+
};
|
|
172052
|
+
|
|
171719
172053
|
// node_modules/@qawolf/api-contracts/dist/v1/tag/list.js
|
|
171720
172054
|
var makeListTagsContract = (ids) => {
|
|
171721
172055
|
const input = exports_external.object({
|
|
@@ -171748,12 +172082,15 @@ var makeContractsV1 = (ids) => {
|
|
|
171748
172082
|
deleteVariable: makeDeleteEnvironmentVariableContract(resolvedIds),
|
|
171749
172083
|
find: makeFindEnvironmentsContract(resolvedIds),
|
|
171750
172084
|
get: makeGetEnvironmentContract(resolvedIds),
|
|
172085
|
+
getVariable: makeGetEnvironmentVariableContract(resolvedIds),
|
|
171751
172086
|
listVariableNames: makeListEnvironmentVariableNamesContract(resolvedIds),
|
|
171752
|
-
setVariable: makeSetEnvironmentVariableContract(resolvedIds)
|
|
172087
|
+
setVariable: makeSetEnvironmentVariableContract(resolvedIds),
|
|
172088
|
+
update: makeUpdateEnvironmentContract(resolvedIds)
|
|
171753
172089
|
},
|
|
171754
172090
|
flow: {
|
|
171755
172091
|
addTag: makeAddTagToFlowsContract(resolvedIds),
|
|
171756
|
-
list: makeListFlowsContract(resolvedIds)
|
|
172092
|
+
list: makeListFlowsContract(resolvedIds),
|
|
172093
|
+
update: makeUpdateFlowContract(resolvedIds)
|
|
171757
172094
|
},
|
|
171758
172095
|
issue: {
|
|
171759
172096
|
create: makeCreateIssueContract(resolvedIds),
|
|
@@ -171766,6 +172103,13 @@ var makeContractsV1 = (ids) => {
|
|
|
171766
172103
|
find: makeFindRunsContract(resolvedIds),
|
|
171767
172104
|
get: makeGetRunContract(resolvedIds)
|
|
171768
172105
|
},
|
|
172106
|
+
runner: {
|
|
172107
|
+
evaluateSnippet: makeEvaluateSnippetOnRunnerContract(),
|
|
172108
|
+
launch: makeLaunchRunnerContract(),
|
|
172109
|
+
readJournal: makeReadRunnerJournalContract(),
|
|
172110
|
+
runFlow: makeRunFlowOnRunnerContract(),
|
|
172111
|
+
stop: makeStopRunnerContract()
|
|
172112
|
+
},
|
|
171769
172113
|
tag: {
|
|
171770
172114
|
create: makeCreateTagContract(),
|
|
171771
172115
|
list: makeListTagsContract(resolvedIds)
|
|
@@ -174272,7 +174616,7 @@ function startUpdateCheck(deps) {
|
|
|
174272
174616
|
// package.json
|
|
174273
174617
|
var package_default = {
|
|
174274
174618
|
name: "@qawolf/cli",
|
|
174275
|
-
version: "1.7.
|
|
174619
|
+
version: "1.7.2",
|
|
174276
174620
|
description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
174277
174621
|
keywords: [
|
|
174278
174622
|
"automation",
|
|
@@ -174333,7 +174677,7 @@ var package_default = {
|
|
|
174333
174677
|
"@napi-rs/keyring": "1.3.0",
|
|
174334
174678
|
"@oxc-node/core": "0.1.0",
|
|
174335
174679
|
"@playwright/test": "1.62.0",
|
|
174336
|
-
"@qawolf/api-contracts": "0.
|
|
174680
|
+
"@qawolf/api-contracts": "0.22.0",
|
|
174337
174681
|
"@qawolf/emails": "1.1.1",
|
|
174338
174682
|
"@qawolf/flow-targets": "1.0.0",
|
|
174339
174683
|
"@qawolf/flows": "0.1.4",
|
|
@@ -180569,20 +180913,9 @@ async function handleFlowsPull(ctx, opts, deps) {
|
|
|
180569
180913
|
}
|
|
180570
180914
|
}
|
|
180571
180915
|
|
|
180572
|
-
// src/domains/environments/
|
|
180916
|
+
// src/domains/environments/pickEnvironment.ts
|
|
180573
180917
|
var findContract = publicContractsV1.environment.find;
|
|
180574
|
-
async function
|
|
180575
|
-
const explicit = opts.explicit?.trim();
|
|
180576
|
-
if (explicit)
|
|
180577
|
-
return { kind: "resolved", env: explicit };
|
|
180578
|
-
const fromEnvVar = deps.env["QAWOLF_ENVIRONMENT"]?.trim();
|
|
180579
|
-
if (fromEnvVar) {
|
|
180580
|
-
deps.ui.info(environmentsMessages.usingFromEnvVar(fromEnvVar));
|
|
180581
|
-
return { kind: "resolved", env: fromEnvVar };
|
|
180582
|
-
}
|
|
180583
|
-
if (deps.ui.mode !== "human") {
|
|
180584
|
-
return { kind: "error", error: opts.requiredMessage };
|
|
180585
|
-
}
|
|
180918
|
+
async function pickEnvironment(deps) {
|
|
180586
180919
|
const fetched = await fetchAllEnvironments(deps.platformClient);
|
|
180587
180920
|
if (!fetched.ok)
|
|
180588
180921
|
return { kind: "error", error: fetched.error };
|
|
@@ -180655,6 +180988,39 @@ async function fetchAllEnvironments(platformClient) {
|
|
|
180655
180988
|
return { ok: false, error: environmentsMessages.tooManyPages(maxPages) };
|
|
180656
180989
|
}
|
|
180657
180990
|
|
|
180991
|
+
// src/domains/environments/resolveEnvironment.ts
|
|
180992
|
+
var findContract2 = publicContractsV1.environment.find;
|
|
180993
|
+
var getContract = publicContractsV1.environment.get;
|
|
180994
|
+
async function resolveEnvironment(deps, opts) {
|
|
180995
|
+
const explicit = opts.explicit?.trim();
|
|
180996
|
+
if (explicit)
|
|
180997
|
+
return resolveRef2(deps, explicit);
|
|
180998
|
+
const fromEnvVar = deps.env["QAWOLF_ENVIRONMENT"]?.trim();
|
|
180999
|
+
if (fromEnvVar) {
|
|
181000
|
+
deps.ui.info(environmentsMessages.usingFromEnvVar(fromEnvVar));
|
|
181001
|
+
return resolveRef2(deps, fromEnvVar);
|
|
181002
|
+
}
|
|
181003
|
+
if (deps.ui.mode !== "human") {
|
|
181004
|
+
return { kind: "error", error: opts.requiredMessage };
|
|
181005
|
+
}
|
|
181006
|
+
return pickEnvironment(deps);
|
|
181007
|
+
}
|
|
181008
|
+
async function resolveRef2(deps, ref) {
|
|
181009
|
+
const result = await deps.platformClient.callPublicApi(getContract, {
|
|
181010
|
+
environmentId: ref
|
|
181011
|
+
});
|
|
181012
|
+
if (!result.ok) {
|
|
181013
|
+
return {
|
|
181014
|
+
kind: "error",
|
|
181015
|
+
error: environmentsMessages.couldNotResolve(ref, result.error)
|
|
181016
|
+
};
|
|
181017
|
+
}
|
|
181018
|
+
if (result.value.id !== ref) {
|
|
181019
|
+
deps.ui.info(environmentsMessages.resolvedAlias(ref, result.value.id));
|
|
181020
|
+
}
|
|
181021
|
+
return { kind: "resolved", env: result.value.id };
|
|
181022
|
+
}
|
|
181023
|
+
|
|
180658
181024
|
// src/commands/flows/withResolvedEnv.ts
|
|
180659
181025
|
function withResolvedEnv(signals, args, fn2) {
|
|
180660
181026
|
return (opts, command) => {
|
|
@@ -183458,8 +183824,10 @@ function registerFlowsRunCommand(flows, signals) {
|
|
|
183458
183824
|
kindNote: "read with --env"
|
|
183459
183825
|
}).description("Run flows matching [pattern], or every flow when omitted; with --env, pull missing flows from that QA Wolf environment").option("--retries <n>", "Retry each failing flow up to this many times", parseInteger("--retries", { min: 0 }), 0).option("--bail", "Stop the run after the first failure", false).option("--workers <n>", "Parallel worker count for web flows", parseInteger("--workers", { min: 1 }), 1).option("--timeout <ms>", "Default timeout for actions and assertions, in milliseconds", parseInteger("--timeout", { min: 0 }), 30000).option("--video <mode>", "Record video: on | off | retain-on-failure", parseEnum("--video", videoModes), videoDefault).option("--trace <mode>", "Record Playwright trace: on | off | retain-on-failure", parseEnum("--trace", traceModes), traceDefault).option("--har <mode>", "Record HAR network log: on | off | retain-on-failure", parseEnum("--har", harModes), harDefault).option("--har-content <mode>", "HAR response bodies: omit | full (full uses more memory)", parseEnum("--har-content", harContentModes), harContentDefault).option("--output-dir <path>", "Directory for run artifacts (videos, traces, HAR)", defaultOutputDir).option("--junit [path]", "Write a JUnit XML report (default: <output-dir>/junit-report.xml)").option("--headed", "Show the browser window instead of headless", false).option("--env <env>", "Pull and run a flow from this environment (UUID or slug) if not cached locally").option("--deps <dir>", "Use this prepared dependency directory instead of auto-installing the runtime; or set QAWOLF_RUNTIME_DIR to relocate the managed runtime").option("--no-browser-deps", "Skip installing OS-level browser dependencies (Linux --with-deps, which needs root); requires the system libraries to already be present").addHelpText("after", runExamples).action((pattern, opts, command) => {
|
|
183460
183826
|
if (opts.env !== undefined) {
|
|
183461
|
-
|
|
183462
|
-
|
|
183827
|
+
return withResolvedEnv(signals, {
|
|
183828
|
+
explicit: opts.env,
|
|
183829
|
+
requiredMessage: flowsMessages.run.requiresEnv
|
|
183830
|
+
}, (ctx, env3) => handleHybridFlowsRun(ctx, pattern, { ...opts, env: env3 }))(opts, command);
|
|
183463
183831
|
}
|
|
183464
183832
|
return withContext(signals, (ctx) => handleFlowsRun(ctx, pattern, opts))(opts, command);
|
|
183465
183833
|
});
|
|
@@ -184358,4 +184726,4 @@ createProgram({ signals }).parseAsync().catch(() => {
|
|
|
184358
184726
|
process.exitCode = 1;
|
|
184359
184727
|
}).finally(() => flushAndExit(typeof process.exitCode === "number" ? process.exitCode : 0));
|
|
184360
184728
|
|
|
184361
|
-
//# debugId=
|
|
184729
|
+
//# debugId=F77E4418A62E284264756E2164756E21
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qawolf/cli",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"description": "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"automation",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@napi-rs/keyring": "1.3.0",
|
|
62
62
|
"@oxc-node/core": "0.1.0",
|
|
63
63
|
"@playwright/test": "1.62.0",
|
|
64
|
-
"@qawolf/api-contracts": "0.
|
|
64
|
+
"@qawolf/api-contracts": "0.22.0",
|
|
65
65
|
"@qawolf/emails": "1.1.1",
|
|
66
66
|
"@qawolf/flow-targets": "1.0.0",
|
|
67
67
|
"@qawolf/flows": "0.1.4",
|
|
@@ -9,9 +9,10 @@ compatibility: Requires the qawolf CLI on PATH. Install it from @qawolf/cli or u
|
|
|
9
9
|
|
|
10
10
|
`qawolf` runs QA Wolf flows locally and calls the QA Wolf public API.
|
|
11
11
|
|
|
12
|
-
This file is an overview, not a reference. Before using
|
|
13
|
-
`qawolf <command> --help`
|
|
14
|
-
always matches its version
|
|
12
|
+
This file is an overview, not a reference. Before first using a command whose
|
|
13
|
+
flags are not shown here, run `qawolf <command> --help` once. The installed CLI
|
|
14
|
+
is authoritative for flags and always matches its version; reuse that syntax
|
|
15
|
+
for the rest of the task.
|
|
15
16
|
|
|
16
17
|
## Auth
|
|
17
18
|
|
|
@@ -26,57 +27,85 @@ target another deployment host, for example
|
|
|
26
27
|
`https://app.staging.example.com`. `QAWOLF_API_URL` is a separate API endpoint
|
|
27
28
|
and does not select the deployment host used by CLI commands.
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
is
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
Resolve the target environment once per task. Use `QAWOLF_ENVIRONMENT` when it
|
|
31
|
+
is set; otherwise select an id or alias from
|
|
32
|
+
`qawolf --json environment find` and reuse it. Shell invocations may not share
|
|
33
|
+
exported variables, so pass the selected environment explicitly instead of
|
|
34
|
+
rediscovering it. Check the command's help for its environment flag: variable
|
|
35
|
+
commands use `--environment-id`; `qawolf flows run` uses `--env`.
|
|
36
|
+
|
|
37
|
+
If multiple environments are returned and the task context does not identify
|
|
38
|
+
the target, ask instead of guessing. Do not default to the newest environment.
|
|
39
|
+
|
|
40
|
+
`qawolf environment listVariableNames` intentionally returns names only. A
|
|
41
|
+
listed name is enough to reference `process.env.NAME` in flow code; do not ask
|
|
42
|
+
for its value merely because the CLI does not return it.
|
|
33
43
|
|
|
34
44
|
## Output
|
|
35
45
|
|
|
36
46
|
When consuming output programmatically, always pass `--json` (or `--agent`).
|
|
37
47
|
Human-formatted output is not stable across versions. Errors go to stderr;
|
|
38
|
-
a non-zero exit code means the command failed.
|
|
48
|
+
a non-zero exit code means the command failed. Reuse successful read results
|
|
49
|
+
within a task unless a relevant write or target change could make them stale.
|
|
39
50
|
|
|
40
51
|
## Safety: reads vs writes
|
|
41
52
|
|
|
42
|
-
Read commands
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
Read commands do not change team data, but some have operational effects noted
|
|
54
|
+
in their command entry. Write commands act on the real team. Do not invent
|
|
55
|
+
configuration or write speculative values; write only when requested or when
|
|
56
|
+
the task requires missing configuration whose exact value is known. A
|
|
57
|
+
successful write response is confirmation, so do not read immediately only to
|
|
58
|
+
verify it. Never blind-retry a write on timeout: it may have reached the server
|
|
59
|
+
the first time.
|
|
60
|
+
|
|
61
|
+
## Git-backed workflows
|
|
62
|
+
|
|
63
|
+
Inspect `git status` before publishing. Stage and commit only files changed for
|
|
64
|
+
the current task, preserve unrelated worktree changes, then push the task's
|
|
65
|
+
current branch.
|
|
45
66
|
|
|
46
67
|
## Commands
|
|
47
68
|
|
|
48
69
|
<!-- commands-table:start — generated by `bun run generate`, do not edit -->
|
|
49
70
|
|
|
50
|
-
| Command | Kind | What it does
|
|
51
|
-
| -------------------------------------- | -------------------------- |
|
|
52
|
-
| `qawolf auth login` | local | Authenticate with your QA Wolf API key
|
|
53
|
-
| `qawolf auth logout` | local | Remove stored credentials
|
|
54
|
-
| `qawolf auth whoami` | read | Show authentication status
|
|
55
|
-
| `qawolf automate` | write | Request
|
|
56
|
-
| `qawolf doctor` | local | Diagnose problems running flows locally
|
|
57
|
-
| `qawolf environment create` | write | Create an environment on the caller's team and return it in the environment.get shape.
|
|
58
|
-
| `qawolf environment deleteVariable` | write | Remove one environment variable by name. Succeeds whether or not the variable existed.
|
|
59
|
-
| `qawolf environment find` | read | List the team's environments, newest first.
|
|
60
|
-
| `qawolf environment get` | read | Read a single environment's name, kind, health status, and termination state.
|
|
61
|
-
| `qawolf environment
|
|
62
|
-
| `qawolf environment
|
|
63
|
-
| `qawolf
|
|
64
|
-
| `qawolf
|
|
65
|
-
| `qawolf
|
|
66
|
-
| `qawolf
|
|
67
|
-
| `qawolf
|
|
68
|
-
| `qawolf
|
|
69
|
-
| `qawolf
|
|
70
|
-
| `qawolf
|
|
71
|
-
| `qawolf install
|
|
72
|
-
| `qawolf
|
|
73
|
-
| `qawolf
|
|
74
|
-
| `qawolf
|
|
75
|
-
| `qawolf
|
|
76
|
-
| `qawolf
|
|
77
|
-
| `qawolf
|
|
78
|
-
| `qawolf
|
|
79
|
-
| `qawolf
|
|
71
|
+
| Command | Kind | What it does |
|
|
72
|
+
| -------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
73
|
+
| `qawolf auth login` | local | Authenticate with your QA Wolf API key |
|
|
74
|
+
| `qawolf auth logout` | local | Remove stored credentials |
|
|
75
|
+
| `qawolf auth whoami` | read | Show authentication status |
|
|
76
|
+
| `qawolf automate` | write | Request automation for draft flows. First create a named local .flow.ts draft for every requested journey that does not already have a matching draft; never reuse a generic starter or placeholder. Each new draft must start with a JSDoc Goal: description, import flow from @qawolf/flows/web, and use export default flow(...); a comment-only file or direct test(...) call is not a valid draft. Commit and push all changes with Git to publish them, then list remote drafts to resolve every selected ID. Do not use patch to create or rename a selected flow. Finally make one automation request containing all requested flow IDs. |
|
|
77
|
+
| `qawolf doctor` | local | Diagnose problems running flows locally |
|
|
78
|
+
| `qawolf environment create` | write | Create an environment on the caller's team and return it in the environment.get shape. |
|
|
79
|
+
| `qawolf environment deleteVariable` | write | Remove one environment variable by name. Succeeds whether or not the variable existed. |
|
|
80
|
+
| `qawolf environment find` | read | List the team's environments, newest first. |
|
|
81
|
+
| `qawolf environment get` | read | Read a single environment's name, kind, health status, run concurrency limit, and termination state. |
|
|
82
|
+
| `qawolf environment getVariable` | read | Read the values of named environment variables in one call. Values are secrets. Names that do not exist go to missingNames and do not fail the call. |
|
|
83
|
+
| `qawolf environment listVariableNames` | read | Use this to answer which QA Wolf environment variables are available to test code. Returns names only; values never leave the server. |
|
|
84
|
+
| `qawolf environment setVariable` | write | Create or replace an environment variable. If the user asks to create one for "my email" without naming it, use DEFAULT_ENVIRONMENT_EMAIL. The value is never returned. |
|
|
85
|
+
| `qawolf environment update` | write | Update an environment owned by the caller's team and return it in the environment.get shape. Omitted fields remain unchanged. |
|
|
86
|
+
| `qawolf flow addTag` | write | Assign an existing tag to the selected flows. Create tags with tag.create. |
|
|
87
|
+
| `qawolf flow update` | write | Move a flow between draft and active readiness. The other statuses shown in the app are derived and cannot be set. |
|
|
88
|
+
| `qawolf flows list` | local (read with --remote) | List flows matching [pattern] from the local project, or from a QA Wolf environment with --remote |
|
|
89
|
+
| `qawolf flows pull` | read | Download an environment's flows into the local .qawolf/<env>/ cache |
|
|
90
|
+
| `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 |
|
|
91
|
+
| `qawolf init` | local | Scaffold a QA Wolf project in the current directory |
|
|
92
|
+
| `qawolf install` | local | Install every runtime dependency the project's flows need |
|
|
93
|
+
| `qawolf install android` | local | Install Android system images, AVDs, and the Appium driver used by the project's Android flows |
|
|
94
|
+
| `qawolf install browsers` | local | Install Playwright browsers used by the project's web flows |
|
|
95
|
+
| `qawolf install clear` | local | Remove the managed runtime cache (all installed runtime versions) |
|
|
96
|
+
| `qawolf issue create` | write | Create a bug or coverage request issue for the caller's team. Maintenance issues cannot be created through the public API. |
|
|
97
|
+
| `qawolf issue find` | read | List the team's bug reports, maintenance reports, or coverage requests, newest first. |
|
|
98
|
+
| `qawolf issue get` | read | Get an issue by id. |
|
|
99
|
+
| `qawolf run create` | write | Create a run for the selected flows and/or tags in an environment. |
|
|
100
|
+
| `qawolf run find` | read | List an environment's recent runs, newest first. |
|
|
101
|
+
| `qawolf run get` | read | Get a run's status, per-flow results, and links. |
|
|
102
|
+
| `qawolf runner evaluateSnippet` | write | Evaluate a snippet against whatever the runner's browser is showing right now. Answers whether the snippet ran, and its error if it threw — not the value it evaluated to, so read anything you want back out of the runner's journal (a snippet's `console.log` lands in the `console` stream). A snippet starts no run and leaves no run-scoped journal entries of its own. `runner-unreachable` if the runner could not be reached or could not evaluate: as well as a runner that is still starting, has terminated after inactivity, or is busy, this covers a runner with no live page to evaluate against — which will never clear, so check that the runner you launched is one that runs a browser before retrying. It is NOT proof the snippet did not run: a snippet that outlives the answer window is still executing when you read this, so do not blindly resubmit one that mutates what the page is looking at. |
|
|
103
|
+
| `qawolf runner launch` | write | Launch an interactive runner on the caller's team under an id the caller chooses. Launching the same id again returns the runner already running rather than starting a second one, and the same id with a different runnerName is refused. A runner is not permanent: it terminates on its own after a period of inactivity, and launching the same id after that starts and bills a new runner — so read `outcome` to tell which happened. |
|
|
104
|
+
| `qawolf runner readJournal` | read | Read a window of one of an interactive runner's journal streams — the newest few, everything after a cursor, or everything belonging to one run. This is how a flow run's outcome and output are followed: `run-status` settles it, `run-logs` and `run-events` carry what it produced, and `recorder` carries the browser actions the runner recorded. A read counts as activity, so working through history does not get the runner reaped underneath you. `runner-unreachable` if the runner could not be reached: it may still be starting, it may have terminated after inactivity, or it may be too busy to answer. Nothing was changed, so retrying is safe; if it persists, launch the runner again. |
|
|
105
|
+
| `qawolf runner runFlow` | write | Run a flow on an interactive runner. Answers as soon as the run is accepted, with the id to follow it by — nothing waits for the run to finish. Which browser or device the run needs is read from the flow file's own execution target, so it is not supplied here; when it does not match what the runner is, the call answers `runner-target-mismatch` rather than failing partway through the run. `runner-unreachable` means the answer did not arrive, which is NOT the same as the run not having started: the runner may have accepted it and been too slow to say so, and resubmitting would start a second run that is billed and journalled alongside the first. Read the runner's `run-status` journal stream before resubmitting, and use the newest run id there if one appeared. |
|
|
106
|
+
| `qawolf runner stop` | write | Stop an interactive runner on the caller's team. Stopping a runner that is not running succeeds and reports `not-running`, so a retry needs no special handling. |
|
|
107
|
+
| `qawolf tag create` | write | Create a tag on the caller's team. Tags select flows in run.create. |
|
|
108
|
+
| `qawolf tag list` | read | List the team's tags, alphabetical by name. Tag names select flows in run.create. |
|
|
80
109
|
|
|
81
110
|
<!-- commands-table:end -->
|
|
82
111
|
|