@qawolf/cli 1.32.0 → 1.34.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 +25 -1
- package/dist/cli.js +2062 -660
- package/dist/runner-sdk.js +1359 -478
- package/dist/types/runnerSdk/index.d.ts +3 -1
- package/dist/types/runnerSdk/types.d.ts +7 -1
- package/package.json +2 -2
- package/skills/qawolf-cli/SKILL.md +19 -1
- package/skills/qawolf-cli/references/runner.md +63 -16
package/dist/cli.js
CHANGED
|
@@ -8873,6 +8873,37 @@ var interactMessages = {
|
|
|
8873
8873
|
promoteSnapshotAnsweredUnknown: (failureReason) => `The runner answered "${failureReason}", which this version of the CLI does not know how to report. Upgrade with npm install -g @qawolf/cli.`
|
|
8874
8874
|
};
|
|
8875
8875
|
|
|
8876
|
+
// src/core/messages/interactiveRunner/sequence.ts
|
|
8877
|
+
var sequenceMessages = {
|
|
8878
|
+
actionsEmpty: `The sequence holds no actions. Pass a JSON array of at least one, for example '[{"type":"click","button":"left","x":480,"y":260},{"type":"type","text":"hello"}]'.`,
|
|
8879
|
+
actionsInvalidAt: (index, error) => `Action ${index} in the sequence was refused: ${error}`,
|
|
8880
|
+
stdinEmptySequence: 'Nothing arrived on stdin. Pipe the sequence in, or pass the JSON array as the argument instead of "-".',
|
|
8881
|
+
actionsNotJsonArray: `The sequence must be a JSON array of actions, for example '[{"type":"click","button":"left","x":480,"y":260},{"type":"type","text":"hello"}]'.`,
|
|
8882
|
+
actionsEachNeedsAPath: "--screenshot-mode each writes one frame per action, so it needs --screenshot <path>; each frame goes to that path with the action's index before the extension.",
|
|
8883
|
+
actionsFinalNeedsAPath: "--screenshot-mode final asks the runner for one frame after the last action, so it needs --screenshot <path> to write it to. Leave the mode off to perform the actions without a frame.",
|
|
8884
|
+
actionsScreenshotWithModeNone: "--screenshot-mode none asks the runner for no frame, so --screenshot would have nothing to write. Drop one of the two.",
|
|
8885
|
+
actionsEachToStdout: "--screenshot-mode each writes one frame per action, which stdout cannot carry. Give --screenshot a file path.",
|
|
8886
|
+
actionsFailedFramesWritten: (paths) => `${paths.length === 1 ? "Its screen was" : "Its screens were"} written to ${paths.join(", ")}, so look at ${paths.length === 1 ? "that" : "those"} rather than sending anything again.`,
|
|
8887
|
+
actionsFailedScreenshotToStdout: "Its screen was written to stdout as a JPEG, so look at that rather than sending anything again.",
|
|
8888
|
+
actionsPerformed: (count) => `Performed ${count} ${count === 1 ? "action" : "actions"}.`,
|
|
8889
|
+
actionsPerformedOfTotal: (performed, total) => `Performed ${String(performed)} of ${pluralize(total, "action")}.`,
|
|
8890
|
+
actionsFramesUnwritable: (unwritten) => `The runner answered, but ${pluralize(unwritten.length, "frame")} could not be written: ${unwritten.map(({ detail, path }) => `"${path}" (${detail})`).join(", ")}. Whatever the actions did has already happened, so do not send them again: take the screen with qawolf runner screenshot, giving --out a path this process can write to.`,
|
|
8891
|
+
actionsFramesMissing: (indexes) => `The runner answered without the ${indexes.length === 1 ? "screen" : "screens"} after ${indexes.length === 1 ? "action" : "actions"} ${indexes.join(", ")}, so nothing was written for ${indexes.length === 1 ? "it" : "them"}. Whatever the actions did has already happened, so do not send them again: take the screen with qawolf runner screenshot instead.`,
|
|
8892
|
+
actionsPerformedWithoutScreenshot: (count) => `Performed ${pluralize(count, "action")}, but the runner answered without the screen it was asked for. Whatever the actions did has already happened, so do not send them again: take the screen with qawolf runner screenshot instead. If this keeps happening, the platform or this CLI is behind the other: upgrade with npm install -g @qawolf/cli.`,
|
|
8893
|
+
actionsPerformedFramesWritten: (count, paths) => `Performed ${pluralize(count, "action")} and wrote ${pluralize(paths.length, "screen")} to ${paths.join(", ")}.`,
|
|
8894
|
+
actionsPerformedScreenshotToStdout: (count) => `Performed ${pluralize(count, "action")} and wrote the runner's screen to stdout as a JPEG. Stdout holds the image bytes alone, so this line is on stderr and the per-action results are not printed at all: give --screenshot a file path when you need to read them.`,
|
|
8895
|
+
actionsPerformedScreenshotWritten: (count, path) => `Performed ${count} ${count === 1 ? "action" : "actions"} and wrote the runner's screen to ${path}.`,
|
|
8896
|
+
actionsOverLimit: (sent, limit) => `The sequence holds ${pluralize(sent, "action")}, and one request carries at most ${String(limit)}. Send them in batches, ending each at the action that changes the page.`,
|
|
8897
|
+
actionsNotAllSucceeded: (failed, total) => `${String(failed)} of ${pluralize(total, "action")} did not succeed.`,
|
|
8898
|
+
actionsFailedWithoutAResult: (failureReason) => `did not succeed ("${failureReason}"), and the answer carried no result for it. Take a screenshot before repeating anything from it on.`,
|
|
8899
|
+
actionsFailedAt: (index, type, why) => `Action ${index}${type === undefined ? "" : ` (${type})`} ${why}`,
|
|
8900
|
+
actionsLeftUnperformed: (count) => `${count} ${count === 1 ? "action" : "actions"} after it ${count === 1 ? "was" : "were"} not attempted.`,
|
|
8901
|
+
actionsMayHaveHappened: "It may have taken effect: take a screenshot before repeating anything from it on.",
|
|
8902
|
+
actionsOutOfTime: "was not reached before the sequence ran out of its time.",
|
|
8903
|
+
actionsPartlyApplied: (lastCompletedIndex) => `The sequence was partly applied: action ${String(lastCompletedIndex)} is the last one that took effect. Do not send it again as a whole; send only what still has to happen as a new request.`,
|
|
8904
|
+
actionsUnconfirmed: (errorMessage) => `has an unknown effect: ${errorMessage}`
|
|
8905
|
+
};
|
|
8906
|
+
|
|
8876
8907
|
// src/core/messages/interactiveRunner/lifecycle.ts
|
|
8877
8908
|
var pageAt = (url) => `Its runner page is at ${url}`;
|
|
8878
8909
|
var noRunnerId = "No runner id. Pass --runner, set QAWOLF_RUNNER_ID, or run qawolf runner launch first.";
|
|
@@ -8953,7 +8984,8 @@ var interactiveRunnerMessages = {
|
|
|
8953
8984
|
...lifecycleMessages,
|
|
8954
8985
|
...listMessages,
|
|
8955
8986
|
...runMessages,
|
|
8956
|
-
...interactMessages
|
|
8987
|
+
...interactMessages,
|
|
8988
|
+
...sequenceMessages
|
|
8957
8989
|
};
|
|
8958
8990
|
// src/core/messages/runner.ts
|
|
8959
8991
|
var maxListedNames = 5;
|
|
@@ -10013,6 +10045,7 @@ var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]
|
|
|
10013
10045
|
var cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;
|
|
10014
10046
|
var base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;
|
|
10015
10047
|
var base64url = /^[A-Za-z0-9_-]*$/;
|
|
10048
|
+
var domain = /^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
|
|
10016
10049
|
var httpProtocol = /^https?$/;
|
|
10017
10050
|
var e164 = /^\+[1-9]\d{6,14}$/;
|
|
10018
10051
|
var dateSource = `(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`;
|
|
@@ -10043,6 +10076,7 @@ var bigint = /^-?\d+n?$/;
|
|
|
10043
10076
|
var integer = /^-?\d+$/;
|
|
10044
10077
|
var number = /^-?\d+(?:\.\d+)?$/;
|
|
10045
10078
|
var boolean = /^(?:true|false)$/i;
|
|
10079
|
+
var _null = /^null$/i;
|
|
10046
10080
|
var lowercase = /^[^A-Z]*$/;
|
|
10047
10081
|
var uppercase = /^[^a-z]*$/;
|
|
10048
10082
|
|
|
@@ -10963,6 +10997,23 @@ var $ZodBigInt = /* @__PURE__ */ $constructor("$ZodBigInt", (inst, def) => {
|
|
|
10963
10997
|
return payload;
|
|
10964
10998
|
};
|
|
10965
10999
|
});
|
|
11000
|
+
var $ZodNull = /* @__PURE__ */ $constructor("$ZodNull", (inst, def) => {
|
|
11001
|
+
$ZodType.init(inst, def);
|
|
11002
|
+
inst._zod.pattern = _null;
|
|
11003
|
+
inst._zod.values = new Set([null]);
|
|
11004
|
+
inst._zod.parse = (payload, _ctx) => {
|
|
11005
|
+
const input = payload.value;
|
|
11006
|
+
if (input === null)
|
|
11007
|
+
return payload;
|
|
11008
|
+
payload.issues.push({
|
|
11009
|
+
expected: "null",
|
|
11010
|
+
code: "invalid_type",
|
|
11011
|
+
input,
|
|
11012
|
+
inst
|
|
11013
|
+
});
|
|
11014
|
+
return payload;
|
|
11015
|
+
};
|
|
11016
|
+
});
|
|
10966
11017
|
var $ZodUnknown = /* @__PURE__ */ $constructor("$ZodUnknown", (inst, def) => {
|
|
10967
11018
|
$ZodType.init(inst, def);
|
|
10968
11019
|
inst._zod.parse = (payload) => payload;
|
|
@@ -12497,6 +12548,12 @@ function _coercedBigint(Class, params) {
|
|
|
12497
12548
|
...normalizeParams(params)
|
|
12498
12549
|
});
|
|
12499
12550
|
}
|
|
12551
|
+
function _null2(Class, params) {
|
|
12552
|
+
return new Class({
|
|
12553
|
+
type: "null",
|
|
12554
|
+
...normalizeParams(params)
|
|
12555
|
+
});
|
|
12556
|
+
}
|
|
12500
12557
|
function _unknown(Class) {
|
|
12501
12558
|
return new Class({
|
|
12502
12559
|
type: "unknown"
|
|
@@ -13948,6 +14005,9 @@ var ZodUUID = /* @__PURE__ */ $constructor("ZodUUID", (inst, def) => {
|
|
|
13948
14005
|
$ZodUUID.init(inst, def);
|
|
13949
14006
|
ZodStringFormat.init(inst, def);
|
|
13950
14007
|
});
|
|
14008
|
+
function uuid2(params) {
|
|
14009
|
+
return _uuid(ZodUUID, params);
|
|
14010
|
+
}
|
|
13951
14011
|
var ZodURL = /* @__PURE__ */ $constructor("ZodURL", (inst, def) => {
|
|
13952
14012
|
$ZodURL.init(inst, def);
|
|
13953
14013
|
ZodStringFormat.init(inst, def);
|
|
@@ -13955,6 +14015,13 @@ var ZodURL = /* @__PURE__ */ $constructor("ZodURL", (inst, def) => {
|
|
|
13955
14015
|
function url(params) {
|
|
13956
14016
|
return _url(ZodURL, params);
|
|
13957
14017
|
}
|
|
14018
|
+
function httpUrl(params) {
|
|
14019
|
+
return _url(ZodURL, {
|
|
14020
|
+
protocol: httpProtocol,
|
|
14021
|
+
hostname: domain,
|
|
14022
|
+
...normalizeParams(params)
|
|
14023
|
+
});
|
|
14024
|
+
}
|
|
13958
14025
|
var ZodEmoji = /* @__PURE__ */ $constructor("ZodEmoji", (inst, def) => {
|
|
13959
14026
|
$ZodEmoji.init(inst, def);
|
|
13960
14027
|
ZodStringFormat.init(inst, def);
|
|
@@ -14116,6 +14183,14 @@ var ZodBigInt = /* @__PURE__ */ $constructor("ZodBigInt", (inst, def) => {
|
|
|
14116
14183
|
inst.maxValue = bag.maximum ?? null;
|
|
14117
14184
|
inst.format = bag.format ?? null;
|
|
14118
14185
|
});
|
|
14186
|
+
var ZodNull = /* @__PURE__ */ $constructor("ZodNull", (inst, def) => {
|
|
14187
|
+
$ZodNull.init(inst, def);
|
|
14188
|
+
ZodType.init(inst, def);
|
|
14189
|
+
inst._zod.processJSONSchema = (ctx, json, params) => nullProcessor(inst, ctx, json, params);
|
|
14190
|
+
});
|
|
14191
|
+
function _null3(params) {
|
|
14192
|
+
return _null2(ZodNull, params);
|
|
14193
|
+
}
|
|
14119
14194
|
var ZodUnknown = /* @__PURE__ */ $constructor("ZodUnknown", (inst, def) => {
|
|
14120
14195
|
$ZodUnknown.init(inst, def);
|
|
14121
14196
|
ZodType.init(inst, def);
|
|
@@ -14567,6 +14642,12 @@ function refine(fn, _params = {}) {
|
|
|
14567
14642
|
function superRefine(fn, params) {
|
|
14568
14643
|
return _superRefine(fn, params);
|
|
14569
14644
|
}
|
|
14645
|
+
function json(params) {
|
|
14646
|
+
const jsonSchema = lazy(() => {
|
|
14647
|
+
return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record(string2(), jsonSchema)]);
|
|
14648
|
+
});
|
|
14649
|
+
return jsonSchema;
|
|
14650
|
+
}
|
|
14570
14651
|
// node_modules/zod/v4/classic/coerce.js
|
|
14571
14652
|
var exports_coerce = {};
|
|
14572
14653
|
__export(exports_coerce, {
|
|
@@ -15129,9 +15210,10 @@ function createLoggingSystem(opts) {
|
|
|
15129
15210
|
}
|
|
15130
15211
|
|
|
15131
15212
|
// src/core/publicApi/notFoundSubject.ts
|
|
15132
|
-
var
|
|
15213
|
+
var runnerRoutesWithoutLivePod = new Set([
|
|
15133
15214
|
"runner.launch",
|
|
15134
|
-
"runner.list"
|
|
15215
|
+
"runner.list",
|
|
15216
|
+
"runner.recordings"
|
|
15135
15217
|
]);
|
|
15136
15218
|
var runRoutesThatResolveOneRun = new Set([
|
|
15137
15219
|
"run.diagnose",
|
|
@@ -15153,7 +15235,7 @@ function field(input, name) {
|
|
|
15153
15235
|
return typeof value === "string" ? value : undefined;
|
|
15154
15236
|
}
|
|
15155
15237
|
function notFoundSubject(contractName, input) {
|
|
15156
|
-
if (contractName.startsWith("runner.") && !
|
|
15238
|
+
if (contractName.startsWith("runner.") && !runnerRoutesWithoutLivePod.has(contractName)) {
|
|
15157
15239
|
return { kind: "runner", runnerId: field(input, "id") };
|
|
15158
15240
|
}
|
|
15159
15241
|
if (runRoutesThatResolveOneRun.has(contractName)) {
|
|
@@ -16560,6 +16642,10 @@ var agentReplySchema = object({
|
|
|
16560
16642
|
text: string2().describe("What the AI said.")
|
|
16561
16643
|
});
|
|
16562
16644
|
|
|
16645
|
+
// node_modules/@qawolf/api-contracts/dist/v1/agent/wait.js
|
|
16646
|
+
var maxAgentWaitSeconds = 45;
|
|
16647
|
+
var agentWakeReasonSchema = _enum(["reply", "status", "timeout"]).describe('Why a held check answered. "reply" means the AI said something new, "status" means the session settled or needs you, "timeout" means the hold ran out with nothing new.');
|
|
16648
|
+
|
|
16563
16649
|
// node_modules/@qawolf/api-contracts/dist/v1/resource.js
|
|
16564
16650
|
function resource(fields, { urlFieldDescription = "Absolute URL." } = {}) {
|
|
16565
16651
|
return object({
|
|
@@ -16717,16 +16803,18 @@ var identityOrganizationsResponse = object({
|
|
|
16717
16803
|
// node_modules/@qawolf/api-contracts/dist/v1/ids.js
|
|
16718
16804
|
var defaultIdSchemas = {
|
|
16719
16805
|
aiTask: string2().min(1).describe("The id of the AI task."),
|
|
16720
|
-
automation: string2().min(1).describe("The id of the automation."),
|
|
16721
16806
|
chatSession: string2().min(1).describe("The id of the chat session."),
|
|
16807
|
+
deployment: string2().min(1).describe("The id of the deployment."),
|
|
16722
16808
|
environment: string2().min(1).describe("The id of the environment."),
|
|
16723
|
-
environmentRef: string2().min(1).describe("The id or alias of the environment.
|
|
16809
|
+
environmentRef: string2().min(1).describe("The id or alias of the environment. An organization or user API key must also send workspaceId to use an alias."),
|
|
16724
16810
|
flow: string2().min(1).describe("The id of the flow."),
|
|
16725
16811
|
issue: string2().min(1).describe("The id of the issue."),
|
|
16812
|
+
legacyTrigger: string2().min(1).describe("The id of the legacy trigger, from legacyTrigger.find."),
|
|
16726
16813
|
run: string2().min(1).describe("The id of the run."),
|
|
16727
16814
|
trigger: string2().min(1).describe("The id of the trigger."),
|
|
16728
16815
|
workspace: string2().min(1).describe("The id of the workspace.")
|
|
16729
16816
|
};
|
|
16817
|
+
var workspaceForEnvironmentRef = (workspace) => workspace.optional().describe("The workspace the environment belongs to. Required to reference the environment by alias when authenticating with an organization or user API key.");
|
|
16730
16818
|
|
|
16731
16819
|
// node_modules/@qawolf/api-contracts/dist/v1/run/flowSelection.js
|
|
16732
16820
|
var makeFlowSelectionSchema = (ids) => object({
|
|
@@ -16745,7 +16833,8 @@ var makeCreateRunContract = (ids) => {
|
|
|
16745
16833
|
environmentVariables: record(string2(), string2()).optional(),
|
|
16746
16834
|
ignoreRules: boolean2().describe("Ignore this team's sequencing rules for this run. By default they are honoured: the flows your selection depends on are added to the run, and flows run in the order the rules require. Set this to run exactly what you selected, in any order.").default(false),
|
|
16747
16835
|
pullRequestNumber: number2().int().positive().describe("The number of the pull request to associate with the run.").optional(),
|
|
16748
|
-
repository: string2().min(1).describe('The owner/name of the repository, e.g. "acme/web".').optional()
|
|
16836
|
+
repository: string2().min(1).describe('The owner/name of the repository, e.g. "acme/web".').optional(),
|
|
16837
|
+
workspaceId: workspaceForEnvironmentRef(ids.workspace)
|
|
16749
16838
|
}).and(makeFlowSelectionSchema(ids)).refine((value) => !(value.aiTaskId && value.chatSessionId), {
|
|
16750
16839
|
message: "Provide at most one of aiTaskId and chatSessionId: a task already implies its own chat session.",
|
|
16751
16840
|
path: ["chatSessionId"]
|
|
@@ -16773,6 +16862,9 @@ var makeCreateRunContract = (ids) => {
|
|
|
16773
16862
|
};
|
|
16774
16863
|
};
|
|
16775
16864
|
|
|
16865
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/actionSequence.js
|
|
16866
|
+
var maxActionsPerRequest = 10;
|
|
16867
|
+
var screenshotModes = ["none", "final", "each"];
|
|
16776
16868
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/browserAction.js
|
|
16777
16869
|
var maxScreenCoordinate = 20000;
|
|
16778
16870
|
var screenCoordinateSchema = int().min(0).max(maxScreenCoordinate);
|
|
@@ -16899,11 +16991,12 @@ var runnerNameSchema = _enum([
|
|
|
16899
16991
|
var runnerWorkspaceRequirement = "Required when the credential is not bound to a single workspace, such as a user API key or an OAuth connection whose organization owns several.";
|
|
16900
16992
|
var runnerWorkspaceIdDescription = `The workspace the runner belongs to. ${runnerWorkspaceRequirement}`;
|
|
16901
16993
|
var runnerIdSchema = string2().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.");
|
|
16994
|
+
var runnerWatchUrlSchema = url().describe("A QA Wolf page showing what the runner is doing right now, where the person who launched it can watch and take over with their own mouse and keyboard. Opening it requires being signed in to QA Wolf. This link belongs to the user: put it in a normal message as soon as the runner is launched, so they can follow along while the browser is being driven. It is theirs to open, never a page for the caller to fetch or navigate to. The page ends when the runner is terminated, so share it while the runner is alive and report what you found at the end instead of repeating a dead link.");
|
|
16902
16995
|
var makeRunnerSchema = () => object({
|
|
16903
16996
|
gpuAccelerated: boolean2().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."),
|
|
16904
16997
|
id: runnerIdSchema.describe("The runner's id, as supplied when launching it."),
|
|
16905
16998
|
runnerName: runnerNameSchema.describe("The runner family requested when the runner was launched. QA Wolf may place it on faster hardware, reported in gpuAccelerated."),
|
|
16906
|
-
url:
|
|
16999
|
+
url: runnerWatchUrlSchema
|
|
16907
17000
|
});
|
|
16908
17001
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/unreachable.js
|
|
16909
17002
|
var runnerUnreachableFailureReason = "runner-unreachable";
|
|
@@ -17190,13 +17283,13 @@ var screenshotImageSchema = string2().min(1).describe("The screenshot, as a base
|
|
|
17190
17283
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/performAction.js
|
|
17191
17284
|
var unreachableDescription = "`runner-unreachable` if the runner could not be reached: it may still be starting, it may have terminated after inactivity, or it may have stopped answering mid-action. This does not mean the action was not performed — take a screenshot before repeating it.";
|
|
17192
17285
|
var notSupportedOnMobileDescription = "`action-not-supported-on-mobile` if the runner is a mobile device and this action has no touchscreen equivalent: `double_click`, `scroll`, `move`, `keypress` and `navigate`, and a `click` whose `button` is not `left`, are all pointer-device concepts a touchscreen has nothing to offer for. `click` taps, `drag` swipes between its path's first and last point, and `type` types into whatever the last tap focused.";
|
|
17193
|
-
var withScreenshotDescription = "With `withScreenshot: true`, the answer also carries `imageJpegBase64`: a screenshot taken after the action
|
|
17286
|
+
var withScreenshotDescription = "With `withScreenshot: true`, the answer also carries `imageJpegBase64`: a screenshot taken after the action. One call instead of `runner.performAction` followed by `runner.takeScreenshot`. The action itself is performed exactly as without the option; if the screen could serve no frame after it — a display restart, or a `navigate` on a desktop that is still starting — the action's result comes back without `imageJpegBase64`, so take a screenshot separately rather than repeating the action. On a mobile runner the screenshot is the device's own, taken right after a performed action.";
|
|
17194
17287
|
var maxActionErrorMessageLength = 1000;
|
|
17195
17288
|
var makePerformActionOnRunnerContract = (ids) => {
|
|
17196
17289
|
const input = object({
|
|
17197
|
-
action: browserActionSchema.describe(
|
|
17290
|
+
action: browserActionSchema.describe(`The action to perform. Coordinates are whole pixels on the runner's virtual desktop, in the same space as \`runner.takeScreenshot\`. The action shapes follow the computer-use vocabulary, minus \`screenshot\` (use \`runner.takeScreenshot\`) and \`wait\` (delay on the caller's side). A \`navigate\` does not go through the screen, so a screen that is not ready does not stop it. ${notSupportedOnMobileDescription}`),
|
|
17198
17291
|
id: runnerIdSchema.describe("Id of the runner to act on."),
|
|
17199
|
-
withScreenshot: boolean2().optional().describe(
|
|
17292
|
+
withScreenshot: boolean2().optional().describe(withScreenshotDescription),
|
|
17200
17293
|
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
17201
17294
|
});
|
|
17202
17295
|
const screenshotAfterAction = screenshotImageSchema.optional();
|
|
@@ -17226,7 +17319,7 @@ var makePerformActionOnRunnerContract = (ids) => {
|
|
|
17226
17319
|
openWorldHint: true,
|
|
17227
17320
|
readOnlyHint: false
|
|
17228
17321
|
},
|
|
17229
|
-
description: `Perform one raw browser action on an interactive runner: click, double_click, move, drag, scroll, keypress, type, or navigate.
|
|
17322
|
+
description: `Perform one raw browser action on an interactive runner: click, double_click, move, drag, scroll, keypress, type, or navigate. One action per request, and the runner serves one at a time. A success means the action took effect. \`action-failed\`, with a reason, if it reached the runner and did not take effect. On a runner image with a browser, the first action on a runner that has never run anything starts its browser and waits for it, so it can take up to a minute to answer — no \`runner.runFlow\` is needed before acting; if the browser is still starting when the wait runs out, the answer is \`screen-not-ready\` and retrying converges. On a mobile runner, the same \`screen-needs-a-run\` and \`screen-not-ready\` outcomes mean no Appium session has started yet, or it did not answer this instant; \`runner.runFlow\` is what starts one, same as a browser. \`screen-needs-a-run\` if the browser could not be started that way — usually a runner whose runs all finished without starting its desktop; call \`runner.runFlow\` with a flow that opens a browser. ${screenNotReadyDescription} ${runnerHasNoScreenDescription} ${unreachableDescription}`,
|
|
17230
17323
|
input,
|
|
17231
17324
|
kind: "write",
|
|
17232
17325
|
name: "runner.performAction",
|
|
@@ -17234,6 +17327,214 @@ var makePerformActionOnRunnerContract = (ids) => {
|
|
|
17234
17327
|
};
|
|
17235
17328
|
};
|
|
17236
17329
|
|
|
17330
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/performActionsSteps.js
|
|
17331
|
+
var outOfTimeFailureReason = "out-of-time";
|
|
17332
|
+
var unconfirmedFailureReason = "action-unconfirmed";
|
|
17333
|
+
var actionIndexSchema = int().min(0).max(maxActionsPerRequest - 1).describe("Position of the action in the request's `actions`, from 0.");
|
|
17334
|
+
var screenshotAfterStep = screenshotImageSchema.optional();
|
|
17335
|
+
var performedStepSchema = object({
|
|
17336
|
+
effect: literal("performed"),
|
|
17337
|
+
imageJpegBase64: screenshotAfterStep,
|
|
17338
|
+
index: actionIndexSchema,
|
|
17339
|
+
outcome: literal("success")
|
|
17340
|
+
});
|
|
17341
|
+
var failedStepSchema = discriminatedUnion("failureReason", [
|
|
17342
|
+
object({
|
|
17343
|
+
effect: literal("not-performed"),
|
|
17344
|
+
errorMessage: string2().max(maxActionErrorMessageLength).describe("What stopped the action from taking effect."),
|
|
17345
|
+
failureReason: literal("action-failed"),
|
|
17346
|
+
imageJpegBase64: screenshotAfterStep,
|
|
17347
|
+
index: actionIndexSchema,
|
|
17348
|
+
outcome: literal("failure")
|
|
17349
|
+
}),
|
|
17350
|
+
object({
|
|
17351
|
+
effect: literal("unknown"),
|
|
17352
|
+
failureReason: literal(runnerUnreachableFailureReason),
|
|
17353
|
+
index: actionIndexSchema,
|
|
17354
|
+
outcome: literal("failure")
|
|
17355
|
+
}),
|
|
17356
|
+
object({
|
|
17357
|
+
effect: literal("unknown"),
|
|
17358
|
+
errorMessage: string2().max(maxActionErrorMessageLength).describe("How the runner's screen went quiet with the action in flight."),
|
|
17359
|
+
failureReason: literal(unconfirmedFailureReason),
|
|
17360
|
+
index: actionIndexSchema,
|
|
17361
|
+
outcome: literal("failure")
|
|
17362
|
+
}),
|
|
17363
|
+
object({
|
|
17364
|
+
effect: literal("not-performed"),
|
|
17365
|
+
failureReason: _enum([
|
|
17366
|
+
...screenFailureReasons,
|
|
17367
|
+
"action-not-supported-on-mobile",
|
|
17368
|
+
outOfTimeFailureReason
|
|
17369
|
+
]),
|
|
17370
|
+
index: actionIndexSchema,
|
|
17371
|
+
outcome: literal("failure")
|
|
17372
|
+
})
|
|
17373
|
+
]);
|
|
17374
|
+
var stepResultSchema = discriminatedUnion("outcome", [
|
|
17375
|
+
performedStepSchema,
|
|
17376
|
+
failedStepSchema
|
|
17377
|
+
]);
|
|
17378
|
+
var sequenceFailureReasons = [
|
|
17379
|
+
"action-failed",
|
|
17380
|
+
...screenFailureReasons,
|
|
17381
|
+
"action-not-supported-on-mobile",
|
|
17382
|
+
outOfTimeFailureReason,
|
|
17383
|
+
unconfirmedFailureReason,
|
|
17384
|
+
runnerUnreachableFailureReason
|
|
17385
|
+
];
|
|
17386
|
+
|
|
17387
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/performActions.js
|
|
17388
|
+
var actionsDescription = `The actions to perform, in order, each exactly what \`runner.performAction\` takes: coordinates are whole pixels on the runner's virtual desktop, in the same space as \`runner.takeScreenshot\`, and the shapes follow the computer-use vocabulary. At most ${maxActionsPerRequest} per request. For when the next several steps are already known — click a field, type into it, press Enter — and one look at the screen at the end is enough. They run back to back on the runner, with no pause between them: put in one request only steps whose targets are all on the screen you last saw and are not moved by the steps before them, and when a step changes the page — a submit, a navigation, opening a menu — make it the last one and read the frame.`;
|
|
17389
|
+
var screenshotModeDescription = "`final`, the default, answers with one screenshot in `imageJpegBase64`: the screen after the last action, or, after a stop, as it stands. `each` puts a screenshot after every action on its entry in `results`, for when each step must be checked; every frame is a full image, so keep such sequences short. `none` sends no screenshot.";
|
|
17390
|
+
var stopOnFailureDescription = "`true`, the default, leaves the remaining actions unperformed once one fails. `false` carries on past an action that reached the runner and did not take effect (`action-failed`, `action-not-supported-on-mobile`), so use it only for actions that do not depend on each other: a `type` after a `click` that failed goes to whatever has focus. A runner that cannot be reached, a screen that cannot serve, or running out of time ends the sequence either way.";
|
|
17391
|
+
var finalImageDescription = "Present with `screenshotMode: final` when the runner still answered: the screen after the last action performed, or, after a stop, as it stands.";
|
|
17392
|
+
var makePerformActionsOnRunnerContract = (ids) => {
|
|
17393
|
+
const input = object({
|
|
17394
|
+
actions: array(browserActionSchema).min(1).max(maxActionsPerRequest).describe(actionsDescription),
|
|
17395
|
+
id: runnerIdSchema.describe("Id of the runner to act on."),
|
|
17396
|
+
screenshotMode: _enum(screenshotModes).optional().describe(screenshotModeDescription),
|
|
17397
|
+
stopOnFailure: boolean2().optional().describe(stopOnFailureDescription),
|
|
17398
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
17399
|
+
});
|
|
17400
|
+
const output = discriminatedUnion("outcome", [
|
|
17401
|
+
object({
|
|
17402
|
+
imageJpegBase64: screenshotImageSchema.optional().describe(finalImageDescription),
|
|
17403
|
+
lastCompletedIndex: actionIndexSchema.describe("Index of the last action that took effect: here, the last one sent."),
|
|
17404
|
+
outcome: literal("success"),
|
|
17405
|
+
results: array(performedStepSchema).min(1).max(maxActionsPerRequest).describe("One entry per action, in order.")
|
|
17406
|
+
}),
|
|
17407
|
+
object({
|
|
17408
|
+
errorMessage: string2().max(maxActionErrorMessageLength).optional().describe("What stopped the first failed action, when its reason is `action-failed` or `action-unconfirmed`."),
|
|
17409
|
+
failedIndex: actionIndexSchema.describe("Index of the first action that did not take effect."),
|
|
17410
|
+
failureReason: _enum(sequenceFailureReasons).describe("Why that action did not take effect, the same as on its entry in `results`."),
|
|
17411
|
+
imageJpegBase64: screenshotImageSchema.optional().describe(finalImageDescription),
|
|
17412
|
+
lastCompletedIndex: actionIndexSchema.optional().describe("Index of the last action that took effect. Absent when none did. With `stopOnFailure: false` it can follow a failed action, so `results` says which ones before it took effect."),
|
|
17413
|
+
outcome: literal("failure"),
|
|
17414
|
+
results: array(stepResultSchema).min(1).max(maxActionsPerRequest).describe("One entry per action reached, in order. Actions after a stop have no entry."),
|
|
17415
|
+
stoppedEarly: boolean2().describe("Whether actions after the failure were left unperformed. False when every action was attempted.")
|
|
17416
|
+
})
|
|
17417
|
+
]);
|
|
17418
|
+
return {
|
|
17419
|
+
annotations: {
|
|
17420
|
+
destructiveHint: true,
|
|
17421
|
+
openWorldHint: true,
|
|
17422
|
+
readOnlyHint: false
|
|
17423
|
+
},
|
|
17424
|
+
description: "Perform a sequence of raw browser actions on an interactive runner in one request, one after another, and answer with what happened to each. A `success` means every action took effect. A `failure` names the first action that did not (`failedIndex`, `failureReason`), the last one that did (`lastCompletedIndex`), and whether actions were left unperformed (`stoppedEarly`); `results` has one entry per action reached, in order. Read `effect` on an entry before sending its action again: `performed` means the runner did it; `not-performed` means the runner answered that it did not take effect, though a `navigate` that timed out may still be loading; `unknown` means the runner stopped answering with the action in flight (`runner-unreachable`), or its screen went quiet mid-action (`action-unconfirmed`), and it may have taken effect — a click that submitted a form, say — so take a screenshot instead of repeating it. A sequence gets about a minute to start its actions; an action it did not reach in time is `out-of-time`, nothing after it was attempted, and the rest can go in a new request. Everything `runner.performAction` says about a runner that has never run anything, a mobile runner, and the `screen-needs-a-run`, `screen-not-ready`, `runner-has-no-screen` and `action-not-supported-on-mobile` reasons holds for each action here.",
|
|
17425
|
+
input,
|
|
17426
|
+
kind: "write",
|
|
17427
|
+
name: "runner.performActions",
|
|
17428
|
+
output
|
|
17429
|
+
};
|
|
17430
|
+
};
|
|
17431
|
+
|
|
17432
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/recording.js
|
|
17433
|
+
var recordingIdSchema = uuid2();
|
|
17434
|
+
var runnerRecordingCommandSchema = discriminatedUnion("action", [
|
|
17435
|
+
strictObject({
|
|
17436
|
+
action: literal("start"),
|
|
17437
|
+
recordingId: recordingIdSchema
|
|
17438
|
+
}),
|
|
17439
|
+
strictObject({ action: literal("stop"), recordingId: recordingIdSchema }),
|
|
17440
|
+
strictObject({ action: literal("auto"), enabled: boolean2() }),
|
|
17441
|
+
strictObject({ action: literal("status") })
|
|
17442
|
+
]);
|
|
17443
|
+
var runnerRecordingSchema = object({
|
|
17444
|
+
endedAt: exports_iso.datetime(),
|
|
17445
|
+
id: recordingIdSchema,
|
|
17446
|
+
mode: _enum(["manual", "automatic"]),
|
|
17447
|
+
runIds: array(string2()),
|
|
17448
|
+
runnerId: runnerIdSchema,
|
|
17449
|
+
runnerInstanceId: uuid2(),
|
|
17450
|
+
startedAt: exports_iso.datetime(),
|
|
17451
|
+
status: _enum(["ready", "failed"]),
|
|
17452
|
+
version: literal(1)
|
|
17453
|
+
});
|
|
17454
|
+
var runnerRecordingStateSchema = object({
|
|
17455
|
+
active: object({
|
|
17456
|
+
id: recordingIdSchema,
|
|
17457
|
+
mode: _enum(["manual", "automatic"]),
|
|
17458
|
+
startedAt: exports_iso.datetime()
|
|
17459
|
+
}).optional(),
|
|
17460
|
+
auto: _enum(["on", "off", "default"])
|
|
17461
|
+
});
|
|
17462
|
+
var runnerRecordingResultSchema = discriminatedUnion("outcome", [
|
|
17463
|
+
object({
|
|
17464
|
+
outcome: literal("success"),
|
|
17465
|
+
recording: runnerRecordingSchema.optional(),
|
|
17466
|
+
state: runnerRecordingStateSchema
|
|
17467
|
+
}),
|
|
17468
|
+
object({
|
|
17469
|
+
failureReason: _enum([
|
|
17470
|
+
"unsupported",
|
|
17471
|
+
"screen-not-ready",
|
|
17472
|
+
"recording-in-progress",
|
|
17473
|
+
"recording-not-found",
|
|
17474
|
+
"recording-id-used",
|
|
17475
|
+
"runner-unreachable",
|
|
17476
|
+
"recording-failed"
|
|
17477
|
+
]),
|
|
17478
|
+
outcome: literal("failure")
|
|
17479
|
+
})
|
|
17480
|
+
]);
|
|
17481
|
+
var makeRecordOnRunnerContract = (ids) => {
|
|
17482
|
+
const input = object({
|
|
17483
|
+
command: runnerRecordingCommandSchema,
|
|
17484
|
+
id: runnerIdSchema,
|
|
17485
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
17486
|
+
});
|
|
17487
|
+
const output = object({
|
|
17488
|
+
result: runnerRecordingResultSchema,
|
|
17489
|
+
url: url()
|
|
17490
|
+
});
|
|
17491
|
+
return {
|
|
17492
|
+
annotations: {
|
|
17493
|
+
destructiveHint: false,
|
|
17494
|
+
openWorldHint: false,
|
|
17495
|
+
readOnlyHint: false
|
|
17496
|
+
},
|
|
17497
|
+
description: "Control video recording on a Playwright runner. Start requires a caller-generated UUID; stop targets that UUID so retries cannot stop a later recording. Manual capture spans runs and suppresses automatic capture. Auto settings affect subsequent full runs. The URL opens recording history after the runner terminates. Videos may be unavailable after abrupt pod loss.",
|
|
17498
|
+
input,
|
|
17499
|
+
kind: "write",
|
|
17500
|
+
name: "runner.record",
|
|
17501
|
+
output
|
|
17502
|
+
};
|
|
17503
|
+
};
|
|
17504
|
+
var makeRunnerRecordingsContract = (ids) => {
|
|
17505
|
+
const input = object({
|
|
17506
|
+
id: runnerIdSchema,
|
|
17507
|
+
pageToken: string2().max(4096).optional(),
|
|
17508
|
+
recordingId: recordingIdSchema.optional(),
|
|
17509
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
17510
|
+
});
|
|
17511
|
+
const output = object({
|
|
17512
|
+
nextPageToken: string2().optional(),
|
|
17513
|
+
recordings: array(runnerRecordingSchema.extend({
|
|
17514
|
+
url: url(),
|
|
17515
|
+
videoUrl: url().optional()
|
|
17516
|
+
}))
|
|
17517
|
+
});
|
|
17518
|
+
return {
|
|
17519
|
+
annotations: {
|
|
17520
|
+
destructiveHint: false,
|
|
17521
|
+
openWorldHint: false,
|
|
17522
|
+
readOnlyHint: true
|
|
17523
|
+
},
|
|
17524
|
+
description: "Read a runner's finalized recordings from workspace storage, including after the runner terminates. Omit recordingId to list recordings in storage-key order; follow nextPageToken for more. An empty list for a specific recording means it is not published or does not exist. Video URLs expire; platform URLs remain stable.",
|
|
17525
|
+
input,
|
|
17526
|
+
kind: "read",
|
|
17527
|
+
name: "runner.recordings",
|
|
17528
|
+
output
|
|
17529
|
+
};
|
|
17530
|
+
};
|
|
17531
|
+
function makeRecordingContracts(ids) {
|
|
17532
|
+
return {
|
|
17533
|
+
record: makeRecordOnRunnerContract(ids),
|
|
17534
|
+
recordings: makeRunnerRecordingsContract(ids)
|
|
17535
|
+
};
|
|
17536
|
+
}
|
|
17537
|
+
|
|
17237
17538
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/runSelection.js
|
|
17238
17539
|
var runSelectionSchema = object({
|
|
17239
17540
|
endLine: int().positive().describe("Last line to run, 1-indexed and inclusive."),
|
|
@@ -17252,7 +17553,16 @@ var isTimezone = (value) => {
|
|
|
17252
17553
|
return false;
|
|
17253
17554
|
}
|
|
17254
17555
|
};
|
|
17255
|
-
var triggerCadenceSchema = _enum(["hourly", "daily"]).describe("How often the schedule fires.");
|
|
17556
|
+
var triggerCadenceSchema = _enum(["hourly", "daily", "weekly"]).describe("How often the schedule fires.");
|
|
17557
|
+
var triggerDayOfWeekSchema = _enum([
|
|
17558
|
+
"monday",
|
|
17559
|
+
"tuesday",
|
|
17560
|
+
"wednesday",
|
|
17561
|
+
"thursday",
|
|
17562
|
+
"friday",
|
|
17563
|
+
"saturday",
|
|
17564
|
+
"sunday"
|
|
17565
|
+
]).describe("A day of the week, named rather than numbered.");
|
|
17256
17566
|
var triggerActionKindSchema = _enum(["createSuite", "generativeSuite"]).describe("createSuite runs the flows named by flowIds and tagNames. generativeSuite lets QA Wolf choose the flows from the deployment's changes, and is not available to a schedule.");
|
|
17257
17567
|
var makeTriggerActionSchema = (ids) => object({
|
|
17258
17568
|
flowIds: array(ids.flow).default([]).describe("Flows to run. Combined with tagNames."),
|
|
@@ -17264,10 +17574,11 @@ var makeTriggerActionSchema = (ids) => object({
|
|
|
17264
17574
|
var makeTriggerScheduleFields = (ids) => ({
|
|
17265
17575
|
action: makeTriggerActionSchema(ids),
|
|
17266
17576
|
cadence: triggerCadenceSchema,
|
|
17577
|
+
dayOfWeek: triggerDayOfWeekSchema.optional().describe("The day a weekly schedule fires on, read in timezoneId. Required when cadence is weekly."),
|
|
17267
17578
|
environmentId: ids.environmentId.describe("The environment the scheduled runs happen in."),
|
|
17268
17579
|
minuteOfHour: number2().int().min(0).max(59).optional().describe("Minutes past the hour an hourly schedule fires. Defaults to 0."),
|
|
17269
|
-
timeOfDay: string2().regex(/^([01]\d|2[0-3]):[0-5]\d$/).optional().describe('The wall clock time a daily schedule fires, as "HH:mm". Required when cadence is daily.'),
|
|
17270
|
-
timezoneId: string2().min(1).refine(isTimezone, "Expected an IANA timezone, such as America/New_York.").optional().describe("The IANA timezone that timeOfDay is read in, such as America/New_York. Required when cadence is daily.")
|
|
17580
|
+
timeOfDay: string2().regex(/^([01]\d|2[0-3]):[0-5]\d$/).optional().describe('The wall clock time a daily or weekly schedule fires, as "HH:mm". Required when cadence is daily or weekly.'),
|
|
17581
|
+
timezoneId: string2().min(1).refine(isTimezone, "Expected an IANA timezone, such as America/New_York.").optional().describe("The IANA timezone that timeOfDay is read in, such as America/New_York. Required when cadence is daily or weekly.")
|
|
17271
17582
|
});
|
|
17272
17583
|
var makeTriggerDeploymentFields = (ids) => ({
|
|
17273
17584
|
action: makeTriggerActionSchema(ids),
|
|
@@ -17277,6 +17588,98 @@ var makeTriggerDeploymentFields = (ids) => ({
|
|
|
17277
17588
|
environmentPattern: string2().min(1).optional().describe("Only fire for deployments to environments matching this pattern. Mutually exclusive with environmentIds.")
|
|
17278
17589
|
});
|
|
17279
17590
|
|
|
17591
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/configurationRules.js
|
|
17592
|
+
var checkAction = (action, ctx) => {
|
|
17593
|
+
const namesFlows = action.flowIds.length + action.tagNames.length > 0;
|
|
17594
|
+
if (action.kind === "generativeSuite") {
|
|
17595
|
+
if (namesFlows) {
|
|
17596
|
+
ctx.addIssue({
|
|
17597
|
+
code: "custom",
|
|
17598
|
+
message: "flowIds and tagNames do not apply when the action is generativeSuite.",
|
|
17599
|
+
path: ["action", "flowIds"]
|
|
17600
|
+
});
|
|
17601
|
+
}
|
|
17602
|
+
return;
|
|
17603
|
+
}
|
|
17604
|
+
if (action.instructions !== undefined) {
|
|
17605
|
+
ctx.addIssue({
|
|
17606
|
+
code: "custom",
|
|
17607
|
+
message: "instructions apply to a generativeSuite action only.",
|
|
17608
|
+
path: ["action", "instructions"]
|
|
17609
|
+
});
|
|
17610
|
+
}
|
|
17611
|
+
if (!namesFlows) {
|
|
17612
|
+
ctx.addIssue({
|
|
17613
|
+
code: "custom",
|
|
17614
|
+
message: "At least one flow or tag is required.",
|
|
17615
|
+
path: ["action", "flowIds"]
|
|
17616
|
+
});
|
|
17617
|
+
}
|
|
17618
|
+
};
|
|
17619
|
+
var requiredScheduleFields = {
|
|
17620
|
+
daily: ["timeOfDay", "timezoneId"],
|
|
17621
|
+
hourly: [],
|
|
17622
|
+
weekly: ["dayOfWeek", "timeOfDay", "timezoneId"]
|
|
17623
|
+
};
|
|
17624
|
+
var cadencesUsingField = {
|
|
17625
|
+
dayOfWeek: "a weekly cadence",
|
|
17626
|
+
minuteOfHour: "an hourly cadence",
|
|
17627
|
+
timeOfDay: "a daily or weekly cadence",
|
|
17628
|
+
timezoneId: "a daily or weekly cadence"
|
|
17629
|
+
};
|
|
17630
|
+
var scheduleFields = [
|
|
17631
|
+
"dayOfWeek",
|
|
17632
|
+
"minuteOfHour",
|
|
17633
|
+
"timeOfDay",
|
|
17634
|
+
"timezoneId"
|
|
17635
|
+
];
|
|
17636
|
+
var checkScheduleFields = (value, ctx) => {
|
|
17637
|
+
const required = requiredScheduleFields[value.cadence];
|
|
17638
|
+
for (const field of scheduleFields) {
|
|
17639
|
+
const isSupplied = value[field] !== undefined;
|
|
17640
|
+
if (required.includes(field)) {
|
|
17641
|
+
if (!isSupplied) {
|
|
17642
|
+
ctx.addIssue({
|
|
17643
|
+
code: "custom",
|
|
17644
|
+
message: `${field} is required when cadence is ${value.cadence}.`,
|
|
17645
|
+
path: [field]
|
|
17646
|
+
});
|
|
17647
|
+
}
|
|
17648
|
+
continue;
|
|
17649
|
+
}
|
|
17650
|
+
if (isSupplied && !(value.cadence === "hourly" && field === "minuteOfHour")) {
|
|
17651
|
+
ctx.addIssue({
|
|
17652
|
+
code: "custom",
|
|
17653
|
+
message: `${field} applies to ${cadencesUsingField[field]} only.`,
|
|
17654
|
+
path: [field]
|
|
17655
|
+
});
|
|
17656
|
+
}
|
|
17657
|
+
}
|
|
17658
|
+
};
|
|
17659
|
+
var checkSchedule = (value, ctx) => {
|
|
17660
|
+
checkScheduleFields(value, ctx);
|
|
17661
|
+
if (value.action.kind === "generativeSuite") {
|
|
17662
|
+
ctx.addIssue({
|
|
17663
|
+
code: "custom",
|
|
17664
|
+
message: "A schedule trigger cannot use a generativeSuite action.",
|
|
17665
|
+
path: ["action", "kind"]
|
|
17666
|
+
});
|
|
17667
|
+
return;
|
|
17668
|
+
}
|
|
17669
|
+
checkAction(value.action, ctx);
|
|
17670
|
+
};
|
|
17671
|
+
var checkDeployment = (value, ctx) => {
|
|
17672
|
+
if (value.environmentPattern !== undefined && value.environmentIds.length > 0) {
|
|
17673
|
+
ctx.addIssue({
|
|
17674
|
+
code: "custom",
|
|
17675
|
+
message: "Supply environmentIds or environmentPattern, not both.",
|
|
17676
|
+
path: ["environmentPattern"]
|
|
17677
|
+
});
|
|
17678
|
+
}
|
|
17679
|
+
checkAction(value.action, ctx);
|
|
17680
|
+
};
|
|
17681
|
+
var checkTriggerConfiguration = (value, ctx) => value.kind === "schedule" ? checkSchedule(value, ctx) : checkDeployment(value, ctx);
|
|
17682
|
+
|
|
17280
17683
|
// node_modules/@qawolf/api-contracts/dist/v1/trigger/resource.js
|
|
17281
17684
|
var makeTriggerConfigurationSchema = (ids) => discriminatedUnion("kind", [
|
|
17282
17685
|
object({
|
|
@@ -17306,19 +17709,51 @@ var makeTriggerResourceSchema = (ids) => resource({
|
|
|
17306
17709
|
pausedAt: exports_iso.datetime().optional().describe("When it was paused."),
|
|
17307
17710
|
updatedAt: exports_iso.datetime()
|
|
17308
17711
|
}, { urlFieldDescription: "Absolute URL of the team's triggers page." });
|
|
17309
|
-
|
|
17310
|
-
|
|
17712
|
+
|
|
17713
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/create.js
|
|
17714
|
+
var makeCreateTriggerContract = (ids) => {
|
|
17715
|
+
const commonFields = {
|
|
17716
|
+
name: string2().trim().min(1).max(255),
|
|
17717
|
+
workspaceId: ids.workspace.optional().describe("The workspace to create the trigger in. Required when authenticating with an organization or user API key.")
|
|
17718
|
+
};
|
|
17719
|
+
const input = discriminatedUnion("kind", [
|
|
17720
|
+
object({
|
|
17721
|
+
...commonFields,
|
|
17722
|
+
...makeTriggerScheduleFields({
|
|
17723
|
+
environmentId: ids.environmentRef,
|
|
17724
|
+
flow: ids.flow
|
|
17725
|
+
}),
|
|
17726
|
+
kind: literal("schedule")
|
|
17727
|
+
}),
|
|
17728
|
+
object({
|
|
17729
|
+
...commonFields,
|
|
17730
|
+
...makeTriggerDeploymentFields(ids),
|
|
17731
|
+
kind: literal("deployment")
|
|
17732
|
+
})
|
|
17733
|
+
]).superRefine(checkTriggerConfiguration);
|
|
17734
|
+
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
17735
|
+
return {
|
|
17736
|
+
annotations: {
|
|
17737
|
+
destructiveHint: true,
|
|
17738
|
+
openWorldHint: true,
|
|
17739
|
+
readOnlyHint: false
|
|
17740
|
+
},
|
|
17741
|
+
description: "Create a trigger. A schedule trigger runs a named set of flows on a cadence; " + "a deployment trigger runs when a matching deployment is reported.",
|
|
17742
|
+
input,
|
|
17743
|
+
kind: "write",
|
|
17744
|
+
name: "trigger.create",
|
|
17745
|
+
output
|
|
17746
|
+
};
|
|
17747
|
+
};
|
|
17748
|
+
|
|
17749
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/find.js
|
|
17750
|
+
var makeFindTriggersContract = (ids) => {
|
|
17311
17751
|
const input = object({
|
|
17312
|
-
|
|
17313
|
-
|
|
17752
|
+
isPaused: boolean2().optional().describe("Only list paused, or only unpaused, triggers. Omit for both."),
|
|
17753
|
+
workspaceId: ids.workspace.optional().describe("The workspace to list triggers from. Required when authenticating with an organization or user API key.")
|
|
17314
17754
|
});
|
|
17315
|
-
const output =
|
|
17316
|
-
|
|
17317
|
-
replies: array(agentReplySchema).describe("What the AI has said since the cursor, oldest first. A reply still being written returns again, longer. A reply carrying choices is a question the work is blocked on."),
|
|
17318
|
-
sessionId: ids.chatSession,
|
|
17319
|
-
status: agentSessionStatusSchema
|
|
17320
|
-
}, {
|
|
17321
|
-
urlFieldDescription: "Absolute URL of the live session in the QA Wolf app."
|
|
17755
|
+
const output = object({
|
|
17756
|
+
triggers: array(makeTriggerResourceSchema(ids)).describe("The team's triggers, newest first.")
|
|
17322
17757
|
});
|
|
17323
17758
|
return {
|
|
17324
17759
|
annotations: {
|
|
@@ -17326,21 +17761,170 @@ var makeAgentGetContract = (ids) => {
|
|
|
17326
17761
|
openWorldHint: false,
|
|
17327
17762
|
readOnlyHint: true
|
|
17328
17763
|
},
|
|
17329
|
-
description:
|
|
17764
|
+
description: "List the team's triggers, newest first.",
|
|
17330
17765
|
input,
|
|
17331
17766
|
kind: "read",
|
|
17332
|
-
name: "
|
|
17767
|
+
name: "trigger.find",
|
|
17333
17768
|
output
|
|
17334
17769
|
};
|
|
17335
17770
|
};
|
|
17336
17771
|
|
|
17337
|
-
// node_modules/@qawolf/api-contracts/dist/v1/
|
|
17338
|
-
var
|
|
17339
|
-
const input = object({
|
|
17340
|
-
|
|
17341
|
-
|
|
17342
|
-
|
|
17343
|
-
|
|
17772
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/get.js
|
|
17773
|
+
var makeGetTriggerContract = (ids) => {
|
|
17774
|
+
const input = object({ triggerId: ids.trigger });
|
|
17775
|
+
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
17776
|
+
return {
|
|
17777
|
+
annotations: {
|
|
17778
|
+
destructiveHint: false,
|
|
17779
|
+
openWorldHint: false,
|
|
17780
|
+
readOnlyHint: true
|
|
17781
|
+
},
|
|
17782
|
+
description: "Get one trigger by id.",
|
|
17783
|
+
input,
|
|
17784
|
+
kind: "read",
|
|
17785
|
+
name: "trigger.get",
|
|
17786
|
+
output
|
|
17787
|
+
};
|
|
17788
|
+
};
|
|
17789
|
+
|
|
17790
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/lifecycle.js
|
|
17791
|
+
var makePauseTriggerContract = (ids) => {
|
|
17792
|
+
const input = object({ triggerId: ids.trigger });
|
|
17793
|
+
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
17794
|
+
return {
|
|
17795
|
+
annotations: {
|
|
17796
|
+
destructiveHint: true,
|
|
17797
|
+
openWorldHint: false,
|
|
17798
|
+
readOnlyHint: false
|
|
17799
|
+
},
|
|
17800
|
+
description: "Pause a trigger so it stops firing. Pausing is idempotent. A team whose " + "triggers are all paused reports no trigger activity at all.",
|
|
17801
|
+
input,
|
|
17802
|
+
kind: "write",
|
|
17803
|
+
name: "trigger.pause",
|
|
17804
|
+
output
|
|
17805
|
+
};
|
|
17806
|
+
};
|
|
17807
|
+
var makeResumeTriggerContract = (ids) => {
|
|
17808
|
+
const input = object({ triggerId: ids.trigger });
|
|
17809
|
+
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
17810
|
+
return {
|
|
17811
|
+
annotations: {
|
|
17812
|
+
destructiveHint: true,
|
|
17813
|
+
openWorldHint: true,
|
|
17814
|
+
readOnlyHint: false
|
|
17815
|
+
},
|
|
17816
|
+
description: "Resume a paused trigger. A schedule trigger restarts from the next " + "upcoming slot: the slots it missed while paused do not run.",
|
|
17817
|
+
input,
|
|
17818
|
+
kind: "write",
|
|
17819
|
+
name: "trigger.resume",
|
|
17820
|
+
output
|
|
17821
|
+
};
|
|
17822
|
+
};
|
|
17823
|
+
var makeDeleteTriggerContract = (ids) => {
|
|
17824
|
+
const input = object({ triggerId: ids.trigger });
|
|
17825
|
+
const output = object({
|
|
17826
|
+
triggerId: ids.trigger.describe("The id of the deleted trigger.")
|
|
17827
|
+
});
|
|
17828
|
+
return {
|
|
17829
|
+
annotations: {
|
|
17830
|
+
destructiveHint: true,
|
|
17831
|
+
openWorldHint: false,
|
|
17832
|
+
readOnlyHint: false
|
|
17833
|
+
},
|
|
17834
|
+
description: "Delete a trigger permanently. The runs it already created are kept. " + "To stop a trigger without losing it, use trigger.pause.",
|
|
17835
|
+
input,
|
|
17836
|
+
kind: "write",
|
|
17837
|
+
name: "trigger.delete",
|
|
17838
|
+
output
|
|
17839
|
+
};
|
|
17840
|
+
};
|
|
17841
|
+
|
|
17842
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/update.js
|
|
17843
|
+
var makeUpdateTriggerContract = (ids) => {
|
|
17844
|
+
const commonFields = {
|
|
17845
|
+
name: string2().trim().min(1).max(255),
|
|
17846
|
+
triggerId: ids.trigger
|
|
17847
|
+
};
|
|
17848
|
+
const input = discriminatedUnion("kind", [
|
|
17849
|
+
object({
|
|
17850
|
+
...commonFields,
|
|
17851
|
+
...makeTriggerScheduleFields({
|
|
17852
|
+
environmentId: ids.environmentRef,
|
|
17853
|
+
flow: ids.flow
|
|
17854
|
+
}),
|
|
17855
|
+
kind: literal("schedule")
|
|
17856
|
+
}),
|
|
17857
|
+
object({
|
|
17858
|
+
...commonFields,
|
|
17859
|
+
...makeTriggerDeploymentFields(ids),
|
|
17860
|
+
kind: literal("deployment")
|
|
17861
|
+
})
|
|
17862
|
+
]).superRefine(checkTriggerConfiguration);
|
|
17863
|
+
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
17864
|
+
return {
|
|
17865
|
+
annotations: {
|
|
17866
|
+
destructiveHint: true,
|
|
17867
|
+
openWorldHint: true,
|
|
17868
|
+
readOnlyHint: false
|
|
17869
|
+
},
|
|
17870
|
+
description: "Replace a trigger's configuration. Every field is written, so read the " + "trigger first and send its configuration back with your changes applied. " + "Pausing is separate: use trigger.pause and trigger.resume.",
|
|
17871
|
+
input,
|
|
17872
|
+
kind: "write",
|
|
17873
|
+
name: "trigger.update",
|
|
17874
|
+
output
|
|
17875
|
+
};
|
|
17876
|
+
};
|
|
17877
|
+
|
|
17878
|
+
// node_modules/@qawolf/api-contracts/dist/v1/trigger/index.js
|
|
17879
|
+
var makeTriggerContracts = (ids) => ({
|
|
17880
|
+
create: makeCreateTriggerContract(ids),
|
|
17881
|
+
delete: makeDeleteTriggerContract(ids),
|
|
17882
|
+
find: makeFindTriggersContract(ids),
|
|
17883
|
+
get: makeGetTriggerContract(ids),
|
|
17884
|
+
pause: makePauseTriggerContract(ids),
|
|
17885
|
+
resume: makeResumeTriggerContract(ids),
|
|
17886
|
+
update: makeUpdateTriggerContract(ids)
|
|
17887
|
+
});
|
|
17888
|
+
// node_modules/@qawolf/api-contracts/dist/v1/agent/get.js
|
|
17889
|
+
var makeAgentGetContract = (ids) => {
|
|
17890
|
+
const input = object({
|
|
17891
|
+
cursor: string2().min(1).optional().describe("Opaque cursor copied from this same session's previous nextCursor. Omit to read from its first reply."),
|
|
17892
|
+
sessionId: ids.chatSession.describe("The id agent.send answered with."),
|
|
17893
|
+
waitSeconds: number2().int().min(0).optional().describe(`How long the server may hold this check open, in seconds. Omit or pass 0 to answer at once. A larger value clamps to ${maxAgentWaitSeconds}. Check again as soon as a held check returns.`)
|
|
17894
|
+
});
|
|
17895
|
+
const output = resource({
|
|
17896
|
+
nextCursor: string2().min(1).describe("Send back as cursor on the next check to read only what follows. Always present, even with no replies. Belongs to this session alone."),
|
|
17897
|
+
replies: array(agentReplySchema).describe("What the AI has said since the cursor, oldest first. A reply still being written returns again, longer. A reply carrying choices is a question the work is blocked on."),
|
|
17898
|
+
sessionId: ids.chatSession,
|
|
17899
|
+
status: agentSessionStatusSchema,
|
|
17900
|
+
waitSeconds: number2().int().optional().describe("The hold this check was allowed, in seconds, after clamping. Absent when the check did not wait."),
|
|
17901
|
+
wokeOn: agentWakeReasonSchema.optional().describe('Why this check answered when it did. Absent when the check did not wait. "timeout" means nothing new: replies is empty and nextCursor unchanged, so check again with the same cursor.')
|
|
17902
|
+
}, {
|
|
17903
|
+
urlFieldDescription: "Absolute URL of the live session in the QA Wolf app."
|
|
17904
|
+
});
|
|
17905
|
+
return {
|
|
17906
|
+
annotations: {
|
|
17907
|
+
destructiveHint: false,
|
|
17908
|
+
openWorldHint: false,
|
|
17909
|
+
readOnlyHint: true
|
|
17910
|
+
},
|
|
17911
|
+
description: 'Monitor a QA Wolf AI session by reading its status and replies. After agent.send, share the returned session URL before monitoring. Pass waitSeconds 45 so the server holds the check open and answers the moment something happens. Check again as soon as it returns; never sleep between checks or loop without waitSeconds. Pass the nextCursor from one response as the cursor on the next check; it then reads only what is new, and only for the session that minted it. Continue monitoring silently when the status is unchanged and no replies come back; do not narrate waiting, announce the next check, or ask whether to keep monitoring. Report only substantive new progress, questions, blockers, or the final outcome. A status of "waiting-for-you" means the last reply is a question the work is blocked on, and answering it with agent.send is what unblocks it. Surface an explicit request for user input even if the status still says "working". Include the session URL when reporting a blocker or final outcome. On "completed", stop status checks and verify the requested result before claiming success. For new flows, validation, publication in the target environment, and readiness are separate checks; a Git push or final reply does not prove the flow is active. If every requested result is verified but status remains "working", report the mismatch and stop monitoring. Stop on "failed" or "cancelled" and report any confirmed partial result.',
|
|
17912
|
+
input,
|
|
17913
|
+
kind: "read",
|
|
17914
|
+
name: "agent.get",
|
|
17915
|
+
output
|
|
17916
|
+
};
|
|
17917
|
+
};
|
|
17918
|
+
|
|
17919
|
+
// node_modules/@qawolf/api-contracts/dist/v1/agent/send.js
|
|
17920
|
+
var makeAgentSendContract = (ids) => {
|
|
17921
|
+
const input = object({
|
|
17922
|
+
environmentId: ids.environmentRef.optional().describe("The environment to work in. Omit to use the team's default environment. Ignored when sessionId is given, because a session keeps the environment it opened in."),
|
|
17923
|
+
filePaths: array(teamStoragePathSchema).max(20).optional().describe("Paths of files this request is about, from file.requestUpload. The AI reads them from storage rather than the message, so a plan of hundreds of journeys costs nothing to send."),
|
|
17924
|
+
message: string2().trim().min(1).max(1e4).describe("What you want QA Wolf to do, in plain language. Name the journey, the part of the app it covers, and anything the AI cannot discover for itself, such as a test account, a feature flag, or how to reach a staging environment. When answering a question from agent.get, send the answer here."),
|
|
17925
|
+
parentSessionId: ids.chatSession.optional().describe("The session that is starting this one. QA Wolf lists the new session underneath it, so whoever is watching that session can open the work it handed out. The session you name must not itself have been started this way, because a sub-session cannot start sub-sessions of its own. Ignored when sessionId is given, since continuing a session does not change what started it."),
|
|
17926
|
+
runnerId: runnerIdSchema.optional().describe("The runner you explored on, from runner.launch. Pass it to hand that browser to QA Wolf instead of having a fresh one started, so the pages you opened and any sign-in the user completed there are carried over. Leave the runner running: QA Wolf takes it over and terminates it when the work is done. Omit this when you have no runner, or when you already terminated it."),
|
|
17927
|
+
sessionId: ids.chatSession.optional().describe("Continue the session with this id instead of opening a new one. Omit to start fresh. Send this to answer a question or to add context to work already running."),
|
|
17344
17928
|
workspaceId: ids.workspace.optional().describe("The workspace to work in. Required when authenticating with an organization or user API key.")
|
|
17345
17929
|
});
|
|
17346
17930
|
const output = resource({
|
|
@@ -17355,7 +17939,7 @@ var makeAgentSendContract = (ids) => {
|
|
|
17355
17939
|
openWorldHint: true,
|
|
17356
17940
|
readOnlyHint: false
|
|
17357
17941
|
},
|
|
17358
|
-
description: "Start or continue work with the QA Wolf AI and return a live session URL to share with the user. Use it to cover a user journey, investigate a failing run, or fix a broken flow. This is the one verb that starts work from nothing: every other write acts on a flow, run or issue that already exists. Returns sessionId, status, and url as soon as the request is accepted; work can take minutes to tens of minutes. After each send,
|
|
17942
|
+
description: "Start or continue work with the QA Wolf AI and return a live session URL to share with the user. Use it to cover a user journey, investigate a failing run, or fix a broken flow. This is the one verb that starts work from nothing: every other write acts on a flow, run or issue that already exists. Returns sessionId, status, and url as soon as the request is accepted; work can take minutes to tens of minutes. After each send, put the exact returned url in a normal user-visible assistant message, and repeat it in the last message of the turn: only a turn's trailing message is shown expanded. Tool output and internal reasoning do not count as sharing the link. Do not run a timer or monitoring call alongside this send. Acceptance does not mean the work is complete. Then monitor the session with agent.get, reporting new progress, blockers, and the final outcome rather than unchanged status. Send here again to answer a question or add context to the same session.",
|
|
17359
17943
|
input,
|
|
17360
17944
|
kind: "write",
|
|
17361
17945
|
name: "agent.send",
|
|
@@ -17363,25 +17947,40 @@ var makeAgentSendContract = (ids) => {
|
|
|
17363
17947
|
};
|
|
17364
17948
|
};
|
|
17365
17949
|
|
|
17366
|
-
// node_modules/@qawolf/api-contracts/dist/v1/
|
|
17367
|
-
var
|
|
17950
|
+
// node_modules/@qawolf/api-contracts/dist/v1/codeHostIntegration/resource.js
|
|
17951
|
+
var makeCodeHostIntegrationResourceSchema = () => resource({
|
|
17952
|
+
connectedAt: exports_iso.datetime().describe("When the code host was connected to the workspace."),
|
|
17953
|
+
provider: _enum(["github", "gitlab"]).describe("The code host this integration connects."),
|
|
17954
|
+
suspendedAt: exports_iso.datetime().optional().describe("Set while GitHub has the QA Wolf app installation suspended. While suspended, GitHub sends QA Wolf no webhooks.")
|
|
17955
|
+
}, {
|
|
17956
|
+
urlFieldDescription: "Absolute URL of the workspace's integrations settings page."
|
|
17957
|
+
});
|
|
17958
|
+
var makeCodeHostRepositoryResourceSchema = () => resource({
|
|
17959
|
+
fullName: string2().describe(`The repository's full name on the code host, such as "acme/storefront".`),
|
|
17960
|
+
provider: _enum(["github", "gitlab"]).describe("The code host the covering integration connects.")
|
|
17961
|
+
}, {
|
|
17962
|
+
urlFieldDescription: "Absolute URL of the workspace's integrations settings page."
|
|
17963
|
+
});
|
|
17964
|
+
|
|
17965
|
+
// node_modules/@qawolf/api-contracts/dist/v1/codeHostIntegration/find.js
|
|
17966
|
+
var makeFindCodeHostIntegrationsContract = (ids) => {
|
|
17368
17967
|
const input = object({
|
|
17369
|
-
|
|
17370
|
-
|
|
17371
|
-
|
|
17372
|
-
|
|
17968
|
+
workspaceId: ids.workspace.optional().describe("The workspace to list code host integrations from. Required when authenticating with an organization or user API key.")
|
|
17969
|
+
});
|
|
17970
|
+
const output = object({
|
|
17971
|
+
integrations: array(makeCodeHostIntegrationResourceSchema()).describe("The workspace's code host integrations. Empty when no code host is connected."),
|
|
17972
|
+
settingsUrl: url().describe("Absolute URL of the workspace's integrations settings page.")
|
|
17373
17973
|
});
|
|
17374
|
-
const output = resource({ automationId: ids.automation }, { urlFieldDescription: "Absolute URL of the automation review page." });
|
|
17375
17974
|
return {
|
|
17376
17975
|
annotations: {
|
|
17377
|
-
destructiveHint:
|
|
17378
|
-
openWorldHint:
|
|
17379
|
-
readOnlyHint:
|
|
17976
|
+
destructiveHint: false,
|
|
17977
|
+
openWorldHint: false,
|
|
17978
|
+
readOnlyHint: true
|
|
17380
17979
|
},
|
|
17381
|
-
description: "
|
|
17980
|
+
description: "List the workspace's code host integrations (GitHub or GitLab). A connected integration lets QA Wolf read repositories and receive the code host's webhooks. codeHostIntegration.listRepositories pages the repositories each integration covers.",
|
|
17382
17981
|
input,
|
|
17383
|
-
kind: "
|
|
17384
|
-
name: "
|
|
17982
|
+
kind: "read",
|
|
17983
|
+
name: "codeHostIntegration.find",
|
|
17385
17984
|
output
|
|
17386
17985
|
};
|
|
17387
17986
|
};
|
|
@@ -17393,6 +17992,221 @@ var makePaginationInputFields = (limits) => ({
|
|
|
17393
17992
|
});
|
|
17394
17993
|
var nextCursorSchema = string2().min(1).optional().describe("Repeat the request with this value as cursor to fetch the next page. Absent when there are no further results.");
|
|
17395
17994
|
|
|
17995
|
+
// node_modules/@qawolf/api-contracts/dist/v1/codeHostIntegration/listRepositories.js
|
|
17996
|
+
var makeListCodeHostRepositoriesContract = (ids) => {
|
|
17997
|
+
const input = object({
|
|
17998
|
+
provider: _enum(["github", "gitlab"]).optional().describe("Only list repositories covered by this code host's integration. Omit for all."),
|
|
17999
|
+
workspaceId: ids.workspace.optional().describe("The workspace to list covered repositories from. Required when authenticating with an organization or user API key."),
|
|
18000
|
+
...makePaginationInputFields({ defaultLimit: 20, maxLimit: 100 })
|
|
18001
|
+
});
|
|
18002
|
+
const output = object({
|
|
18003
|
+
nextCursor: nextCursorSchema,
|
|
18004
|
+
repositories: array(makeCodeHostRepositoryResourceSchema()).describe("The repositories the workspace's code host integrations cover, alphabetical by full name.")
|
|
18005
|
+
});
|
|
18006
|
+
return {
|
|
18007
|
+
annotations: {
|
|
18008
|
+
destructiveHint: false,
|
|
18009
|
+
openWorldHint: false,
|
|
18010
|
+
readOnlyHint: true
|
|
18011
|
+
},
|
|
18012
|
+
description: "List the repositories the workspace's code host integrations cover, alphabetical by full name. The list reflects the last sync from the code host; codeHostIntegration.find lists the integrations themselves.",
|
|
18013
|
+
input,
|
|
18014
|
+
kind: "read",
|
|
18015
|
+
name: "codeHostIntegration.listRepositories",
|
|
18016
|
+
output
|
|
18017
|
+
};
|
|
18018
|
+
};
|
|
18019
|
+
|
|
18020
|
+
// node_modules/@qawolf/api-contracts/dist/v1/deployment/resource.js
|
|
18021
|
+
var makeDeploymentResourceSchema = (ids) => resource({
|
|
18022
|
+
branch: string2().optional().describe("The git branch the deployed commit is on. Absent when neither the deployer nor the code host declared one."),
|
|
18023
|
+
commitAuthorName: string2().optional().describe("The author of the deployed commit."),
|
|
18024
|
+
commitMessage: string2().optional().describe("The message of the deployed commit."),
|
|
18025
|
+
commitSha: string2().optional().describe("The sha of the deployed commit."),
|
|
18026
|
+
deployedAt: exports_iso.datetime().describe("When QA Wolf first received the deployment."),
|
|
18027
|
+
deployTarget: string2().optional().describe("Identifies the deployed app within the environment: the URL it serves, or the build path of a mobile build. A trigger's deployTargetPattern matches against it."),
|
|
18028
|
+
environmentId: ids.environment.describe("The QA Wolf environment the deployment reported into."),
|
|
18029
|
+
environmentName: string2().optional().describe("The environment's name. Absent when the environment has since been deleted."),
|
|
18030
|
+
id: ids.deployment,
|
|
18031
|
+
provider: _enum(["github", "gitlab", "api"]).optional().describe("How QA Wolf received the deployment: a GitHub webhook, a GitLab webhook, or the deployment.reportStatus API."),
|
|
18032
|
+
status: _enum(["pending", "success", "failure", "inactive"]).optional().describe("The deployment's lifecycle status, in the vocabulary deployment.reportStatus accepts. Absent when no status was ever reported.")
|
|
18033
|
+
}, {
|
|
18034
|
+
urlFieldDescription: "Absolute URL of the workspace's deployments page."
|
|
18035
|
+
});
|
|
18036
|
+
|
|
18037
|
+
// node_modules/@qawolf/api-contracts/dist/v1/deployment/find.js
|
|
18038
|
+
var makeFindDeploymentsContract = (ids) => {
|
|
18039
|
+
const input = object({
|
|
18040
|
+
workspaceId: ids.workspace.optional().describe("The workspace to list deployments from. Required when authenticating with an organization or user API key."),
|
|
18041
|
+
...makePaginationInputFields({ defaultLimit: 20, maxLimit: 100 })
|
|
18042
|
+
});
|
|
18043
|
+
const output = object({
|
|
18044
|
+
deployments: array(makeDeploymentResourceSchema(ids)).describe("The workspace's deployments, newest first."),
|
|
18045
|
+
nextCursor: nextCursorSchema
|
|
18046
|
+
});
|
|
18047
|
+
return {
|
|
18048
|
+
annotations: {
|
|
18049
|
+
destructiveHint: false,
|
|
18050
|
+
openWorldHint: false,
|
|
18051
|
+
readOnlyHint: true
|
|
18052
|
+
},
|
|
18053
|
+
description: "List the deployments QA Wolf has received for the workspace, newest first. A deployment arrives through a code host integration's webhook or through deployment.reportStatus, and is what a deployment trigger evaluates against. deployment.listTriggerEvaluations reports each trigger's verdict for one deployment.",
|
|
18054
|
+
input,
|
|
18055
|
+
kind: "read",
|
|
18056
|
+
name: "deployment.find",
|
|
18057
|
+
output
|
|
18058
|
+
};
|
|
18059
|
+
};
|
|
18060
|
+
|
|
18061
|
+
// node_modules/@qawolf/api-contracts/dist/v1/deployment/listTriggerEvaluations.js
|
|
18062
|
+
var conditionKindSchema = _enum(["environment", "deployTarget", "branch"]).describe("What the condition checks on the deployment: the environment it reported into, the deploy target identifying the deployed app, or the git branch of the deployed commit.");
|
|
18063
|
+
var conditionVerdictSchema = discriminatedUnion("outcome", [
|
|
18064
|
+
object({
|
|
18065
|
+
kind: conditionKindSchema,
|
|
18066
|
+
outcome: literal("matched")
|
|
18067
|
+
}),
|
|
18068
|
+
object({
|
|
18069
|
+
kind: conditionKindSchema,
|
|
18070
|
+
outcome: literal("did-not-match"),
|
|
18071
|
+
reason: string2().describe("Why the condition did not match this deployment, in plain language.")
|
|
18072
|
+
})
|
|
18073
|
+
]);
|
|
18074
|
+
var conditionSetsField = array(object({
|
|
18075
|
+
conditions: array(conditionVerdictSchema).describe("One verdict per condition. Conditions in a set are ANDed; a set with no conditions matches every deployment."),
|
|
18076
|
+
outcome: _enum(["matched", "did-not-match"]).describe("Whether every condition in the set matched.")
|
|
18077
|
+
})).describe("The trigger's condition sets as they were when the deployment was evaluated, each with per-condition verdicts. Sets are ORed: the trigger matches when any one set matches. Most triggers have exactly one set.");
|
|
18078
|
+
var makeRunOutcomeSchema = (ids) => discriminatedUnion("status", [
|
|
18079
|
+
object({
|
|
18080
|
+
runId: ids.run.describe("The run the trigger created."),
|
|
18081
|
+
status: literal("ran")
|
|
18082
|
+
}),
|
|
18083
|
+
object({ status: literal("starting") }),
|
|
18084
|
+
object({
|
|
18085
|
+
reason: string2().describe("Why the matched trigger created no run, in plain language."),
|
|
18086
|
+
status: literal("did-not-run"),
|
|
18087
|
+
supersededByRunId: ids.run.optional().describe("The run that covered this deployment instead, when an earlier delivery of the same deployment had already created one.")
|
|
18088
|
+
}),
|
|
18089
|
+
object({ status: literal("unknown") })
|
|
18090
|
+
]).describe("What became of the match. Matching is not running: a matched trigger can still be stopped from creating a run, and did-not-run carries the reason. starting means run creation is still in progress; unknown means QA Wolf has no record of what followed the match.");
|
|
18091
|
+
var makeVerdictSchema = (ids) => discriminatedUnion("outcome", [
|
|
18092
|
+
object({
|
|
18093
|
+
conditionSets: conditionSetsField,
|
|
18094
|
+
outcome: literal("matched"),
|
|
18095
|
+
run: makeRunOutcomeSchema(ids)
|
|
18096
|
+
}),
|
|
18097
|
+
object({
|
|
18098
|
+
conditionSets: conditionSetsField,
|
|
18099
|
+
outcome: literal("did-not-match")
|
|
18100
|
+
}),
|
|
18101
|
+
object({
|
|
18102
|
+
outcome: literal("not-considered"),
|
|
18103
|
+
reason: _enum(["trigger-paused", "not-a-deployment-trigger"]).describe("Why the conditions were never checked. trigger-paused: the trigger was paused when the deployment was evaluated. not-a-deployment-trigger: the trigger has no deployment conditions (a schedule trigger, for example), so no deployment can fire it.")
|
|
18104
|
+
})
|
|
18105
|
+
]).describe("The trigger's verdict for this deployment: matched, did-not-match with per-condition verdicts, or not-considered when the conditions were never checked at all.");
|
|
18106
|
+
var makeEvaluationSchema = (ids) => object({
|
|
18107
|
+
triggerId: ids.trigger.optional().describe("Absent when the trigger has been deleted since."),
|
|
18108
|
+
triggerName: string2().describe("The trigger's name when the deployment was evaluated."),
|
|
18109
|
+
verdict: makeVerdictSchema(ids)
|
|
18110
|
+
});
|
|
18111
|
+
var makeListDeploymentTriggerEvaluationsContract = (ids) => {
|
|
18112
|
+
const input = object({ deploymentId: ids.deployment });
|
|
18113
|
+
const output = discriminatedUnion("state", [
|
|
18114
|
+
object({
|
|
18115
|
+
evaluations: array(makeEvaluationSchema(ids)).describe("One verdict per trigger that existed when the deployment was evaluated."),
|
|
18116
|
+
state: literal("evaluated")
|
|
18117
|
+
}),
|
|
18118
|
+
object({ state: literal("not-evaluated") }).describe("Triggers were never evaluated for this deployment. Only a deployment's first success report evaluates triggers, so a deployment that never reported success carries no verdicts.")
|
|
18119
|
+
]);
|
|
18120
|
+
return {
|
|
18121
|
+
annotations: {
|
|
18122
|
+
destructiveHint: false,
|
|
18123
|
+
openWorldHint: false,
|
|
18124
|
+
readOnlyHint: true
|
|
18125
|
+
},
|
|
18126
|
+
description: "List the per-trigger verdicts recorded when a deployment was evaluated against the workspace's triggers. Each verdict says whether the trigger matched, did not match with the reason per condition, or was never considered and why. Verdicts are a snapshot from evaluation time: editing or deleting a trigger later does not change them.",
|
|
18127
|
+
input,
|
|
18128
|
+
kind: "read",
|
|
18129
|
+
name: "deployment.listTriggerEvaluations",
|
|
18130
|
+
output
|
|
18131
|
+
};
|
|
18132
|
+
};
|
|
18133
|
+
|
|
18134
|
+
// node_modules/@qawolf/api-contracts/dist/v1/deployment/reportStatusMetadata.js
|
|
18135
|
+
var deploymentReportMetadata = strictObject({
|
|
18136
|
+
commitAuthorName: string2().trim().min(1).max(255).describe("The display name of the person who authored the deployed commit.").optional(),
|
|
18137
|
+
commitMessage: string2().trim().min(1).max(2048).describe("The message of the deployed commit. The deployments list shows its first line.").optional(),
|
|
18138
|
+
commitSha: string2().trim().min(1).max(255).describe("The commit sha of the deployed application revision.").optional(),
|
|
18139
|
+
pullRequestNumber: number2().int().positive().describe("The number of the pull request the deployment belongs to. Requires repository.").optional(),
|
|
18140
|
+
ref: string2().trim().min(1).max(255).describe("The branch or ref that was deployed.").optional(),
|
|
18141
|
+
repository: string2().trim().min(1).max(255).describe('The owner/name of the repository, e.g. "acme/web".').optional()
|
|
18142
|
+
}).refine((value) => value.pullRequestNumber === undefined || value.repository !== undefined, {
|
|
18143
|
+
message: "repository is required when pullRequestNumber is provided.",
|
|
18144
|
+
path: ["pullRequestNumber"]
|
|
18145
|
+
});
|
|
18146
|
+
|
|
18147
|
+
// node_modules/@qawolf/api-contracts/dist/v1/deployment/reportStatus.js
|
|
18148
|
+
var deploymentStatusSchema = _enum([
|
|
18149
|
+
"pending",
|
|
18150
|
+
"success",
|
|
18151
|
+
"failure",
|
|
18152
|
+
"inactive"
|
|
18153
|
+
]);
|
|
18154
|
+
var makeReportDeploymentStatusContract = (ids) => {
|
|
18155
|
+
const environment = union([
|
|
18156
|
+
strictObject({
|
|
18157
|
+
id: ids.environment.describe("The id of an existing environment. Never creates an environment. Send an alias through name instead.")
|
|
18158
|
+
}),
|
|
18159
|
+
strictObject({
|
|
18160
|
+
baseEphemeralEnvironment: string2().trim().min(1).max(255).describe("The alias or name of the environment a created ephemeral environment copies from. Only valid with ephemeral. The server uses the team default base environment when no environment matches this value.").optional(),
|
|
18161
|
+
ephemeral: boolean2().describe("Requests an ephemeral environment when the name creates one. A name that matches an existing environment keeps that environment's kind.").optional(),
|
|
18162
|
+
name: string2().trim().min(1).max(255).describe("The provider deploy environment name. Resolves an environment by alias or name; creates one when no match exists.")
|
|
18163
|
+
})
|
|
18164
|
+
], {
|
|
18165
|
+
error: "Send environment as either { id } or { name }, with optional ephemeral and baseEphemeralEnvironment."
|
|
18166
|
+
});
|
|
18167
|
+
const input = strictObject({
|
|
18168
|
+
deployTarget: httpUrl().max(2048).describe("The URL the deployment serves, e.g. a preview URL.").optional(),
|
|
18169
|
+
environment: environment.describe("Selects the environment the deployment reports into.").optional(),
|
|
18170
|
+
environmentVariables: record(string2(), string2()).describe("Environment variable overrides for the runs this deployment requests. A report that omits this field keeps the stored values. A report that sends it replaces them.").optional(),
|
|
18171
|
+
metadata: deploymentReportMetadata.describe("Details the deployer declares about the deployed revision; a code host integration's own record of the same commit is shown in preference to the message and author declared here.").optional(),
|
|
18172
|
+
providerDeploymentId: string2().trim().min(1).max(255).describe('Your identifier for the deployment, e.g. "vercel_dpl_123". Reports with the same providerDeploymentId update one deployment within this workspace.'),
|
|
18173
|
+
status: deploymentStatusSchema.describe("The deployment lifecycle status. A deployment's first success report evaluates global triggers asynchronously, and it is the only report that does. The response does not include the resulting runs."),
|
|
18174
|
+
workspaceId: ids.workspace.describe("The workspace to report the deployment status into. A team API key must name its own workspace.")
|
|
18175
|
+
}).superRefine((value, context) => {
|
|
18176
|
+
if (value.environment !== undefined && "name" in value.environment && value.environment.baseEphemeralEnvironment !== undefined && value.environment.ephemeral !== true) {
|
|
18177
|
+
context.addIssue({
|
|
18178
|
+
code: "custom",
|
|
18179
|
+
message: "baseEphemeralEnvironment is only valid with ephemeral.",
|
|
18180
|
+
path: ["environment", "baseEphemeralEnvironment"]
|
|
18181
|
+
});
|
|
18182
|
+
}
|
|
18183
|
+
});
|
|
18184
|
+
const deployment = resource({
|
|
18185
|
+
id: ids.deployment,
|
|
18186
|
+
status: deploymentStatusSchema.describe("The status reported in this request.")
|
|
18187
|
+
}, { urlFieldDescription: "Absolute URL of the environment's runs page." });
|
|
18188
|
+
const output = object({ deployment });
|
|
18189
|
+
return {
|
|
18190
|
+
annotations: {
|
|
18191
|
+
destructiveHint: true,
|
|
18192
|
+
openWorldHint: true,
|
|
18193
|
+
readOnlyHint: false
|
|
18194
|
+
},
|
|
18195
|
+
description: "Report a deployment lifecycle status. A deployment's first success report evaluates global triggers asynchronously, and it is the only report that does: a trigger added or unpaused later does not make an already reported deployment evaluate again. Report that deployment under a new providerDeploymentId to evaluate it against the current triggers. The response contains the deployment only, not the resulting runs.",
|
|
18196
|
+
input,
|
|
18197
|
+
kind: "write",
|
|
18198
|
+
name: "deployment.reportStatus",
|
|
18199
|
+
output
|
|
18200
|
+
};
|
|
18201
|
+
};
|
|
18202
|
+
|
|
18203
|
+
// node_modules/@qawolf/api-contracts/dist/v1/deployment/index.js
|
|
18204
|
+
var makeDeploymentContracts = (ids) => ({
|
|
18205
|
+
find: makeFindDeploymentsContract(ids),
|
|
18206
|
+
listTriggerEvaluations: makeListDeploymentTriggerEvaluationsContract(ids),
|
|
18207
|
+
reportStatus: makeReportDeploymentStatusContract(ids)
|
|
18208
|
+
});
|
|
18209
|
+
|
|
17396
18210
|
// node_modules/@qawolf/api-contracts/dist/v1/email/find.js
|
|
17397
18211
|
var makeFindEmailsContract = (ids) => {
|
|
17398
18212
|
const input = object({
|
|
@@ -17560,29 +18374,6 @@ var makeSendEmailContract = (ids) => {
|
|
|
17560
18374
|
};
|
|
17561
18375
|
};
|
|
17562
18376
|
|
|
17563
|
-
// node_modules/@qawolf/api-contracts/dist/v1/environment/deleteVariable.js
|
|
17564
|
-
var makeDeleteEnvironmentVariableContract = (ids) => {
|
|
17565
|
-
const input = object({
|
|
17566
|
-
environmentId: ids.environmentRef,
|
|
17567
|
-
name: string2().trim().min(1, "Environment variable name cannot be blank.").max(255).describe("Name of the environment variable to remove. Canonicalized the same way as environment.setVariable, so the name that was set is the name that deletes.")
|
|
17568
|
-
});
|
|
17569
|
-
const output = object({
|
|
17570
|
-
name: string2().describe("The canonicalized name that was removed. Returned even when the environment had no such variable.")
|
|
17571
|
-
});
|
|
17572
|
-
return {
|
|
17573
|
-
annotations: {
|
|
17574
|
-
destructiveHint: true,
|
|
17575
|
-
openWorldHint: false,
|
|
17576
|
-
readOnlyHint: false
|
|
17577
|
-
},
|
|
17578
|
-
description: "Remove one environment variable by name. Succeeds whether or not the variable existed.",
|
|
17579
|
-
input,
|
|
17580
|
-
kind: "write",
|
|
17581
|
-
name: "environment.deleteVariable",
|
|
17582
|
-
output
|
|
17583
|
-
};
|
|
17584
|
-
};
|
|
17585
|
-
|
|
17586
18377
|
// node_modules/@qawolf/api-contracts/dist/v1/environment/resource.js
|
|
17587
18378
|
var makeEnvironmentResourceSchema = (ids) => resource({
|
|
17588
18379
|
alias: string2().optional().describe("The environment's alias: a team-unique slug that team API keys can use in place of the id. Terminated environments have none."),
|
|
@@ -17603,9 +18394,55 @@ var makeEnvironmentResourceSchema = (ids) => resource({
|
|
|
17603
18394
|
name: string2(),
|
|
17604
18395
|
runConcurrencyLimit: string2().describe('Maximum number of concurrent runs: a whole number, "unlimited", or a concurrency expression.'),
|
|
17605
18396
|
status: _enum(["blocked", "needs-investigation", "ready", "running"]).describe("Standing health across the environment's runs, not its Git reconciliation state: blocked (open blocking bugs), needs-investigation (failures awaiting triage), running (flows executing), ready (none of the above — the greenlit state)."),
|
|
17606
|
-
terminatedAt: exports_iso.datetime().optional().describe("When the environment was terminated, if it has been.")
|
|
18397
|
+
terminatedAt: exports_iso.datetime().optional().describe("When the environment was terminated, if it has been."),
|
|
18398
|
+
variablesUrl: url().optional().describe("Absolute URL of the page where a person saves this environment's variables. Give it to the user when a login or key is missing, rather than collecting the value yourself. Absent once the environment is terminated, because that page no longer exists.")
|
|
17607
18399
|
}, { urlFieldDescription: "Absolute URL of the environment page." });
|
|
17608
18400
|
|
|
18401
|
+
// node_modules/@qawolf/api-contracts/dist/v1/environment/create.js
|
|
18402
|
+
var makeCreateEnvironmentContract = (ids) => {
|
|
18403
|
+
const input = object({
|
|
18404
|
+
name: string2().min(1).max(255).regex(/\S/, "Environment name cannot be blank.").describe("Display name of the new environment."),
|
|
18405
|
+
workspaceId: ids.workspace.optional().describe("The workspace to create the environment in. Required when authenticating with an organization or user API key.")
|
|
18406
|
+
});
|
|
18407
|
+
const output = makeEnvironmentResourceSchema(ids);
|
|
18408
|
+
return {
|
|
18409
|
+
annotations: {
|
|
18410
|
+
destructiveHint: false,
|
|
18411
|
+
openWorldHint: true,
|
|
18412
|
+
readOnlyHint: false
|
|
18413
|
+
},
|
|
18414
|
+
description: "Create an environment on the caller's team and return it in the environment.get shape. This can also create a branch on the team's connected Git provider.",
|
|
18415
|
+
input,
|
|
18416
|
+
kind: "write",
|
|
18417
|
+
name: "environment.create",
|
|
18418
|
+
output
|
|
18419
|
+
};
|
|
18420
|
+
};
|
|
18421
|
+
|
|
18422
|
+
// node_modules/@qawolf/api-contracts/dist/v1/environment/deleteVariable.js
|
|
18423
|
+
var makeDeleteEnvironmentVariableContract = (ids) => {
|
|
18424
|
+
const input = object({
|
|
18425
|
+
environmentId: ids.environmentRef,
|
|
18426
|
+
name: string2().trim().min(1, "Environment variable name cannot be blank.").max(255).describe("Name of the environment variable to remove. Canonicalized the same way as environment.setVariable, so the name that was set is the name that deletes."),
|
|
18427
|
+
workspaceId: workspaceForEnvironmentRef(ids.workspace)
|
|
18428
|
+
});
|
|
18429
|
+
const output = object({
|
|
18430
|
+
name: string2().describe("The canonicalized name that was removed. Returned even when the environment had no such variable.")
|
|
18431
|
+
});
|
|
18432
|
+
return {
|
|
18433
|
+
annotations: {
|
|
18434
|
+
destructiveHint: true,
|
|
18435
|
+
openWorldHint: false,
|
|
18436
|
+
readOnlyHint: false
|
|
18437
|
+
},
|
|
18438
|
+
description: "Remove one environment variable by name. Succeeds whether or not the variable existed.",
|
|
18439
|
+
input,
|
|
18440
|
+
kind: "write",
|
|
18441
|
+
name: "environment.deleteVariable",
|
|
18442
|
+
output
|
|
18443
|
+
};
|
|
18444
|
+
};
|
|
18445
|
+
|
|
17609
18446
|
// node_modules/@qawolf/api-contracts/dist/v1/environment/find.js
|
|
17610
18447
|
var makeFindEnvironmentsContract = (ids) => {
|
|
17611
18448
|
const input = object({
|
|
@@ -17637,7 +18474,8 @@ var makeFindEnvironmentsContract = (ids) => {
|
|
|
17637
18474
|
var makeGetEnvironmentVariableContract = (ids) => {
|
|
17638
18475
|
const input = object({
|
|
17639
18476
|
environmentId: ids.environmentRef,
|
|
17640
|
-
variableNames: array(string2().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.")
|
|
18477
|
+
variableNames: array(string2().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."),
|
|
18478
|
+
workspaceId: workspaceForEnvironmentRef(ids.workspace)
|
|
17641
18479
|
});
|
|
17642
18480
|
const output = object({
|
|
17643
18481
|
missingNames: array(string2()).describe("Requested names the environment does not have, sorted alphabetically."),
|
|
@@ -17663,7 +18501,8 @@ var makeGetEnvironmentVariableContract = (ids) => {
|
|
|
17663
18501
|
// node_modules/@qawolf/api-contracts/dist/v1/environment/index.js
|
|
17664
18502
|
var makeGetEnvironmentContract = (ids) => {
|
|
17665
18503
|
const input = object({
|
|
17666
|
-
environmentId: ids.environmentRef
|
|
18504
|
+
environmentId: ids.environmentRef,
|
|
18505
|
+
workspaceId: workspaceForEnvironmentRef(ids.workspace)
|
|
17667
18506
|
});
|
|
17668
18507
|
const output = makeEnvironmentResourceSchema(ids);
|
|
17669
18508
|
return {
|
|
@@ -17681,7 +18520,8 @@ var makeGetEnvironmentContract = (ids) => {
|
|
|
17681
18520
|
};
|
|
17682
18521
|
var makeListEnvironmentVariableNamesContract = (ids) => {
|
|
17683
18522
|
const input = object({
|
|
17684
|
-
environmentId: ids.environmentRef
|
|
18523
|
+
environmentId: ids.environmentRef,
|
|
18524
|
+
workspaceId: workspaceForEnvironmentRef(ids.workspace)
|
|
17685
18525
|
});
|
|
17686
18526
|
const output = object({
|
|
17687
18527
|
variableNames: array(string2()).describe("Names of the environment's variables, sorted alphabetically. Values are never returned.")
|
|
@@ -17703,7 +18543,8 @@ var makeSetEnvironmentVariableContract = (ids) => {
|
|
|
17703
18543
|
const input = object({
|
|
17704
18544
|
environmentId: ids.environmentRef,
|
|
17705
18545
|
name: string2().trim().min(1, "Environment variable name cannot be blank.").max(255).describe("Name of the environment variable to create or replace. Leading and trailing whitespace is removed, remaining whitespace is replaced with underscores, and letters are uppercased."),
|
|
17706
|
-
value: string2().describe("Value to store. This secret value is accepted but never returned.")
|
|
18546
|
+
value: string2().describe("Value to store. This secret value is accepted but never returned."),
|
|
18547
|
+
workspaceId: workspaceForEnvironmentRef(ids.workspace)
|
|
17707
18548
|
});
|
|
17708
18549
|
const output = object({
|
|
17709
18550
|
name: string2().describe("The stored variable name after whitespace is replaced with underscores and letters are uppercased.")
|
|
@@ -17721,25 +18562,6 @@ var makeSetEnvironmentVariableContract = (ids) => {
|
|
|
17721
18562
|
output
|
|
17722
18563
|
};
|
|
17723
18564
|
};
|
|
17724
|
-
var makeCreateEnvironmentContract = (ids) => {
|
|
17725
|
-
const input = object({
|
|
17726
|
-
name: string2().min(1).max(255).regex(/\S/, "Environment name cannot be blank.").describe("Display name of the new environment."),
|
|
17727
|
-
workspaceId: ids.workspace.optional().describe("The workspace to create the environment in. Required when authenticating with an organization or user API key.")
|
|
17728
|
-
});
|
|
17729
|
-
const output = makeEnvironmentResourceSchema(ids);
|
|
17730
|
-
return {
|
|
17731
|
-
annotations: {
|
|
17732
|
-
destructiveHint: false,
|
|
17733
|
-
openWorldHint: true,
|
|
17734
|
-
readOnlyHint: false
|
|
17735
|
-
},
|
|
17736
|
-
description: "Create an environment on the caller's team and return it in the environment.get shape. This can also create a branch on the team's connected Git provider.",
|
|
17737
|
-
input,
|
|
17738
|
-
kind: "write",
|
|
17739
|
-
name: "environment.create",
|
|
17740
|
-
output
|
|
17741
|
-
};
|
|
17742
|
-
};
|
|
17743
18565
|
|
|
17744
18566
|
// node_modules/@qawolf/api-contracts/dist/v1/environment/update.js
|
|
17745
18567
|
var makeUpdateEnvironmentContract = (ids) => {
|
|
@@ -17748,7 +18570,8 @@ var makeUpdateEnvironmentContract = (ids) => {
|
|
|
17748
18570
|
const input = object({
|
|
17749
18571
|
environmentId: ids.environmentRef,
|
|
17750
18572
|
name: nameSchema.optional(),
|
|
17751
|
-
runConcurrencyLimit: runConcurrencyLimitSchema.optional()
|
|
18573
|
+
runConcurrencyLimit: runConcurrencyLimitSchema.optional(),
|
|
18574
|
+
workspaceId: workspaceForEnvironmentRef(ids.workspace)
|
|
17752
18575
|
}).refine((update) => update.name !== undefined || update.runConcurrencyLimit !== undefined, {
|
|
17753
18576
|
message: "At least one of name or runConcurrencyLimit is required."
|
|
17754
18577
|
});
|
|
@@ -17835,10 +18658,27 @@ var makeListFlowsContract = (ids) => {
|
|
|
17835
18658
|
const input = object({
|
|
17836
18659
|
aiTaskId: ids.aiTask.describe("List the flows on this AI task's branch, including drafts, instead of the ones in the environment. The task must belong to the environment.").optional(),
|
|
17837
18660
|
environmentId: ids.environmentRef,
|
|
17838
|
-
includeDrafts: boolean2().default(false).describe("Include draft flows in the listing.")
|
|
18661
|
+
includeDrafts: boolean2().default(false).describe("Include draft flows in the listing."),
|
|
18662
|
+
workspaceId: workspaceForEnvironmentRef(ids.workspace)
|
|
17839
18663
|
});
|
|
17840
18664
|
const executionTarget = union([string2().min(1), record(string2(), unknown())]).describe("The target the flow executes against, as a preset or object.");
|
|
18665
|
+
const emptyReason = discriminatedUnion("type", [
|
|
18666
|
+
object({
|
|
18667
|
+
type: literal("branch-never-reconciled").describe("The environment's flow code branch has no reconciled commit yet, so there is nothing to list. Read environment.get's flowCodeBranch and list again once syncStatus is reconciled.")
|
|
18668
|
+
}),
|
|
18669
|
+
object({
|
|
18670
|
+
type: literal("commit-has-no-flow-files").describe("The reconciled commit holds no flow file. The branch may never have had one, or a commit removed it: read the branch history at lastSyncedCommitHash before concluding either.")
|
|
18671
|
+
}),
|
|
18672
|
+
object({
|
|
18673
|
+
hiddenDraftCount: number2().int().positive().describe("How many draft flows this listing hid."),
|
|
18674
|
+
type: literal("every-flow-is-a-draft").describe("Every flow this listing matched is a draft, and the listing excluded drafts. Send includeDrafts to see them.")
|
|
18675
|
+
}),
|
|
18676
|
+
object({
|
|
18677
|
+
type: literal("no-flow-matched-the-listing").describe("The reconciled commit contains flow files, but this listing matched none of them: their flows were deleted after the commit reconciled, or a filter excluded every one.")
|
|
18678
|
+
})
|
|
18679
|
+
]).describe("Why this listing has no flows, present only when flows is empty. Read it before concluding that a flow went missing.");
|
|
17841
18680
|
const output = object({
|
|
18681
|
+
emptyReason: emptyReason.optional(),
|
|
17842
18682
|
flows: array(resource({
|
|
17843
18683
|
executionTarget,
|
|
17844
18684
|
flowId: ids.flow,
|
|
@@ -18112,6 +18952,126 @@ var makeUpdateIssueContract = (ids) => {
|
|
|
18112
18952
|
};
|
|
18113
18953
|
};
|
|
18114
18954
|
|
|
18955
|
+
// node_modules/@qawolf/api-contracts/dist/v1/legacyTrigger/resource.js
|
|
18956
|
+
var namedIdentity = object({ id: string2(), name: string2() });
|
|
18957
|
+
var branchScope = union([
|
|
18958
|
+
object({ kind: literal("all-branches") }),
|
|
18959
|
+
object({
|
|
18960
|
+
caseSensitiveGlobs: array(string2()),
|
|
18961
|
+
kind: literal("listed-branches"),
|
|
18962
|
+
negated: boolean2(),
|
|
18963
|
+
raw: string2()
|
|
18964
|
+
})
|
|
18965
|
+
]);
|
|
18966
|
+
var codeHostingServiceRepository = union([
|
|
18967
|
+
object({ kind: literal("any") }),
|
|
18968
|
+
object({ kind: literal("none") }),
|
|
18969
|
+
object({ id: string2(), kind: literal("by-id") })
|
|
18970
|
+
]);
|
|
18971
|
+
var makeLegacyTriggerSchema = (ids) => {
|
|
18972
|
+
const base = {
|
|
18973
|
+
environment: object({
|
|
18974
|
+
id: ids.environment,
|
|
18975
|
+
kind: _enum(["static", "preview"]).describe("Whether the environment is static or a preview. A preview environment holds copies of the static deployment triggers."),
|
|
18976
|
+
name: string2(),
|
|
18977
|
+
terminatedAt: exports_iso.datetime().optional().describe("When the environment was terminated, if it has been.")
|
|
18978
|
+
}),
|
|
18979
|
+
id: ids.legacyTrigger,
|
|
18980
|
+
isPaused: boolean2(),
|
|
18981
|
+
name: string2(),
|
|
18982
|
+
pausedAt: exports_iso.datetime().optional(),
|
|
18983
|
+
tags: array(namedIdentity)
|
|
18984
|
+
};
|
|
18985
|
+
return union([
|
|
18986
|
+
object({
|
|
18987
|
+
...base,
|
|
18988
|
+
nextAt: exports_iso.datetime().optional(),
|
|
18989
|
+
repeatMinutes: number2(),
|
|
18990
|
+
startAt: exports_iso.datetime().optional(),
|
|
18991
|
+
timezoneId: string2().optional(),
|
|
18992
|
+
type: literal("scheduled")
|
|
18993
|
+
}),
|
|
18994
|
+
object({
|
|
18995
|
+
...base,
|
|
18996
|
+
adaptiveFlowSelection: boolean2().describe("Whether QA Wolf picks the flows to run from the deployment's changes. Such a trigger migrates to a generativeSuite action, which names no flows or tags of its own."),
|
|
18997
|
+
branchScope,
|
|
18998
|
+
codeHostingServiceRepository,
|
|
18999
|
+
deploymentPrLabel: string2().optional(),
|
|
19000
|
+
deploymentType: string2().optional(),
|
|
19001
|
+
type: union([literal("generic"), literal("pr-testing-button")])
|
|
19002
|
+
})
|
|
19003
|
+
]);
|
|
19004
|
+
};
|
|
19005
|
+
|
|
19006
|
+
// node_modules/@qawolf/api-contracts/dist/v1/legacyTrigger/find.js
|
|
19007
|
+
var makeFindLegacyTriggersContract = (ids) => {
|
|
19008
|
+
const input = object({
|
|
19009
|
+
kind: _enum(["active", "all", "static"]).default("active").describe(`Which environments to list triggers from. "active", the default, covers the static environments plus the pull request environments still in use. "static" covers the static environments only, where every pull request copy's original lives. "all" adds the pull request environments that have already been torn down, whose environment carries a terminatedAt.`),
|
|
19010
|
+
workspaceId: ids.workspace.optional().describe("The workspace to list legacy triggers from. Required when authenticating with an organization or user API key.")
|
|
19011
|
+
});
|
|
19012
|
+
const output = object({
|
|
19013
|
+
legacyTriggers: array(makeLegacyTriggerSchema(ids)).describe("The workspace's legacy triggers, by name.")
|
|
19014
|
+
});
|
|
19015
|
+
return {
|
|
19016
|
+
annotations: {
|
|
19017
|
+
destructiveHint: false,
|
|
19018
|
+
openWorldHint: false,
|
|
19019
|
+
readOnlyHint: true
|
|
19020
|
+
},
|
|
19021
|
+
description: "List a workspace's legacy per-environment triggers; trigger.find " + "lists the current ones. For migration only; it goes when legacy " + "triggers go. adaptiveFlowSelection becomes a generativeSuite action, " + "which takes no tags.",
|
|
19022
|
+
input,
|
|
19023
|
+
kind: "read",
|
|
19024
|
+
name: "legacyTrigger.find",
|
|
19025
|
+
output
|
|
19026
|
+
};
|
|
19027
|
+
};
|
|
19028
|
+
|
|
19029
|
+
// node_modules/@qawolf/api-contracts/dist/v1/legacyTrigger/lifecycle.js
|
|
19030
|
+
var makeLifecycleShape = (ids) => ({
|
|
19031
|
+
input: object({ legacyTriggerId: ids.legacyTrigger }),
|
|
19032
|
+
output: object({
|
|
19033
|
+
isPaused: boolean2(),
|
|
19034
|
+
legacyTriggerId: ids.legacyTrigger
|
|
19035
|
+
})
|
|
19036
|
+
});
|
|
19037
|
+
var makePauseLegacyTriggerContract = (ids) => {
|
|
19038
|
+
const { input, output } = makeLifecycleShape(ids);
|
|
19039
|
+
return {
|
|
19040
|
+
annotations: {
|
|
19041
|
+
destructiveHint: true,
|
|
19042
|
+
openWorldHint: false,
|
|
19043
|
+
readOnlyHint: false
|
|
19044
|
+
},
|
|
19045
|
+
description: "Pause one legacy trigger, the older per-environment kind, so it stops " + "firing; use trigger.pause for a current one. Its copies in pull " + "request environments pause too. For migration only; it goes when " + "legacy triggers go.",
|
|
19046
|
+
input,
|
|
19047
|
+
kind: "write",
|
|
19048
|
+
name: "legacyTrigger.pause",
|
|
19049
|
+
output
|
|
19050
|
+
};
|
|
19051
|
+
};
|
|
19052
|
+
var makeResumeLegacyTriggerContract = (ids) => {
|
|
19053
|
+
const { input, output } = makeLifecycleShape(ids);
|
|
19054
|
+
return {
|
|
19055
|
+
annotations: {
|
|
19056
|
+
destructiveHint: true,
|
|
19057
|
+
openWorldHint: true,
|
|
19058
|
+
readOnlyHint: false
|
|
19059
|
+
},
|
|
19060
|
+
description: "Resume one legacy trigger, the older per-environment kind; use " + "trigger.resume for a current one. Its copies in pull request " + "environments resume too, and a scheduled one jumps to the next " + "upcoming slot even if it was not paused. For migration only; it goes " + "when legacy triggers go.",
|
|
19061
|
+
input,
|
|
19062
|
+
kind: "write",
|
|
19063
|
+
name: "legacyTrigger.resume",
|
|
19064
|
+
output
|
|
19065
|
+
};
|
|
19066
|
+
};
|
|
19067
|
+
|
|
19068
|
+
// node_modules/@qawolf/api-contracts/dist/v1/legacyTrigger/index.js
|
|
19069
|
+
var makeLegacyTriggerContracts = (ids) => ({
|
|
19070
|
+
find: makeFindLegacyTriggersContract(ids),
|
|
19071
|
+
pause: makePauseLegacyTriggerContract(ids),
|
|
19072
|
+
resume: makeResumeLegacyTriggerContract(ids)
|
|
19073
|
+
});
|
|
19074
|
+
|
|
18115
19075
|
// node_modules/@qawolf/api-contracts/dist/v1/run/diagnose.js
|
|
18116
19076
|
var makeDiagnoseRunContract = (ids) => {
|
|
18117
19077
|
const input = object({
|
|
@@ -18155,7 +19115,8 @@ var makeFindRunsContract = (ids) => {
|
|
|
18155
19115
|
const input = object({
|
|
18156
19116
|
createdAfter: exports_iso.datetime({ offset: true }).optional().describe("Only include runs created after this instant, e.g. the runs since a deploy."),
|
|
18157
19117
|
...makePaginationInputFields({ defaultLimit: 20, maxLimit: 100 }),
|
|
18158
|
-
environmentId: ids.environmentRef
|
|
19118
|
+
environmentId: ids.environmentRef,
|
|
19119
|
+
workspaceId: workspaceForEnvironmentRef(ids.workspace)
|
|
18159
19120
|
});
|
|
18160
19121
|
const output = object({
|
|
18161
19122
|
nextCursor: nextCursorSchema,
|
|
@@ -18303,6 +19264,54 @@ var makeStopRunContract = (ids) => {
|
|
|
18303
19264
|
};
|
|
18304
19265
|
};
|
|
18305
19266
|
|
|
19267
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/triage.js
|
|
19268
|
+
var probability = number2().min(0).max(1);
|
|
19269
|
+
var verdictAnswer = object({
|
|
19270
|
+
choice: _enum(["bug", "maintenance"]),
|
|
19271
|
+
confidence: probability,
|
|
19272
|
+
probabilities: record(string2(), probability),
|
|
19273
|
+
type: literal("choice")
|
|
19274
|
+
});
|
|
19275
|
+
var makeTriageRunContract = (ids) => {
|
|
19276
|
+
const input = object({
|
|
19277
|
+
flowId: ids.flow.describe("The flow that failed in the run."),
|
|
19278
|
+
runId: ids.run
|
|
19279
|
+
});
|
|
19280
|
+
const decision = discriminatedUnion("type", [
|
|
19281
|
+
object({
|
|
19282
|
+
diagnosis: _enum(["bug", "maintenance"]),
|
|
19283
|
+
type: literal("diagnose")
|
|
19284
|
+
}),
|
|
19285
|
+
object({
|
|
19286
|
+
diagnosis: _enum(["bug", "maintenance"]),
|
|
19287
|
+
type: literal("suggest")
|
|
19288
|
+
}),
|
|
19289
|
+
object({
|
|
19290
|
+
type: literal("needsHuman")
|
|
19291
|
+
})
|
|
19292
|
+
]);
|
|
19293
|
+
const output = object({
|
|
19294
|
+
answers: object({
|
|
19295
|
+
verdict: verdictAnswer
|
|
19296
|
+
}),
|
|
19297
|
+
decision: decision.describe("What the triage would do with the verdict. The prototype records nothing; it only reports."),
|
|
19298
|
+
model: string2(),
|
|
19299
|
+
state: json().describe("The context that was sent to the model, for inspection.")
|
|
19300
|
+
});
|
|
19301
|
+
return {
|
|
19302
|
+
annotations: {
|
|
19303
|
+
destructiveHint: false,
|
|
19304
|
+
openWorldHint: false,
|
|
19305
|
+
readOnlyHint: true
|
|
19306
|
+
},
|
|
19307
|
+
description: "Prototype. Ask Jev, a TypeSafe System One model, whether a flow failure in a run is a bug in the product " + "or a test that needs maintenance. Compares the failing attempt with the flow's last passing run in the " + "same environment. Records nothing.",
|
|
19308
|
+
input,
|
|
19309
|
+
kind: "read",
|
|
19310
|
+
name: "run.triage",
|
|
19311
|
+
output
|
|
19312
|
+
};
|
|
19313
|
+
};
|
|
19314
|
+
|
|
18306
19315
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/evaluateSnippet.js
|
|
18307
19316
|
var maxSnippetCodeLength = 64 * 1024;
|
|
18308
19317
|
var makeEvaluateSnippetOnRunnerContract = (ids) => {
|
|
@@ -18347,18 +19356,26 @@ var makeGetRunnerContract = (ids) => {
|
|
|
18347
19356
|
id: runnerIdSchema.describe("Id of the runner to look up."),
|
|
18348
19357
|
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
18349
19358
|
});
|
|
18350
|
-
const
|
|
19359
|
+
const addressed = object({
|
|
18351
19360
|
id: runnerIdSchema.describe("Id of the runner the call addressed."),
|
|
18352
|
-
outcome: literal("success")
|
|
18353
|
-
running: boolean2().describe("False when no runner is running under this id: it was never launched, it was terminated, or it terminated on its own after inactivity. These are not distinguished, because the run system keeps no record of a runner once it is gone.")
|
|
19361
|
+
outcome: literal("success")
|
|
18354
19362
|
});
|
|
19363
|
+
const output = discriminatedUnion("running", [
|
|
19364
|
+
addressed.extend({
|
|
19365
|
+
running: literal(false).describe("No runner is running under this id: it was never launched, it was terminated, or it terminated on its own after inactivity. These are not distinguished, because the run system keeps no record of a runner once it is gone.")
|
|
19366
|
+
}),
|
|
19367
|
+
addressed.extend({
|
|
19368
|
+
running: literal(true).describe("A runner is running under this id and can be addressed."),
|
|
19369
|
+
url: runnerWatchUrlSchema
|
|
19370
|
+
})
|
|
19371
|
+
]);
|
|
18355
19372
|
return {
|
|
18356
19373
|
annotations: {
|
|
18357
19374
|
destructiveHint: false,
|
|
18358
19375
|
openWorldHint: false,
|
|
18359
19376
|
readOnlyHint: true
|
|
18360
19377
|
},
|
|
18361
|
-
description: "Report whether a runner is running under this id on the caller's team. This is a lookup: it never starts a runner, and it does not reset a runner's inactivity clock the way reading its journal does. An id that is not running is a success with `running: false` rather than an error, so this is how to check a runner is still there before addressing it.",
|
|
19378
|
+
description: "Report whether a runner is running under this id on the caller's team. This is a lookup: it never starts a runner, and it does not reset a runner's inactivity clock the way reading its journal does. An id that is not running is a success with `running: false` rather than an error, so this is how to check a runner is still there before addressing it. A running runner also reports the `url` to watch it on, so an id alone is enough to recover the link to give the user.",
|
|
18362
19379
|
input,
|
|
18363
19380
|
kind: "read",
|
|
18364
19381
|
name: "runner.get",
|
|
@@ -18459,7 +19476,7 @@ var makeLaunchRunnerContract = (ids) => {
|
|
|
18459
19476
|
openWorldHint: false,
|
|
18460
19477
|
readOnlyHint: false
|
|
18461
19478
|
},
|
|
18462
|
-
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 `alreadyRunning` to tell which happened.",
|
|
19479
|
+
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 `alreadyRunning` to tell which happened. A success means the runner is answering: the call waits for the pod it started to come up, so the very next call to the runner reaches it. If the pod does not come up in time the call fails instead, leaving the runner running — launching the same id again attaches to it. After a successful launch, put the exact returned `url` in a normal user-visible assistant message so they can watch while the browser is driven. Tool output and internal reasoning do not count as sharing the link. When `alreadyRunning` is true the `url` is the same page, so share it again instead of assuming the caller still has it.",
|
|
18463
19480
|
input,
|
|
18464
19481
|
kind: "write",
|
|
18465
19482
|
name: "runner.launch",
|
|
@@ -18706,6 +19723,75 @@ var makeTakeScreenshotOnRunnerContract = (ids) => {
|
|
|
18706
19723
|
};
|
|
18707
19724
|
};
|
|
18708
19725
|
|
|
19726
|
+
// node_modules/@qawolf/api-contracts/dist/v1/runner/catalog.js
|
|
19727
|
+
var makeRunnerContracts = (ids) => ({
|
|
19728
|
+
evaluateSnippet: makeEvaluateSnippetOnRunnerContract(ids),
|
|
19729
|
+
get: makeGetRunnerContract(ids),
|
|
19730
|
+
highlightSelector: makeHighlightSelectorOnRunnerContract(ids),
|
|
19731
|
+
importPackage: makeImportPackageOnRunnerContract(ids),
|
|
19732
|
+
inspect: makeInspectOnRunnerContract(ids),
|
|
19733
|
+
inspectMobile: makeInspectMobileOnRunnerContract(ids),
|
|
19734
|
+
launch: makeLaunchRunnerContract(ids),
|
|
19735
|
+
list: makeListRunnersContract(ids),
|
|
19736
|
+
performAction: makePerformActionOnRunnerContract(ids),
|
|
19737
|
+
performActions: makePerformActionsOnRunnerContract(ids),
|
|
19738
|
+
promoteSnapshot: makePromoteSnapshotOnRunnerContract(ids),
|
|
19739
|
+
readJournal: makeReadRunnerJournalContract(ids),
|
|
19740
|
+
...makeRecordingContracts(ids),
|
|
19741
|
+
runFlow: makeRunFlowOnRunnerContract(ids),
|
|
19742
|
+
stopRun: makeStopRunOnRunnerContract(ids),
|
|
19743
|
+
takeScreenshot: makeTakeScreenshotOnRunnerContract(ids),
|
|
19744
|
+
terminate: makeTerminateRunnerContract(ids)
|
|
19745
|
+
});
|
|
19746
|
+
|
|
19747
|
+
// node_modules/@qawolf/api-contracts/dist/v1/skill/index.js
|
|
19748
|
+
var skillName = string2().trim().min(1).max(255).describe("The skill's name as skill.list reports it, such as qawolf-onboarding. The qawolf- prefix is optional, so onboarding names the same skill.");
|
|
19749
|
+
var skillSummary = object({
|
|
19750
|
+
description: string2().describe("When to use the skill, in the words the agent routes on."),
|
|
19751
|
+
name: string2().describe("The skill's name. Read it with skill.get.")
|
|
19752
|
+
});
|
|
19753
|
+
var readOnlyAnnotations = {
|
|
19754
|
+
destructiveHint: false,
|
|
19755
|
+
openWorldHint: false,
|
|
19756
|
+
readOnlyHint: true
|
|
19757
|
+
};
|
|
19758
|
+
var makeListSkillsContract = () => {
|
|
19759
|
+
const input = object({});
|
|
19760
|
+
const output = object({
|
|
19761
|
+
skills: array(skillSummary).describe("Every skill the server serves, in catalog order.")
|
|
19762
|
+
});
|
|
19763
|
+
return {
|
|
19764
|
+
annotations: readOnlyAnnotations,
|
|
19765
|
+
description: "List the QA Wolf skills, each with its name and the description that says when to use it. A skill is the instructions a coding agent follows for one kind of QA Wolf work, such as onboarding an application, creating a flow, repairing a failing flow, or setting up triggers. Call this before any QA Wolf work, pick the skill whose description matches the request, and read it with skill.get.",
|
|
19766
|
+
input,
|
|
19767
|
+
kind: "read",
|
|
19768
|
+
name: "skill.list",
|
|
19769
|
+
output
|
|
19770
|
+
};
|
|
19771
|
+
};
|
|
19772
|
+
var makeGetSkillContract = () => {
|
|
19773
|
+
const input = object({ name: skillName });
|
|
19774
|
+
const output = skillSummary.extend({
|
|
19775
|
+
instructions: string2().describe("The skill's SKILL.md, frontmatter included."),
|
|
19776
|
+
references: array(object({
|
|
19777
|
+
path: string2().describe("The file's path relative to the skill directory."),
|
|
19778
|
+
text: string2().describe("The file's content.")
|
|
19779
|
+
})).describe("The files the skill links to under its directory.")
|
|
19780
|
+
});
|
|
19781
|
+
return {
|
|
19782
|
+
annotations: readOnlyAnnotations,
|
|
19783
|
+
description: "Read a QA Wolf skill: the instructions a coding agent follows for one kind of QA Wolf work. The reply carries the skill's SKILL.md and every file under its references directory, so nothing else has to be fetched for it. Read the skill before starting the work it covers and follow it. A link in the reply of the form ../<skill>/SKILL.md names another skill, which this call reads by that name.",
|
|
19784
|
+
input,
|
|
19785
|
+
kind: "read",
|
|
19786
|
+
name: "skill.get",
|
|
19787
|
+
output
|
|
19788
|
+
};
|
|
19789
|
+
};
|
|
19790
|
+
var makeSkillContracts = () => ({
|
|
19791
|
+
get: makeGetSkillContract(),
|
|
19792
|
+
list: makeListSkillsContract()
|
|
19793
|
+
});
|
|
19794
|
+
|
|
18709
19795
|
// node_modules/@qawolf/api-contracts/dist/v1/tag/list.js
|
|
18710
19796
|
var makeListTagsContract = (ids) => {
|
|
18711
19797
|
const input = object({
|
|
@@ -18734,250 +19820,6 @@ var makeListTagsContract = (ids) => {
|
|
|
18734
19820
|
};
|
|
18735
19821
|
};
|
|
18736
19822
|
|
|
18737
|
-
// node_modules/@qawolf/api-contracts/dist/v1/trigger/configurationRules.js
|
|
18738
|
-
var checkAction = (action, ctx) => {
|
|
18739
|
-
const namesFlows = action.flowIds.length + action.tagNames.length > 0;
|
|
18740
|
-
if (action.kind === "generativeSuite") {
|
|
18741
|
-
if (namesFlows) {
|
|
18742
|
-
ctx.addIssue({
|
|
18743
|
-
code: "custom",
|
|
18744
|
-
message: "flowIds and tagNames do not apply when the action is generativeSuite.",
|
|
18745
|
-
path: ["action", "flowIds"]
|
|
18746
|
-
});
|
|
18747
|
-
}
|
|
18748
|
-
return;
|
|
18749
|
-
}
|
|
18750
|
-
if (action.instructions !== undefined) {
|
|
18751
|
-
ctx.addIssue({
|
|
18752
|
-
code: "custom",
|
|
18753
|
-
message: "instructions apply to a generativeSuite action only.",
|
|
18754
|
-
path: ["action", "instructions"]
|
|
18755
|
-
});
|
|
18756
|
-
}
|
|
18757
|
-
if (!namesFlows) {
|
|
18758
|
-
ctx.addIssue({
|
|
18759
|
-
code: "custom",
|
|
18760
|
-
message: "At least one flow or tag is required.",
|
|
18761
|
-
path: ["action", "flowIds"]
|
|
18762
|
-
});
|
|
18763
|
-
}
|
|
18764
|
-
};
|
|
18765
|
-
var checkSchedule = (value, ctx) => {
|
|
18766
|
-
if (value.cadence === "daily") {
|
|
18767
|
-
for (const field of ["timeOfDay", "timezoneId"]) {
|
|
18768
|
-
if (value[field] === undefined) {
|
|
18769
|
-
ctx.addIssue({
|
|
18770
|
-
code: "custom",
|
|
18771
|
-
message: `${field} is required when cadence is daily.`,
|
|
18772
|
-
path: [field]
|
|
18773
|
-
});
|
|
18774
|
-
}
|
|
18775
|
-
}
|
|
18776
|
-
if (value.minuteOfHour !== undefined) {
|
|
18777
|
-
ctx.addIssue({
|
|
18778
|
-
code: "custom",
|
|
18779
|
-
message: "minuteOfHour applies to an hourly cadence only.",
|
|
18780
|
-
path: ["minuteOfHour"]
|
|
18781
|
-
});
|
|
18782
|
-
}
|
|
18783
|
-
} else {
|
|
18784
|
-
for (const field of ["timeOfDay", "timezoneId"]) {
|
|
18785
|
-
if (value[field] !== undefined) {
|
|
18786
|
-
ctx.addIssue({
|
|
18787
|
-
code: "custom",
|
|
18788
|
-
message: `${field} applies to a daily cadence only.`,
|
|
18789
|
-
path: [field]
|
|
18790
|
-
});
|
|
18791
|
-
}
|
|
18792
|
-
}
|
|
18793
|
-
}
|
|
18794
|
-
if (value.action.kind === "generativeSuite") {
|
|
18795
|
-
ctx.addIssue({
|
|
18796
|
-
code: "custom",
|
|
18797
|
-
message: "A schedule trigger cannot use a generativeSuite action.",
|
|
18798
|
-
path: ["action", "kind"]
|
|
18799
|
-
});
|
|
18800
|
-
return;
|
|
18801
|
-
}
|
|
18802
|
-
checkAction(value.action, ctx);
|
|
18803
|
-
};
|
|
18804
|
-
var checkDeployment = (value, ctx) => {
|
|
18805
|
-
if (value.environmentPattern !== undefined && value.environmentIds.length > 0) {
|
|
18806
|
-
ctx.addIssue({
|
|
18807
|
-
code: "custom",
|
|
18808
|
-
message: "Supply environmentIds or environmentPattern, not both.",
|
|
18809
|
-
path: ["environmentPattern"]
|
|
18810
|
-
});
|
|
18811
|
-
}
|
|
18812
|
-
checkAction(value.action, ctx);
|
|
18813
|
-
};
|
|
18814
|
-
var checkTriggerConfiguration = (value, ctx) => value.kind === "schedule" ? checkSchedule(value, ctx) : checkDeployment(value, ctx);
|
|
18815
|
-
|
|
18816
|
-
// node_modules/@qawolf/api-contracts/dist/v1/trigger/create.js
|
|
18817
|
-
var makeCreateTriggerContract = (ids) => {
|
|
18818
|
-
const commonFields = {
|
|
18819
|
-
name: string2().trim().min(1).max(255),
|
|
18820
|
-
workspaceId: ids.workspace.optional().describe("The workspace to create the trigger in. Required when authenticating with an organization or user API key.")
|
|
18821
|
-
};
|
|
18822
|
-
const input = discriminatedUnion("kind", [
|
|
18823
|
-
object({
|
|
18824
|
-
...commonFields,
|
|
18825
|
-
...makeTriggerScheduleFields({
|
|
18826
|
-
environmentId: ids.environmentRef,
|
|
18827
|
-
flow: ids.flow
|
|
18828
|
-
}),
|
|
18829
|
-
kind: literal("schedule")
|
|
18830
|
-
}),
|
|
18831
|
-
object({
|
|
18832
|
-
...commonFields,
|
|
18833
|
-
...makeTriggerDeploymentFields(ids),
|
|
18834
|
-
kind: literal("deployment")
|
|
18835
|
-
})
|
|
18836
|
-
]).superRefine(checkTriggerConfiguration);
|
|
18837
|
-
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
18838
|
-
return {
|
|
18839
|
-
annotations: {
|
|
18840
|
-
destructiveHint: true,
|
|
18841
|
-
openWorldHint: true,
|
|
18842
|
-
readOnlyHint: false
|
|
18843
|
-
},
|
|
18844
|
-
description: "Create a trigger. A schedule trigger runs a named set of flows on a cadence; " + "a deployment trigger runs when a matching deployment is reported.",
|
|
18845
|
-
input,
|
|
18846
|
-
kind: "write",
|
|
18847
|
-
name: "trigger.create",
|
|
18848
|
-
output
|
|
18849
|
-
};
|
|
18850
|
-
};
|
|
18851
|
-
|
|
18852
|
-
// node_modules/@qawolf/api-contracts/dist/v1/trigger/find.js
|
|
18853
|
-
var makeFindTriggersContract = (ids) => {
|
|
18854
|
-
const input = object({
|
|
18855
|
-
isPaused: boolean2().optional().describe("Only list paused, or only unpaused, triggers. Omit for both."),
|
|
18856
|
-
workspaceId: ids.workspace.optional().describe("The workspace to list triggers from. Required when authenticating with an organization or user API key.")
|
|
18857
|
-
});
|
|
18858
|
-
const output = object({
|
|
18859
|
-
triggers: array(makeTriggerResourceSchema(ids)).describe("The team's triggers, newest first.")
|
|
18860
|
-
});
|
|
18861
|
-
return {
|
|
18862
|
-
annotations: {
|
|
18863
|
-
destructiveHint: false,
|
|
18864
|
-
openWorldHint: false,
|
|
18865
|
-
readOnlyHint: true
|
|
18866
|
-
},
|
|
18867
|
-
description: "List the team's triggers, newest first.",
|
|
18868
|
-
input,
|
|
18869
|
-
kind: "read",
|
|
18870
|
-
name: "trigger.find",
|
|
18871
|
-
output
|
|
18872
|
-
};
|
|
18873
|
-
};
|
|
18874
|
-
|
|
18875
|
-
// node_modules/@qawolf/api-contracts/dist/v1/trigger/get.js
|
|
18876
|
-
var makeGetTriggerContract = (ids) => {
|
|
18877
|
-
const input = object({ triggerId: ids.trigger });
|
|
18878
|
-
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
18879
|
-
return {
|
|
18880
|
-
annotations: {
|
|
18881
|
-
destructiveHint: false,
|
|
18882
|
-
openWorldHint: false,
|
|
18883
|
-
readOnlyHint: true
|
|
18884
|
-
},
|
|
18885
|
-
description: "Get one trigger by id.",
|
|
18886
|
-
input,
|
|
18887
|
-
kind: "read",
|
|
18888
|
-
name: "trigger.get",
|
|
18889
|
-
output
|
|
18890
|
-
};
|
|
18891
|
-
};
|
|
18892
|
-
|
|
18893
|
-
// node_modules/@qawolf/api-contracts/dist/v1/trigger/lifecycle.js
|
|
18894
|
-
var makePauseTriggerContract = (ids) => {
|
|
18895
|
-
const input = object({ triggerId: ids.trigger });
|
|
18896
|
-
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
18897
|
-
return {
|
|
18898
|
-
annotations: {
|
|
18899
|
-
destructiveHint: true,
|
|
18900
|
-
openWorldHint: false,
|
|
18901
|
-
readOnlyHint: false
|
|
18902
|
-
},
|
|
18903
|
-
description: "Pause a trigger so it stops firing. Pausing is idempotent. A team whose " + "triggers are all paused reports no trigger activity at all.",
|
|
18904
|
-
input,
|
|
18905
|
-
kind: "write",
|
|
18906
|
-
name: "trigger.pause",
|
|
18907
|
-
output
|
|
18908
|
-
};
|
|
18909
|
-
};
|
|
18910
|
-
var makeResumeTriggerContract = (ids) => {
|
|
18911
|
-
const input = object({ triggerId: ids.trigger });
|
|
18912
|
-
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
18913
|
-
return {
|
|
18914
|
-
annotations: {
|
|
18915
|
-
destructiveHint: true,
|
|
18916
|
-
openWorldHint: true,
|
|
18917
|
-
readOnlyHint: false
|
|
18918
|
-
},
|
|
18919
|
-
description: "Resume a paused trigger. A schedule trigger restarts from the next " + "upcoming slot: the slots it missed while paused do not run.",
|
|
18920
|
-
input,
|
|
18921
|
-
kind: "write",
|
|
18922
|
-
name: "trigger.resume",
|
|
18923
|
-
output
|
|
18924
|
-
};
|
|
18925
|
-
};
|
|
18926
|
-
var makeDeleteTriggerContract = (ids) => {
|
|
18927
|
-
const input = object({ triggerId: ids.trigger });
|
|
18928
|
-
const output = object({
|
|
18929
|
-
triggerId: ids.trigger.describe("The id of the deleted trigger.")
|
|
18930
|
-
});
|
|
18931
|
-
return {
|
|
18932
|
-
annotations: {
|
|
18933
|
-
destructiveHint: true,
|
|
18934
|
-
openWorldHint: false,
|
|
18935
|
-
readOnlyHint: false
|
|
18936
|
-
},
|
|
18937
|
-
description: "Delete a trigger permanently. The runs it already created are kept. " + "To stop a trigger without losing it, use trigger.pause.",
|
|
18938
|
-
input,
|
|
18939
|
-
kind: "write",
|
|
18940
|
-
name: "trigger.delete",
|
|
18941
|
-
output
|
|
18942
|
-
};
|
|
18943
|
-
};
|
|
18944
|
-
|
|
18945
|
-
// node_modules/@qawolf/api-contracts/dist/v1/trigger/update.js
|
|
18946
|
-
var makeUpdateTriggerContract = (ids) => {
|
|
18947
|
-
const commonFields = {
|
|
18948
|
-
name: string2().trim().min(1).max(255),
|
|
18949
|
-
triggerId: ids.trigger
|
|
18950
|
-
};
|
|
18951
|
-
const input = discriminatedUnion("kind", [
|
|
18952
|
-
object({
|
|
18953
|
-
...commonFields,
|
|
18954
|
-
...makeTriggerScheduleFields({
|
|
18955
|
-
environmentId: ids.environmentRef,
|
|
18956
|
-
flow: ids.flow
|
|
18957
|
-
}),
|
|
18958
|
-
kind: literal("schedule")
|
|
18959
|
-
}),
|
|
18960
|
-
object({
|
|
18961
|
-
...commonFields,
|
|
18962
|
-
...makeTriggerDeploymentFields(ids),
|
|
18963
|
-
kind: literal("deployment")
|
|
18964
|
-
})
|
|
18965
|
-
]).superRefine(checkTriggerConfiguration);
|
|
18966
|
-
const output = object({ trigger: makeTriggerResourceSchema(ids) });
|
|
18967
|
-
return {
|
|
18968
|
-
annotations: {
|
|
18969
|
-
destructiveHint: true,
|
|
18970
|
-
openWorldHint: true,
|
|
18971
|
-
readOnlyHint: false
|
|
18972
|
-
},
|
|
18973
|
-
description: "Replace a trigger's configuration. Every field is written, so read the " + "trigger first and send its configuration back with your changes applied. " + "Pausing is separate: use trigger.pause and trigger.resume.",
|
|
18974
|
-
input,
|
|
18975
|
-
kind: "write",
|
|
18976
|
-
name: "trigger.update",
|
|
18977
|
-
output
|
|
18978
|
-
};
|
|
18979
|
-
};
|
|
18980
|
-
|
|
18981
19823
|
// node_modules/@qawolf/api-contracts/dist/v1/index.js
|
|
18982
19824
|
var makeContractsV1 = (ids) => {
|
|
18983
19825
|
const resolvedIds = { ...defaultIdSchemas, ...ids };
|
|
@@ -18986,7 +19828,11 @@ var makeContractsV1 = (ids) => {
|
|
|
18986
19828
|
get: makeAgentGetContract(resolvedIds),
|
|
18987
19829
|
send: makeAgentSendContract(resolvedIds)
|
|
18988
19830
|
},
|
|
18989
|
-
|
|
19831
|
+
codeHostIntegration: {
|
|
19832
|
+
find: makeFindCodeHostIntegrationsContract(resolvedIds),
|
|
19833
|
+
listRepositories: makeListCodeHostRepositoriesContract(resolvedIds)
|
|
19834
|
+
},
|
|
19835
|
+
deployment: makeDeploymentContracts(resolvedIds),
|
|
18990
19836
|
email: {
|
|
18991
19837
|
find: makeFindEmailsContract(resolvedIds),
|
|
18992
19838
|
get: makeGetEmailContract(resolvedIds),
|
|
@@ -19020,44 +19866,23 @@ var makeContractsV1 = (ids) => {
|
|
|
19020
19866
|
removeFlows: makeRemoveFlowsFromIssueContract(resolvedIds),
|
|
19021
19867
|
update: makeUpdateIssueContract(resolvedIds)
|
|
19022
19868
|
},
|
|
19869
|
+
legacyTrigger: makeLegacyTriggerContracts(resolvedIds),
|
|
19023
19870
|
run: {
|
|
19024
19871
|
create: makeCreateRunContract(resolvedIds),
|
|
19025
19872
|
diagnose: makeDiagnoseRunContract(resolvedIds),
|
|
19026
19873
|
find: makeFindRunsContract(resolvedIds),
|
|
19027
19874
|
get: makeGetRunContract(resolvedIds),
|
|
19028
19875
|
reattempt: makeReattemptRunContract(resolvedIds),
|
|
19029
|
-
stop: makeStopRunContract(resolvedIds)
|
|
19030
|
-
|
|
19031
|
-
runner: {
|
|
19032
|
-
evaluateSnippet: makeEvaluateSnippetOnRunnerContract(resolvedIds),
|
|
19033
|
-
get: makeGetRunnerContract(resolvedIds),
|
|
19034
|
-
highlightSelector: makeHighlightSelectorOnRunnerContract(resolvedIds),
|
|
19035
|
-
importPackage: makeImportPackageOnRunnerContract(resolvedIds),
|
|
19036
|
-
inspect: makeInspectOnRunnerContract(resolvedIds),
|
|
19037
|
-
inspectMobile: makeInspectMobileOnRunnerContract(resolvedIds),
|
|
19038
|
-
launch: makeLaunchRunnerContract(resolvedIds),
|
|
19039
|
-
list: makeListRunnersContract(resolvedIds),
|
|
19040
|
-
performAction: makePerformActionOnRunnerContract(resolvedIds),
|
|
19041
|
-
promoteSnapshot: makePromoteSnapshotOnRunnerContract(resolvedIds),
|
|
19042
|
-
readJournal: makeReadRunnerJournalContract(resolvedIds),
|
|
19043
|
-
runFlow: makeRunFlowOnRunnerContract(resolvedIds),
|
|
19044
|
-
stopRun: makeStopRunOnRunnerContract(resolvedIds),
|
|
19045
|
-
takeScreenshot: makeTakeScreenshotOnRunnerContract(resolvedIds),
|
|
19046
|
-
terminate: makeTerminateRunnerContract(resolvedIds)
|
|
19876
|
+
stop: makeStopRunContract(resolvedIds),
|
|
19877
|
+
triage: makeTriageRunContract(resolvedIds)
|
|
19047
19878
|
},
|
|
19879
|
+
runner: makeRunnerContracts(resolvedIds),
|
|
19880
|
+
skill: makeSkillContracts(),
|
|
19048
19881
|
tag: {
|
|
19049
19882
|
create: makeCreateTagContract(resolvedIds),
|
|
19050
19883
|
list: makeListTagsContract(resolvedIds)
|
|
19051
19884
|
},
|
|
19052
|
-
trigger:
|
|
19053
|
-
create: makeCreateTriggerContract(resolvedIds),
|
|
19054
|
-
delete: makeDeleteTriggerContract(resolvedIds),
|
|
19055
|
-
find: makeFindTriggersContract(resolvedIds),
|
|
19056
|
-
get: makeGetTriggerContract(resolvedIds),
|
|
19057
|
-
pause: makePauseTriggerContract(resolvedIds),
|
|
19058
|
-
resume: makeResumeTriggerContract(resolvedIds),
|
|
19059
|
-
update: makeUpdateTriggerContract(resolvedIds)
|
|
19060
|
-
}
|
|
19885
|
+
trigger: makeTriggerContracts(resolvedIds)
|
|
19061
19886
|
};
|
|
19062
19887
|
};
|
|
19063
19888
|
var publicContractsV1 = makeContractsV1(defaultIdSchemas);
|
|
@@ -21565,7 +22390,7 @@ function startUpdateCheck(deps) {
|
|
|
21565
22390
|
// package.json
|
|
21566
22391
|
var package_default = {
|
|
21567
22392
|
name: "@qawolf/cli",
|
|
21568
|
-
version: "1.
|
|
22393
|
+
version: "1.34.0",
|
|
21569
22394
|
description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
21570
22395
|
keywords: [
|
|
21571
22396
|
"automation",
|
|
@@ -21636,7 +22461,7 @@ var package_default = {
|
|
|
21636
22461
|
"@clack/prompts": "1.5.1",
|
|
21637
22462
|
"@napi-rs/keyring": "1.3.0",
|
|
21638
22463
|
"@oxc-node/core": "0.1.0",
|
|
21639
|
-
"@qawolf/api-contracts": "0.
|
|
22464
|
+
"@qawolf/api-contracts": "0.64.0",
|
|
21640
22465
|
"@qawolf/emails": "1.1.1",
|
|
21641
22466
|
"@qawolf/flow-targets": "1.0.0",
|
|
21642
22467
|
"@qawolf/flows": "0.1.4",
|
|
@@ -22177,21 +23002,34 @@ function withoutExclusiveConflict(spec, command, options) {
|
|
|
22177
23002
|
}
|
|
22178
23003
|
|
|
22179
23004
|
// src/domains/agent/send.ts
|
|
23005
|
+
var chatSessionIdEnvironmentVariable = "QAWOLF_CHAT_SESSION_ID";
|
|
22180
23006
|
function parseInput(options) {
|
|
22181
23007
|
return publicContractsV1.agent.send.input.safeParse({
|
|
22182
23008
|
message: options.message,
|
|
22183
23009
|
...options.environmentId === undefined ? {} : { environmentId: options.environmentId },
|
|
22184
23010
|
...options.filePaths === undefined ? {} : { filePaths: options.filePaths },
|
|
23011
|
+
...options.parentSessionId === undefined ? {} : { parentSessionId: options.parentSessionId },
|
|
22185
23012
|
...options.session === undefined ? {} : { sessionId: options.session },
|
|
22186
23013
|
...options.workspaceId === undefined ? {} : { workspaceId: options.workspaceId }
|
|
22187
23014
|
});
|
|
22188
23015
|
}
|
|
23016
|
+
function resolveParentSessionId(options) {
|
|
23017
|
+
if (options.session !== undefined)
|
|
23018
|
+
return;
|
|
23019
|
+
return options.env[chatSessionIdEnvironmentVariable]?.trim() || undefined;
|
|
23020
|
+
}
|
|
22189
23021
|
async function handleAgentSend(ctx, options, deps) {
|
|
22190
23022
|
const timeout = parseFollowTimeout(options.timeout, defaultAgentFollowTimeoutSeconds);
|
|
22191
23023
|
if (!timeout.ok) {
|
|
22192
23024
|
return { error: timeout.error, exitCode: exitCodes.invalidArgs };
|
|
22193
23025
|
}
|
|
22194
|
-
const input = parseInput(
|
|
23026
|
+
const input = parseInput({
|
|
23027
|
+
...options,
|
|
23028
|
+
parentSessionId: resolveParentSessionId({
|
|
23029
|
+
env: deps.env,
|
|
23030
|
+
session: options.session
|
|
23031
|
+
})
|
|
23032
|
+
});
|
|
22195
23033
|
if (!input.success) {
|
|
22196
23034
|
return {
|
|
22197
23035
|
error: prettifyError(input.error),
|
|
@@ -34826,8 +35664,11 @@ var handWrittenContractNames = [
|
|
|
34826
35664
|
"runner.launch",
|
|
34827
35665
|
"runner.list",
|
|
34828
35666
|
"runner.performAction",
|
|
35667
|
+
"runner.performActions",
|
|
34829
35668
|
"runner.promoteSnapshot",
|
|
34830
35669
|
"runner.readJournal",
|
|
35670
|
+
"runner.record",
|
|
35671
|
+
"runner.recordings",
|
|
34831
35672
|
"runner.runFlow",
|
|
34832
35673
|
"runner.stopRun",
|
|
34833
35674
|
"runner.takeScreenshot",
|
|
@@ -34882,30 +35723,385 @@ function registerPublicApiCommands(program, signals, options = {}) {
|
|
|
34882
35723
|
}
|
|
34883
35724
|
}
|
|
34884
35725
|
|
|
34885
|
-
// src/
|
|
34886
|
-
function
|
|
34887
|
-
const
|
|
35726
|
+
// src/core/sentences.ts
|
|
35727
|
+
function appendSentence(text, sentence) {
|
|
35728
|
+
const punctuated = /[.!?]$/.test(text.trimEnd()) ? text.trimEnd() : `${text.trimEnd()}.`;
|
|
35729
|
+
return `${punctuated} ${sentence}`;
|
|
35730
|
+
}
|
|
35731
|
+
|
|
35732
|
+
// src/shell/interactiveRunner/writeScreenshot.ts
|
|
35733
|
+
import { dirname as dirname15 } from "node:path";
|
|
35734
|
+
var jpegStartOfImage = [255, 216, 255];
|
|
35735
|
+
var stdoutPath = "-";
|
|
35736
|
+
async function writeScreenshot(options) {
|
|
35737
|
+
const bytes = Buffer.from(options.imageJpegBase64, "base64");
|
|
35738
|
+
if (jpegStartOfImage.some((byte, index) => bytes[index] !== byte)) {
|
|
35739
|
+
return { ok: false, reason: "not-a-jpeg" };
|
|
35740
|
+
}
|
|
35741
|
+
try {
|
|
35742
|
+
if (options.path === stdoutPath) {
|
|
35743
|
+
await writeToStdout(options.stdout ?? process.stdout, bytes);
|
|
35744
|
+
} else {
|
|
35745
|
+
await options.fs.mkdir(dirname15(options.path), { recursive: true });
|
|
35746
|
+
await options.fs.writeFile(options.path, bytes);
|
|
35747
|
+
}
|
|
35748
|
+
} catch (error) {
|
|
35749
|
+
return { detail: errorMessage(error), ok: false, reason: "unwritable" };
|
|
35750
|
+
}
|
|
35751
|
+
return { ok: true };
|
|
35752
|
+
}
|
|
35753
|
+
function writeToStdout(stdout, bytes) {
|
|
35754
|
+
return new Promise((resolve, reject) => {
|
|
35755
|
+
const onError = (error) => reject(error);
|
|
35756
|
+
stdout.once("error", onError);
|
|
35757
|
+
stdout.write(bytes, (error) => {
|
|
35758
|
+
if (error) {
|
|
35759
|
+
reject(error);
|
|
35760
|
+
return;
|
|
35761
|
+
}
|
|
35762
|
+
stdout.off("error", onError);
|
|
35763
|
+
resolve();
|
|
35764
|
+
});
|
|
35765
|
+
});
|
|
35766
|
+
}
|
|
35767
|
+
|
|
35768
|
+
// src/domains/interactiveRunner/performActionsReasons.ts
|
|
35769
|
+
var exitCodesWorstFirst = [
|
|
35770
|
+
exitCodes.network,
|
|
35771
|
+
exitCodes.timeout,
|
|
35772
|
+
exitCodes.invalidArgs,
|
|
35773
|
+
exitCodes.testFailure
|
|
35774
|
+
];
|
|
35775
|
+
function worstExitCode(codes) {
|
|
35776
|
+
return codes.reduce((worst, code) => exitCodesWorstFirst.indexOf(code) < exitCodesWorstFirst.indexOf(worst) ? code : worst);
|
|
35777
|
+
}
|
|
35778
|
+
function describeAnswerWithoutAResult(failureReason) {
|
|
35779
|
+
return {
|
|
35780
|
+
exitCode: exitCodes.network,
|
|
35781
|
+
why: interactiveRunnerMessages.actionsFailedWithoutAResult(failureReason)
|
|
35782
|
+
};
|
|
35783
|
+
}
|
|
35784
|
+
function describeReason(step) {
|
|
35785
|
+
const { failureReason } = step;
|
|
34888
35786
|
switch (failureReason) {
|
|
34889
|
-
case "
|
|
35787
|
+
case "action-failed":
|
|
34890
35788
|
return {
|
|
34891
|
-
|
|
34892
|
-
|
|
35789
|
+
exitCode: exitCodes.testFailure,
|
|
35790
|
+
why: `reached the runner and did not take effect: ${step.errorMessage}.`
|
|
35791
|
+
};
|
|
35792
|
+
case "action-unconfirmed":
|
|
35793
|
+
return {
|
|
35794
|
+
exitCode: exitCodes.network,
|
|
35795
|
+
why: appendSentence(interactiveRunnerMessages.actionsUnconfirmed(step.errorMessage), interactiveRunnerMessages.actionsMayHaveHappened)
|
|
34893
35796
|
};
|
|
34894
35797
|
case "runner-unreachable":
|
|
34895
35798
|
return {
|
|
34896
|
-
|
|
34897
|
-
|
|
35799
|
+
exitCode: exitCodes.network,
|
|
35800
|
+
why: appendSentence("could not be confirmed: the runner stopped answering.", interactiveRunnerMessages.actionsMayHaveHappened)
|
|
34898
35801
|
};
|
|
34899
|
-
|
|
35802
|
+
case "out-of-time":
|
|
34900
35803
|
return {
|
|
34901
|
-
|
|
34902
|
-
|
|
35804
|
+
exitCode: exitCodes.timeout,
|
|
35805
|
+
why: interactiveRunnerMessages.actionsOutOfTime
|
|
35806
|
+
};
|
|
35807
|
+
case "action-not-supported-on-mobile":
|
|
35808
|
+
return {
|
|
35809
|
+
exitCode: exitCodes.invalidArgs,
|
|
35810
|
+
why: "has no touchscreen equivalent on a mobile runner."
|
|
35811
|
+
};
|
|
35812
|
+
case "screen-needs-a-run":
|
|
35813
|
+
return {
|
|
35814
|
+
exitCode: exitCodes.invalidArgs,
|
|
35815
|
+
why: `was refused: ${interactiveRunnerMessages.screenNeedsARun}`
|
|
35816
|
+
};
|
|
35817
|
+
case "runner-has-no-screen":
|
|
35818
|
+
return {
|
|
35819
|
+
exitCode: exitCodes.invalidArgs,
|
|
35820
|
+
why: `was refused: ${interactiveRunnerMessages.runnerHasNoScreen}`
|
|
35821
|
+
};
|
|
35822
|
+
case "screen-not-ready":
|
|
35823
|
+
return {
|
|
35824
|
+
exitCode: exitCodes.network,
|
|
35825
|
+
why: `was refused: ${interactiveRunnerMessages.screenNotReady}`
|
|
35826
|
+
};
|
|
35827
|
+
default: {
|
|
35828
|
+
return {
|
|
35829
|
+
exitCode: exitCodes.network,
|
|
35830
|
+
why: interactiveRunnerMessages.actionAnsweredUnknown(failureReason)
|
|
34903
35831
|
};
|
|
35832
|
+
}
|
|
35833
|
+
}
|
|
35834
|
+
}
|
|
35835
|
+
|
|
35836
|
+
// src/domains/interactiveRunner/performActionsFailure.ts
|
|
35837
|
+
function describeSequenceFailure(options) {
|
|
35838
|
+
const { actions, answer } = options;
|
|
35839
|
+
const failed = answer.results.flatMap((step) => step.outcome === "failure" ? [step] : []);
|
|
35840
|
+
const described = failed.length > 0 ? failed.map((step) => ({ index: step.index, ...describeReason(step) })) : [
|
|
35841
|
+
{
|
|
35842
|
+
index: answer.failedIndex,
|
|
35843
|
+
...describeAnswerWithoutAResult(answer.failureReason)
|
|
35844
|
+
}
|
|
35845
|
+
];
|
|
35846
|
+
const sentences = [
|
|
35847
|
+
...described.length > 1 ? [
|
|
35848
|
+
interactiveRunnerMessages.actionsNotAllSucceeded(described.length, actions.length)
|
|
35849
|
+
] : [],
|
|
35850
|
+
...described.map(({ index, why }) => interactiveRunnerMessages.actionsFailedAt(index, actions[index]?.type, why)),
|
|
35851
|
+
...answer.stoppedEarly ? [
|
|
35852
|
+
interactiveRunnerMessages.actionsLeftUnperformed(actions.length - answer.results.length)
|
|
35853
|
+
] : [],
|
|
35854
|
+
...answer.lastCompletedIndex === undefined ? [] : [
|
|
35855
|
+
interactiveRunnerMessages.actionsPartlyApplied(answer.lastCompletedIndex)
|
|
35856
|
+
]
|
|
35857
|
+
];
|
|
35858
|
+
return {
|
|
35859
|
+
error: sentences.reduce((text, sentence) => appendSentence(text, sentence)),
|
|
35860
|
+
exitCode: worstExitCode(described.map(({ exitCode }) => exitCode))
|
|
35861
|
+
};
|
|
35862
|
+
}
|
|
35863
|
+
|
|
35864
|
+
// src/domains/interactiveRunner/performActionsOutput.ts
|
|
35865
|
+
function withoutFrames(answer, frames) {
|
|
35866
|
+
const { imageJpegBase64: _final, results, ...rest } = answer;
|
|
35867
|
+
return {
|
|
35868
|
+
...rest,
|
|
35869
|
+
results: results.map((step) => {
|
|
35870
|
+
if (!("imageJpegBase64" in step))
|
|
35871
|
+
return step;
|
|
35872
|
+
const { imageJpegBase64: _frame, ...stepRest } = step;
|
|
35873
|
+
const path = frames.byAction.get(step.index);
|
|
35874
|
+
return path === undefined ? stepRest : { ...stepRest, screenshotPath: path };
|
|
35875
|
+
}),
|
|
35876
|
+
...frames.final === undefined ? {} : { screenshotPath: frames.final }
|
|
35877
|
+
};
|
|
35878
|
+
}
|
|
35879
|
+
function describePerformed(count, frames) {
|
|
35880
|
+
if (frames.final === stdoutPath) {
|
|
35881
|
+
return interactiveRunnerMessages.actionsPerformedScreenshotToStdout(count);
|
|
35882
|
+
}
|
|
35883
|
+
if (frames.final !== undefined) {
|
|
35884
|
+
return interactiveRunnerMessages.actionsPerformedScreenshotWritten(count, frames.final);
|
|
35885
|
+
}
|
|
35886
|
+
if (frames.byAction.size > 0) {
|
|
35887
|
+
return interactiveRunnerMessages.actionsPerformedFramesWritten(count, [
|
|
35888
|
+
...frames.byAction.values()
|
|
35889
|
+
]);
|
|
34904
35890
|
}
|
|
35891
|
+
return interactiveRunnerMessages.actionsPerformed(count);
|
|
35892
|
+
}
|
|
35893
|
+
function describeFailureFrames(frames) {
|
|
35894
|
+
if (frames.problem !== undefined)
|
|
35895
|
+
return frames.problem.error;
|
|
35896
|
+
if (frames.final === stdoutPath) {
|
|
35897
|
+
return interactiveRunnerMessages.actionsFailedScreenshotToStdout;
|
|
35898
|
+
}
|
|
35899
|
+
const paths = [
|
|
35900
|
+
...frames.final === undefined ? [] : [frames.final],
|
|
35901
|
+
...frames.byAction.values()
|
|
35902
|
+
];
|
|
35903
|
+
return paths.length === 0 ? undefined : interactiveRunnerMessages.actionsFailedFramesWritten(paths);
|
|
35904
|
+
}
|
|
35905
|
+
function reportSequence(ctx, options) {
|
|
35906
|
+
const { answer, frames } = options;
|
|
35907
|
+
const message = options.failed ? interactiveRunnerMessages.actionsPerformedOfTotal(answer.results.filter((step) => step.effect === "performed").length, options.actionCount) : describePerformed(answer.results.length, frames);
|
|
35908
|
+
if (!options.toStdout) {
|
|
35909
|
+
ctx.ui.output(withoutFrames(answer, frames), message);
|
|
35910
|
+
return;
|
|
35911
|
+
}
|
|
35912
|
+
if (!options.failed)
|
|
35913
|
+
ctx.ui.success(message);
|
|
35914
|
+
}
|
|
35915
|
+
|
|
35916
|
+
// src/domains/interactiveRunner/performActionsFramePlan.ts
|
|
35917
|
+
import { extname } from "node:path";
|
|
35918
|
+
function indexedFramePath(out, index) {
|
|
35919
|
+
const extension = extname(out);
|
|
35920
|
+
const stem = extension === "" ? out : out.slice(0, -extension.length);
|
|
35921
|
+
return `${stem}-${index}${extension}`;
|
|
35922
|
+
}
|
|
35923
|
+
function isOwedAFinalFrame(answer) {
|
|
35924
|
+
const somethingTookEffect = answer.results.some((step) => step.effect === "performed");
|
|
35925
|
+
const runnerWentQuiet = "failureReason" in answer && answer.failureReason === "runner-unreachable";
|
|
35926
|
+
return somethingTookEffect && !runnerWentQuiet;
|
|
35927
|
+
}
|
|
35928
|
+
function framesAskedFor(answer, out, screenshotMode) {
|
|
35929
|
+
if (screenshotMode === "final") {
|
|
35930
|
+
const frame = {
|
|
35931
|
+
imageJpegBase64: answer.imageJpegBase64,
|
|
35932
|
+
index: undefined,
|
|
35933
|
+
path: out
|
|
35934
|
+
};
|
|
35935
|
+
return frame.imageJpegBase64 === undefined && !isOwedAFinalFrame(answer) ? [] : [frame];
|
|
35936
|
+
}
|
|
35937
|
+
return answer.results.flatMap((step) => {
|
|
35938
|
+
const imageJpegBase64 = "imageJpegBase64" in step ? step.imageJpegBase64 : undefined;
|
|
35939
|
+
return imageJpegBase64 === undefined && step.effect !== "performed" ? [] : [
|
|
35940
|
+
{
|
|
35941
|
+
imageJpegBase64,
|
|
35942
|
+
index: step.index,
|
|
35943
|
+
path: indexedFramePath(out, step.index)
|
|
35944
|
+
}
|
|
35945
|
+
];
|
|
35946
|
+
});
|
|
35947
|
+
}
|
|
35948
|
+
function describeShortfall(options) {
|
|
35949
|
+
const { missing, performedCount, unwritten } = options;
|
|
35950
|
+
const missingIndexes = missing.flatMap((frame) => frame.index === undefined ? [] : [frame.index]);
|
|
35951
|
+
const sentences = [
|
|
35952
|
+
...missingIndexes.length > 0 ? [interactiveRunnerMessages.actionsFramesMissing(missingIndexes)] : [],
|
|
35953
|
+
...missing.some((frame) => frame.index === undefined) ? [
|
|
35954
|
+
interactiveRunnerMessages.actionsPerformedWithoutScreenshot(performedCount)
|
|
35955
|
+
] : [],
|
|
35956
|
+
...unwritten.length > 0 ? [interactiveRunnerMessages.actionsFramesUnwritable(unwritten)] : []
|
|
35957
|
+
];
|
|
35958
|
+
return sentences.length === 0 ? undefined : sentences.reduce((text, sentence) => appendSentence(text, sentence));
|
|
35959
|
+
}
|
|
35960
|
+
|
|
35961
|
+
// src/domains/interactiveRunner/performActionsScreenshots.ts
|
|
35962
|
+
async function writeSequenceFrames(options, deps) {
|
|
35963
|
+
const { answer, out, screenshotMode } = options;
|
|
35964
|
+
if (out === undefined || screenshotMode === "none")
|
|
35965
|
+
return { byAction: new Map, final: undefined, problem: undefined };
|
|
35966
|
+
const asked = framesAskedFor(answer, out, screenshotMode);
|
|
35967
|
+
const arrived = asked.flatMap(({ imageJpegBase64, index, path }) => imageJpegBase64 === undefined ? [] : [{ imageJpegBase64, index, path }]);
|
|
35968
|
+
const writes = await Promise.all(arrived.map(async (frame) => ({
|
|
35969
|
+
frame,
|
|
35970
|
+
written: await deps.writeScreenshot({
|
|
35971
|
+
imageJpegBase64: frame.imageJpegBase64,
|
|
35972
|
+
path: frame.path
|
|
35973
|
+
})
|
|
35974
|
+
})));
|
|
35975
|
+
const unwritten = writes.flatMap(({ frame, written }) => written.ok ? [] : [
|
|
35976
|
+
{
|
|
35977
|
+
detail: written.reason === "not-a-jpeg" ? "it did not arrive as a JPEG" : written.detail,
|
|
35978
|
+
path: frame.path
|
|
35979
|
+
}
|
|
35980
|
+
]);
|
|
35981
|
+
const onDisk = writes.filter(({ written }) => written.ok).map(({ frame }) => frame);
|
|
35982
|
+
const shortfall = describeShortfall({
|
|
35983
|
+
missing: asked.filter((frame) => frame.imageJpegBase64 === undefined),
|
|
35984
|
+
performedCount: answer.results.filter((step) => step.effect === "performed").length,
|
|
35985
|
+
unwritten
|
|
35986
|
+
});
|
|
35987
|
+
return {
|
|
35988
|
+
byAction: new Map(onDisk.flatMap((frame) => frame.index === undefined ? [] : [[frame.index, frame.path]])),
|
|
35989
|
+
final: onDisk.find((frame) => frame.index === undefined)?.path,
|
|
35990
|
+
problem: shortfall === undefined ? undefined : { error: shortfall, exitCode: exitCodes.network }
|
|
35991
|
+
};
|
|
35992
|
+
}
|
|
35993
|
+
|
|
35994
|
+
// src/core/interactiveRunner/browserAction.ts
|
|
35995
|
+
function parseJsonPath(path) {
|
|
35996
|
+
try {
|
|
35997
|
+
return { ok: true, value: JSON.parse(path) };
|
|
35998
|
+
} catch {
|
|
35999
|
+
return {
|
|
36000
|
+
error: `--path must be a JSON array of points, for example '[{"x":10,"y":20},{"x":80,"y":90}]'.`,
|
|
36001
|
+
ok: false
|
|
36002
|
+
};
|
|
36003
|
+
}
|
|
36004
|
+
}
|
|
36005
|
+
function toNumber(value) {
|
|
36006
|
+
return value.trim() === "" ? Number.NaN : Number(value);
|
|
36007
|
+
}
|
|
36008
|
+
function buildBrowserAction(type, flags) {
|
|
36009
|
+
const parsedPath = flags.path === undefined ? undefined : parseJsonPath(flags.path);
|
|
36010
|
+
if (parsedPath !== undefined && !parsedPath.ok)
|
|
36011
|
+
return parsedPath;
|
|
36012
|
+
const candidate = {
|
|
36013
|
+
type,
|
|
36014
|
+
...flags.button === undefined ? {} : { button: flags.button },
|
|
36015
|
+
...flags.keys === undefined ? {} : { keys: flags.keys },
|
|
36016
|
+
...parsedPath === undefined ? {} : { path: parsedPath.value },
|
|
36017
|
+
...flags.scrollX === undefined ? {} : { scroll_x: toNumber(flags.scrollX) },
|
|
36018
|
+
...flags.scrollY === undefined ? {} : { scroll_y: toNumber(flags.scrollY) },
|
|
36019
|
+
...flags.text === undefined ? {} : { text: flags.text },
|
|
36020
|
+
...flags.url === undefined ? {} : { url: flags.url },
|
|
36021
|
+
...flags.x === undefined ? {} : { x: toNumber(flags.x) },
|
|
36022
|
+
...flags.y === undefined ? {} : { y: toNumber(flags.y) }
|
|
36023
|
+
};
|
|
36024
|
+
return parseBrowserAction(candidate);
|
|
36025
|
+
}
|
|
36026
|
+
function parseBrowserAction(candidate) {
|
|
36027
|
+
const parsed = browserActionSchema.safeParse(candidate);
|
|
36028
|
+
if (!parsed.success) {
|
|
36029
|
+
return { error: prettifyError(parsed.error), ok: false };
|
|
36030
|
+
}
|
|
36031
|
+
return { action: parsed.data, ok: true };
|
|
36032
|
+
}
|
|
36033
|
+
|
|
36034
|
+
// src/domains/interactiveRunner/readActions.ts
|
|
36035
|
+
var stdinArgument = "-";
|
|
36036
|
+
async function readActions(argument, deps) {
|
|
36037
|
+
const raw = argument === stdinArgument ? (await deps.readStdin()).trim() : argument;
|
|
36038
|
+
if (argument === stdinArgument && raw === "") {
|
|
36039
|
+
return { error: interactiveRunnerMessages.stdinEmptySequence, ok: false };
|
|
36040
|
+
}
|
|
36041
|
+
const parsed = parseJsonArray(raw);
|
|
36042
|
+
if (!parsed.ok)
|
|
36043
|
+
return parsed;
|
|
36044
|
+
if (parsed.items.length === 0)
|
|
36045
|
+
return { error: interactiveRunnerMessages.actionsEmpty, ok: false };
|
|
36046
|
+
if (parsed.items.length > maxActionsPerRequest) {
|
|
36047
|
+
return {
|
|
36048
|
+
error: interactiveRunnerMessages.actionsOverLimit(parsed.items.length, maxActionsPerRequest),
|
|
36049
|
+
ok: false
|
|
36050
|
+
};
|
|
36051
|
+
}
|
|
36052
|
+
const built = parsed.items.map(parseBrowserAction);
|
|
36053
|
+
const firstRefused = built.findIndex((action) => !action.ok);
|
|
36054
|
+
const refused = built[firstRefused];
|
|
36055
|
+
if (refused !== undefined && !refused.ok) {
|
|
36056
|
+
return {
|
|
36057
|
+
error: interactiveRunnerMessages.actionsInvalidAt(firstRefused, refused.error),
|
|
36058
|
+
ok: false
|
|
36059
|
+
};
|
|
36060
|
+
}
|
|
36061
|
+
return {
|
|
36062
|
+
actions: built.flatMap((action) => action.ok ? [action.action] : []),
|
|
36063
|
+
ok: true
|
|
36064
|
+
};
|
|
36065
|
+
}
|
|
36066
|
+
function parseJsonArray(raw) {
|
|
36067
|
+
try {
|
|
36068
|
+
const value = JSON.parse(raw);
|
|
36069
|
+
return Array.isArray(value) ? { items: value, ok: true } : { error: interactiveRunnerMessages.actionsNotJsonArray, ok: false };
|
|
36070
|
+
} catch {
|
|
36071
|
+
return { error: interactiveRunnerMessages.actionsNotJsonArray, ok: false };
|
|
36072
|
+
}
|
|
36073
|
+
}
|
|
36074
|
+
function refuseUnwritableFrames(ctx, screenshotMode, options) {
|
|
36075
|
+
if (screenshotMode === "none" && options.screenshot !== undefined) {
|
|
36076
|
+
return {
|
|
36077
|
+
error: interactiveRunnerMessages.actionsScreenshotWithModeNone,
|
|
36078
|
+
exitCode: exitCodes.invalidArgs
|
|
36079
|
+
};
|
|
36080
|
+
}
|
|
36081
|
+
if (screenshotMode !== "none" && options.screenshot === undefined) {
|
|
36082
|
+
return {
|
|
36083
|
+
error: screenshotMode === "each" ? interactiveRunnerMessages.actionsEachNeedsAPath : interactiveRunnerMessages.actionsFinalNeedsAPath,
|
|
36084
|
+
exitCode: exitCodes.invalidArgs
|
|
36085
|
+
};
|
|
36086
|
+
}
|
|
36087
|
+
if (screenshotMode === "each" && options.screenshot === stdoutPath) {
|
|
36088
|
+
return {
|
|
36089
|
+
error: interactiveRunnerMessages.actionsEachToStdout,
|
|
36090
|
+
exitCode: exitCodes.invalidArgs
|
|
36091
|
+
};
|
|
36092
|
+
}
|
|
36093
|
+
if (options.screenshot === stdoutPath && ctx.outputMode === "human") {
|
|
36094
|
+
return {
|
|
36095
|
+
error: interactiveRunnerMessages.stdoutIsATerminal("--screenshot"),
|
|
36096
|
+
exitCode: exitCodes.invalidArgs
|
|
36097
|
+
};
|
|
36098
|
+
}
|
|
36099
|
+
return;
|
|
34905
36100
|
}
|
|
34906
36101
|
|
|
34907
36102
|
// src/domains/interactiveRunner/runnerCallOptions.ts
|
|
34908
36103
|
var runnerCallOptions = { timeoutMs: 90000 };
|
|
36104
|
+
var sequenceCallOptions = { timeoutMs: 180000 };
|
|
34909
36105
|
|
|
34910
36106
|
// src/domains/interactiveRunner/launchAndRemember.ts
|
|
34911
36107
|
async function launchRunner(ctx, options) {
|
|
@@ -34995,178 +36191,90 @@ async function resolveRunner(ctx, options, deps) {
|
|
|
34995
36191
|
return {
|
|
34996
36192
|
error: options.noRunnerIdMessage ?? interactiveRunnerMessages.noRunnerId,
|
|
34997
36193
|
exitCode: exitCodes.invalidArgs,
|
|
34998
|
-
type: "failed"
|
|
34999
|
-
};
|
|
35000
|
-
}
|
|
35001
|
-
const launched = await launchAndRemember(ctx, { id: deps.makeRunnerId(), runnerName: undefined }, deps);
|
|
35002
|
-
if (!launched.ok) {
|
|
35003
|
-
return {
|
|
35004
|
-
...failureFields(launched),
|
|
35005
|
-
exitCode: launched.exitCode,
|
|
35006
|
-
type: "failed"
|
|
35007
|
-
};
|
|
35008
|
-
}
|
|
35009
|
-
return {
|
|
35010
|
-
runnerId: launched.value.id,
|
|
35011
|
-
source: "launched",
|
|
35012
|
-
type: "launched",
|
|
35013
|
-
url: launched.value.url
|
|
35014
|
-
};
|
|
35015
|
-
}
|
|
35016
|
-
function announceRunner(ctx, resolved) {
|
|
35017
|
-
if (resolved.type === "launched") {
|
|
35018
|
-
ctx.ui.info(interactiveRunnerMessages.launchedForCommand(resolved.runnerId, resolved.url));
|
|
35019
|
-
}
|
|
35020
|
-
}
|
|
35021
|
-
|
|
35022
|
-
// src/domains/interactiveRunner/runnerRequestFailure.ts
|
|
35023
|
-
function runnerRequestFailure(failure, runner) {
|
|
35024
|
-
const fields = failureFields(failure);
|
|
35025
|
-
if (failure.exitCode !== exitCodes.notFound) {
|
|
35026
|
-
return { ...fields, exitCode: failure.exitCode ?? exitCodes.network };
|
|
35027
|
-
}
|
|
35028
|
-
const cameFrom = interactiveRunnerMessages.runnerIdCameFrom(runner.runnerId, runner.source);
|
|
35029
|
-
return {
|
|
35030
|
-
...fields,
|
|
35031
|
-
errorBody: fields.errorBody ? `${fields.errorBody}
|
|
35032
|
-
${cameFrom}` : cameFrom,
|
|
35033
|
-
exitCode: exitCodes.notFound
|
|
35034
|
-
};
|
|
35035
|
-
}
|
|
35036
|
-
|
|
35037
|
-
// src/core/interactiveRunner/runFiles.ts
|
|
35038
|
-
import { isAbsolute as isAbsolute5, relative as relative10, resolve as resolve11, sep as sep5 } from "node:path";
|
|
35039
|
-
function toCollectedPath(cwd, path) {
|
|
35040
|
-
const absolute = isAbsolute5(path) ? path : resolve11(cwd, path);
|
|
35041
|
-
return relative10(cwd, absolute).split(sep5).join("/");
|
|
35042
|
-
}
|
|
35043
|
-
var namedLargestFileCount = 3;
|
|
35044
|
-
function largestFiles(files) {
|
|
35045
|
-
return Object.entries(files).map(([path, content]) => ({
|
|
35046
|
-
byteLength: runFilesByteLength({ [path]: content }),
|
|
35047
|
-
path
|
|
35048
|
-
})).sort((a, b) => b.byteLength - a.byteLength).slice(0, namedLargestFileCount);
|
|
35049
|
-
}
|
|
35050
|
-
function checkPresenceAndSize(files, requiredPath) {
|
|
35051
|
-
if (!Object.hasOwn(files, requiredPath)) {
|
|
35052
|
-
return { path: requiredPath, type: "missing-file" };
|
|
35053
|
-
}
|
|
35054
|
-
const byteLength = runFilesByteLength(files);
|
|
35055
|
-
if (byteLength > maxRunFilesByteLength) {
|
|
35056
|
-
return {
|
|
35057
|
-
byteLength,
|
|
35058
|
-
largest: largestFiles(files),
|
|
35059
|
-
maxByteLength: maxRunFilesByteLength,
|
|
35060
|
-
type: "too-large"
|
|
36194
|
+
type: "failed"
|
|
35061
36195
|
};
|
|
35062
36196
|
}
|
|
35063
|
-
const
|
|
35064
|
-
if (
|
|
36197
|
+
const launched = await launchAndRemember(ctx, { id: deps.makeRunnerId(), runnerName: undefined }, deps);
|
|
36198
|
+
if (!launched.ok) {
|
|
35065
36199
|
return {
|
|
35066
|
-
|
|
35067
|
-
|
|
35068
|
-
type: "
|
|
36200
|
+
...failureFields(launched),
|
|
36201
|
+
exitCode: launched.exitCode,
|
|
36202
|
+
type: "failed"
|
|
35069
36203
|
};
|
|
35070
36204
|
}
|
|
35071
|
-
return {
|
|
35072
|
-
|
|
35073
|
-
|
|
35074
|
-
|
|
35075
|
-
|
|
35076
|
-
|
|
35077
|
-
if (!Object.hasOwn(files, runPackageJsonPath)) {
|
|
35078
|
-
return { type: "missing-package-json" };
|
|
35079
|
-
}
|
|
35080
|
-
return { type: "ok" };
|
|
35081
|
-
}
|
|
35082
|
-
function checkSnippetFiles(files, contextFilePath) {
|
|
35083
|
-
return checkPresenceAndSize(files, contextFilePath);
|
|
36205
|
+
return {
|
|
36206
|
+
runnerId: launched.value.id,
|
|
36207
|
+
source: "launched",
|
|
36208
|
+
type: "launched",
|
|
36209
|
+
url: launched.value.url
|
|
36210
|
+
};
|
|
35084
36211
|
}
|
|
35085
|
-
function
|
|
35086
|
-
|
|
35087
|
-
|
|
35088
|
-
return interactiveRunnerMessages.fileNotCollected(check.path);
|
|
35089
|
-
case "missing-package-json":
|
|
35090
|
-
return interactiveRunnerMessages.missingPackageJson;
|
|
35091
|
-
case "too-large":
|
|
35092
|
-
return interactiveRunnerMessages.filesTooLarge(check.byteLength, check.maxByteLength, check.largest);
|
|
35093
|
-
case "request-too-large":
|
|
35094
|
-
return interactiveRunnerMessages.requestTooLarge(check.byteLength, check.maxByteLength);
|
|
36212
|
+
function announceRunner(ctx, resolved) {
|
|
36213
|
+
if (resolved.type === "launched") {
|
|
36214
|
+
ctx.ui.info(interactiveRunnerMessages.launchedForCommand(resolved.runnerId, resolved.url));
|
|
35095
36215
|
}
|
|
35096
36216
|
}
|
|
35097
36217
|
|
|
35098
|
-
// src/domains/interactiveRunner/
|
|
35099
|
-
|
|
35100
|
-
|
|
35101
|
-
|
|
35102
|
-
|
|
35103
|
-
const filePath = toCollectedPath(deps.cwd, contextFile);
|
|
35104
|
-
const { files } = await deps.collectRunFiles([filePath]);
|
|
35105
|
-
const check = checkSnippetFiles(files, filePath);
|
|
35106
|
-
if (check.type !== "ok") {
|
|
35107
|
-
return { error: describeRunFilesCheck(check), ok: false };
|
|
36218
|
+
// src/domains/interactiveRunner/runnerRequestFailure.ts
|
|
36219
|
+
function runnerRequestFailure(failure, runner) {
|
|
36220
|
+
const fields = failureFields(failure);
|
|
36221
|
+
if (failure.exitCode !== exitCodes.notFound) {
|
|
36222
|
+
return { ...fields, exitCode: failure.exitCode ?? exitCodes.network };
|
|
35108
36223
|
}
|
|
35109
|
-
|
|
36224
|
+
const cameFrom = interactiveRunnerMessages.runnerIdCameFrom(runner.runnerId, runner.source);
|
|
36225
|
+
return {
|
|
36226
|
+
...fields,
|
|
36227
|
+
errorBody: fields.errorBody ? `${fields.errorBody}
|
|
36228
|
+
${cameFrom}` : cameFrom,
|
|
36229
|
+
exitCode: exitCodes.notFound
|
|
36230
|
+
};
|
|
35110
36231
|
}
|
|
35111
36232
|
|
|
35112
|
-
// src/domains/interactiveRunner/
|
|
35113
|
-
|
|
35114
|
-
|
|
35115
|
-
|
|
35116
|
-
return;
|
|
35117
|
-
|
|
35118
|
-
|
|
35119
|
-
|
|
35120
|
-
|
|
35121
|
-
ok: false
|
|
35122
|
-
};
|
|
35123
|
-
}
|
|
35124
|
-
if (code.trim() === "") {
|
|
35125
|
-
return source === stdinArgument ? { error: interactiveRunnerMessages.stdinEmptySnippet, ok: false } : { error: interactiveRunnerMessages.snippetEmpty(source), ok: false };
|
|
35126
|
-
}
|
|
35127
|
-
return { code, ok: true };
|
|
35128
|
-
}
|
|
35129
|
-
async function handleRunnerExec(ctx, options, deps) {
|
|
35130
|
-
const code = await readCode(options.source, deps);
|
|
35131
|
-
if (!code.ok)
|
|
35132
|
-
return { error: code.error, exitCode: exitCodes.invalidArgs };
|
|
35133
|
-
const scope = await resolveSnippetScope(options.contextFile, deps);
|
|
35134
|
-
if (!scope.ok)
|
|
35135
|
-
return { error: scope.error, exitCode: exitCodes.invalidArgs };
|
|
36233
|
+
// src/domains/interactiveRunner/performActions.ts
|
|
36234
|
+
async function handleRunnerActions(ctx, options, deps) {
|
|
36235
|
+
const read = await readActions(options.actions, deps);
|
|
36236
|
+
if (!read.ok)
|
|
36237
|
+
return { error: read.error, exitCode: exitCodes.invalidArgs };
|
|
36238
|
+
const screenshotMode = options.screenshotMode ?? (options.screenshot === undefined ? "none" : "final");
|
|
36239
|
+
const refusedLocally = refuseUnwritableFrames(ctx, screenshotMode, options);
|
|
36240
|
+
if (refusedLocally !== undefined)
|
|
36241
|
+
return refusedLocally;
|
|
35136
36242
|
const resolved = await resolveRunner(ctx, { autoLaunch: true, runner: options.runner }, deps);
|
|
35137
|
-
if (resolved.type === "failed")
|
|
36243
|
+
if (resolved.type === "failed")
|
|
35138
36244
|
return { ...failureFields(resolved), exitCode: resolved.exitCode };
|
|
35139
|
-
}
|
|
35140
36245
|
announceRunner(ctx, resolved);
|
|
35141
|
-
const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.
|
|
35142
|
-
|
|
36246
|
+
const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.performActions, {
|
|
36247
|
+
actions: read.actions,
|
|
35143
36248
|
id: resolved.runnerId,
|
|
35144
|
-
|
|
35145
|
-
|
|
35146
|
-
},
|
|
36249
|
+
screenshotMode,
|
|
36250
|
+
stopOnFailure: !options.continueOnFailure
|
|
36251
|
+
}, sequenceCallOptions);
|
|
35147
36252
|
if (!result.ok) {
|
|
35148
|
-
|
|
35149
|
-
|
|
35150
|
-
|
|
35151
|
-
|
|
35152
|
-
|
|
35153
|
-
switch (result.value.result) {
|
|
35154
|
-
case "success":
|
|
35155
|
-
ctx.ui.output({ outcome: "evaluated", result: "success" }, interactiveRunnerMessages.snippetRan);
|
|
35156
|
-
return;
|
|
35157
|
-
case "stopped":
|
|
35158
|
-
return {
|
|
35159
|
-
error: interactiveRunnerMessages.snippetStopped,
|
|
35160
|
-
exitCode: exitCodes.testFailure
|
|
35161
|
-
};
|
|
35162
|
-
case "error":
|
|
35163
|
-
return {
|
|
35164
|
-
error: interactiveRunnerMessages.snippetErrored(result.value.errorMessage),
|
|
35165
|
-
exitCode: exitCodes.testFailure
|
|
35166
|
-
};
|
|
36253
|
+
const fields = runnerRequestFailure(result, resolved);
|
|
36254
|
+
return result.mayHaveArrived ? {
|
|
36255
|
+
...fields,
|
|
36256
|
+
error: appendSentence(fields.error, interactiveRunnerMessages.actionsMayHaveHappened)
|
|
36257
|
+
} : fields;
|
|
35167
36258
|
}
|
|
36259
|
+
const answer = result.value;
|
|
36260
|
+
const frames = await writeSequenceFrames({ answer, out: options.screenshot, screenshotMode }, deps);
|
|
36261
|
+
const failure = answer.outcome === "success" ? undefined : describeSequenceFailure({ actions: read.actions, answer });
|
|
36262
|
+
reportSequence(ctx, {
|
|
36263
|
+
actionCount: read.actions.length,
|
|
36264
|
+
answer,
|
|
36265
|
+
failed: failure !== undefined,
|
|
36266
|
+
frames,
|
|
36267
|
+
toStdout: screenshotMode !== "none" && options.screenshot === stdoutPath
|
|
36268
|
+
});
|
|
36269
|
+
if (failure === undefined)
|
|
36270
|
+
return frames.problem;
|
|
36271
|
+
const framesNote = describeFailureFrames(frames);
|
|
36272
|
+
return framesNote === undefined ? failure : { ...failure, error: appendSentence(failure.error, framesNote) };
|
|
35168
36273
|
}
|
|
35169
36274
|
|
|
36275
|
+
// src/domains/interactiveRunner/deps.ts
|
|
36276
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
36277
|
+
|
|
35170
36278
|
// src/shell/interactiveRunner/collectRunFiles.ts
|
|
35171
36279
|
import { join as join53 } from "node:path";
|
|
35172
36280
|
|
|
@@ -35207,7 +36315,7 @@ function isPathAlias(importPath, paths) {
|
|
|
35207
36315
|
}
|
|
35208
36316
|
|
|
35209
36317
|
// src/core/interactiveRunner/resolveImportPath.ts
|
|
35210
|
-
import { dirname as
|
|
36318
|
+
import { dirname as dirname16, join as join52, normalize as normalize3 } from "node:path/posix";
|
|
35211
36319
|
var supportedExtensions = [".ts", ".js"];
|
|
35212
36320
|
function candidatesForExplicitExtension(options) {
|
|
35213
36321
|
const matching = supportedExtensions.find((extension) => options.importPath.endsWith(extension));
|
|
@@ -35221,7 +36329,7 @@ function candidatesForExplicitExtension(options) {
|
|
|
35221
36329
|
}
|
|
35222
36330
|
function resolveImportPath(options) {
|
|
35223
36331
|
const aliasTarget = resolvePathAlias(options.importPath, options.tsconfigPaths);
|
|
35224
|
-
const resolvedPath = aliasTarget === undefined ? normalize3(join52(
|
|
36332
|
+
const resolvedPath = aliasTarget === undefined ? normalize3(join52(dirname16(options.importingFilePath), options.importPath)) : normalize3(aliasTarget);
|
|
35225
36333
|
const hasExplicitExtension = supportedExtensions.some((extension) => options.importPath.endsWith(extension));
|
|
35226
36334
|
const candidates = hasExplicitExtension ? candidatesForExplicitExtension({
|
|
35227
36335
|
importPath: options.importPath,
|
|
@@ -35417,79 +36525,216 @@ function makeRunnerStore(options) {
|
|
|
35417
36525
|
};
|
|
35418
36526
|
}
|
|
35419
36527
|
|
|
35420
|
-
// src/shell/
|
|
35421
|
-
|
|
35422
|
-
|
|
35423
|
-
|
|
35424
|
-
|
|
35425
|
-
const
|
|
35426
|
-
|
|
35427
|
-
|
|
36528
|
+
// src/shell/stdin.ts
|
|
36529
|
+
async function readStdin2() {
|
|
36530
|
+
if (process.stdin.isTTY === true)
|
|
36531
|
+
return "";
|
|
36532
|
+
const chunks = [];
|
|
36533
|
+
for await (const chunk of process.stdin)
|
|
36534
|
+
chunks.push(chunk);
|
|
36535
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
36536
|
+
}
|
|
36537
|
+
|
|
36538
|
+
// src/domains/interactiveRunner/deps.ts
|
|
36539
|
+
function makeInteractiveRunnerDeps(options) {
|
|
36540
|
+
return {
|
|
36541
|
+
collectRunFiles: (roots) => collectRunFiles({ cwd: options.cwd, fs: options.fs, roots }),
|
|
36542
|
+
cwd: options.cwd,
|
|
36543
|
+
env: options.env,
|
|
36544
|
+
makeRunnerId,
|
|
36545
|
+
makeRecordingId: randomUUID3,
|
|
36546
|
+
readFile: (path) => options.fs.readFile(path),
|
|
36547
|
+
readStdin: readStdin2,
|
|
36548
|
+
runFilesManifest: makeRunFilesManifestStore({
|
|
36549
|
+
cwd: options.cwd,
|
|
36550
|
+
fs: options.fs
|
|
36551
|
+
}),
|
|
36552
|
+
sleep,
|
|
36553
|
+
store: makeRunnerStore({ cwd: options.cwd, fs: options.fs }),
|
|
36554
|
+
writeScreenshot: (screenshot) => writeScreenshot({ ...screenshot, fs: options.fs })
|
|
36555
|
+
};
|
|
36556
|
+
}
|
|
36557
|
+
|
|
36558
|
+
// src/commands/runner/context.ts
|
|
36559
|
+
var runnerFlagDescription = "Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner";
|
|
36560
|
+
function runnerDeps(ctx) {
|
|
36561
|
+
return makeInteractiveRunnerDeps({
|
|
36562
|
+
cwd: process.cwd(),
|
|
36563
|
+
env: process.env,
|
|
36564
|
+
fs: ctx.fs
|
|
36565
|
+
});
|
|
36566
|
+
}
|
|
36567
|
+
|
|
36568
|
+
// src/commands/runner/actions.register.ts
|
|
36569
|
+
var actionsExamples = `
|
|
36570
|
+
Examples:
|
|
36571
|
+
$ qawolf runner actions '[{"type":"click","button":"left","x":480,"y":260},{"type":"type","text":"hello@example.com"},{"type":"keypress","keys":["Enter"]}]' --screenshot after-login.jpg
|
|
36572
|
+
$ echo '[{"type":"click","button":"left","x":1,"y":2},{"type":"type","text":"hi"}]' | qawolf runner actions -
|
|
36573
|
+
$ qawolf runner actions '[{"type":"click","button":"left","x":480,"y":260},{"type":"type","text":"hello"}]' --screenshot-mode each --screenshot step.jpg
|
|
36574
|
+
$ qawolf runner actions '[{"type":"scroll","x":480,"y":260,"scroll_x":0,"scroll_y":600},{"type":"click","button":"left","x":120,"y":700}]' --continue-on-failure`;
|
|
36575
|
+
function registerRunnerActionsCommand(runner, signals) {
|
|
36576
|
+
declareCommandKind(runner.command("actions <sequence>"), "write").description("Perform a sequence of up to ten raw actions on a runner's screen in one request, as a JSON array of the same actions `runner act` takes. Use - to read the array from stdin. Actions run back to back, so batch only steps whose targets are on the screen you last saw, and end the sequence at the step that changes the page. Each result carries an effect: performed, not-performed, or unknown when the runner stopped answering and the action may have landed").option("--continue-on-failure", "Carry on past an action that reached the runner and did not take effect; a runner that cannot be reached or a screen that cannot serve still ends the sequence").option("--runner <id>", runnerFlagDescription).option("--screenshot <path>", "Save a JPEG of the screen after the last action to this file. With --screenshot-mode each, one file per action, with the action's index before the extension. - writes the final frame to stdout and moves the confirmation to stderr").addOption(new Option2("--screenshot-mode <mode>", "Defaults to final when --screenshot is given, none otherwise").choices([...screenshotModes])).addHelpText("after", actionsExamples).action((sequence, opts, command) => withAuthContext(signals, (ctx) => handleRunnerActions(ctx, {
|
|
36577
|
+
actions: sequence,
|
|
36578
|
+
continueOnFailure: opts.continueOnFailure === true,
|
|
36579
|
+
runner: opts.runner,
|
|
36580
|
+
screenshot: opts.screenshot,
|
|
36581
|
+
screenshotMode: opts.screenshotMode
|
|
36582
|
+
}, runnerDeps(ctx)))(opts, command));
|
|
36583
|
+
}
|
|
36584
|
+
|
|
36585
|
+
// src/domains/interactiveRunner/evaluateSnippetFailure.ts
|
|
36586
|
+
function describeEvaluateSnippetFailure(failure) {
|
|
36587
|
+
const { failureReason } = failure;
|
|
36588
|
+
switch (failureReason) {
|
|
36589
|
+
case "runner-cannot-evaluate-snippets":
|
|
36590
|
+
return {
|
|
36591
|
+
error: interactiveRunnerMessages.runnerCannotEvaluateSnippets,
|
|
36592
|
+
exitCode: exitCodes.invalidArgs
|
|
36593
|
+
};
|
|
36594
|
+
case "runner-unreachable":
|
|
36595
|
+
return {
|
|
36596
|
+
error: interactiveRunnerMessages.snippetRunnerUnreachable,
|
|
36597
|
+
exitCode: exitCodes.network
|
|
36598
|
+
};
|
|
36599
|
+
default:
|
|
36600
|
+
return {
|
|
36601
|
+
error: interactiveRunnerMessages.snippetAnsweredUnknown(failureReason),
|
|
36602
|
+
exitCode: exitCodes.network
|
|
36603
|
+
};
|
|
35428
36604
|
}
|
|
35429
|
-
|
|
35430
|
-
|
|
35431
|
-
|
|
35432
|
-
|
|
35433
|
-
|
|
35434
|
-
|
|
35435
|
-
|
|
35436
|
-
|
|
35437
|
-
|
|
36605
|
+
}
|
|
36606
|
+
|
|
36607
|
+
// src/core/interactiveRunner/runFiles.ts
|
|
36608
|
+
import { isAbsolute as isAbsolute5, relative as relative10, resolve as resolve11, sep as sep5 } from "node:path";
|
|
36609
|
+
function toCollectedPath(cwd, path) {
|
|
36610
|
+
const absolute = isAbsolute5(path) ? path : resolve11(cwd, path);
|
|
36611
|
+
return relative10(cwd, absolute).split(sep5).join("/");
|
|
36612
|
+
}
|
|
36613
|
+
var namedLargestFileCount = 3;
|
|
36614
|
+
function largestFiles(files) {
|
|
36615
|
+
return Object.entries(files).map(([path, content]) => ({
|
|
36616
|
+
byteLength: runFilesByteLength({ [path]: content }),
|
|
36617
|
+
path
|
|
36618
|
+
})).sort((a, b) => b.byteLength - a.byteLength).slice(0, namedLargestFileCount);
|
|
36619
|
+
}
|
|
36620
|
+
function checkPresenceAndSize(files, requiredPath) {
|
|
36621
|
+
if (!Object.hasOwn(files, requiredPath)) {
|
|
36622
|
+
return { path: requiredPath, type: "missing-file" };
|
|
35438
36623
|
}
|
|
35439
|
-
|
|
36624
|
+
const byteLength = runFilesByteLength(files);
|
|
36625
|
+
if (byteLength > maxRunFilesByteLength) {
|
|
36626
|
+
return {
|
|
36627
|
+
byteLength,
|
|
36628
|
+
largest: largestFiles(files),
|
|
36629
|
+
maxByteLength: maxRunFilesByteLength,
|
|
36630
|
+
type: "too-large"
|
|
36631
|
+
};
|
|
36632
|
+
}
|
|
36633
|
+
const encodedByteLength = Buffer.byteLength(JSON.stringify({ files, path: requiredPath }), "utf8");
|
|
36634
|
+
if (encodedByteLength > maxRunnerRequestEncodedByteLength) {
|
|
36635
|
+
return {
|
|
36636
|
+
byteLength: encodedByteLength,
|
|
36637
|
+
maxByteLength: maxRunnerRequestEncodedByteLength,
|
|
36638
|
+
type: "request-too-large"
|
|
36639
|
+
};
|
|
36640
|
+
}
|
|
36641
|
+
return { type: "ok" };
|
|
35440
36642
|
}
|
|
35441
|
-
function
|
|
35442
|
-
|
|
35443
|
-
|
|
35444
|
-
|
|
35445
|
-
|
|
35446
|
-
|
|
35447
|
-
|
|
35448
|
-
|
|
35449
|
-
}
|
|
35450
|
-
stdout.off("error", onError);
|
|
35451
|
-
resolve();
|
|
35452
|
-
});
|
|
35453
|
-
});
|
|
36643
|
+
function checkRunFiles(files, entryPointPath) {
|
|
36644
|
+
const present = checkPresenceAndSize(files, entryPointPath);
|
|
36645
|
+
if (present.type !== "ok")
|
|
36646
|
+
return present;
|
|
36647
|
+
if (!Object.hasOwn(files, runPackageJsonPath)) {
|
|
36648
|
+
return { type: "missing-package-json" };
|
|
36649
|
+
}
|
|
36650
|
+
return { type: "ok" };
|
|
35454
36651
|
}
|
|
35455
|
-
|
|
35456
|
-
|
|
35457
|
-
|
|
35458
|
-
|
|
35459
|
-
|
|
35460
|
-
|
|
35461
|
-
|
|
35462
|
-
|
|
35463
|
-
|
|
36652
|
+
function checkSnippetFiles(files, contextFilePath) {
|
|
36653
|
+
return checkPresenceAndSize(files, contextFilePath);
|
|
36654
|
+
}
|
|
36655
|
+
function describeRunFilesCheck(check) {
|
|
36656
|
+
switch (check.type) {
|
|
36657
|
+
case "missing-file":
|
|
36658
|
+
return interactiveRunnerMessages.fileNotCollected(check.path);
|
|
36659
|
+
case "missing-package-json":
|
|
36660
|
+
return interactiveRunnerMessages.missingPackageJson;
|
|
36661
|
+
case "too-large":
|
|
36662
|
+
return interactiveRunnerMessages.filesTooLarge(check.byteLength, check.maxByteLength, check.largest);
|
|
36663
|
+
case "request-too-large":
|
|
36664
|
+
return interactiveRunnerMessages.requestTooLarge(check.byteLength, check.maxByteLength);
|
|
36665
|
+
}
|
|
35464
36666
|
}
|
|
35465
36667
|
|
|
35466
|
-
// src/domains/interactiveRunner/
|
|
35467
|
-
function
|
|
35468
|
-
|
|
35469
|
-
|
|
35470
|
-
|
|
35471
|
-
|
|
35472
|
-
|
|
35473
|
-
|
|
35474
|
-
|
|
35475
|
-
|
|
35476
|
-
|
|
35477
|
-
|
|
35478
|
-
}),
|
|
35479
|
-
sleep,
|
|
35480
|
-
store: makeRunnerStore({ cwd: options.cwd, fs: options.fs }),
|
|
35481
|
-
writeScreenshot: (screenshot) => writeScreenshot({ ...screenshot, fs: options.fs })
|
|
35482
|
-
};
|
|
36668
|
+
// src/domains/interactiveRunner/snippetScope.ts
|
|
36669
|
+
async function resolveSnippetScope(contextFile, deps) {
|
|
36670
|
+
if (contextFile === undefined) {
|
|
36671
|
+
return { filePath: undefined, files: undefined, ok: true };
|
|
36672
|
+
}
|
|
36673
|
+
const filePath = toCollectedPath(deps.cwd, contextFile);
|
|
36674
|
+
const { files } = await deps.collectRunFiles([filePath]);
|
|
36675
|
+
const check = checkSnippetFiles(files, filePath);
|
|
36676
|
+
if (check.type !== "ok") {
|
|
36677
|
+
return { error: describeRunFilesCheck(check), ok: false };
|
|
36678
|
+
}
|
|
36679
|
+
return { filePath, files, ok: true };
|
|
35483
36680
|
}
|
|
35484
36681
|
|
|
35485
|
-
// src/
|
|
35486
|
-
var
|
|
35487
|
-
function
|
|
35488
|
-
|
|
35489
|
-
|
|
35490
|
-
env: process.env,
|
|
35491
|
-
fs: ctx.fs
|
|
36682
|
+
// src/domains/interactiveRunner/evaluateSnippet.ts
|
|
36683
|
+
var stdinArgument2 = "-";
|
|
36684
|
+
async function readCode(source, deps) {
|
|
36685
|
+
const code = source === stdinArgument2 ? await deps.readStdin() : await deps.readFile(source).catch(() => {
|
|
36686
|
+
return;
|
|
35492
36687
|
});
|
|
36688
|
+
if (code === undefined) {
|
|
36689
|
+
return {
|
|
36690
|
+
error: interactiveRunnerMessages.snippetFileUnreadable(source),
|
|
36691
|
+
ok: false
|
|
36692
|
+
};
|
|
36693
|
+
}
|
|
36694
|
+
if (code.trim() === "") {
|
|
36695
|
+
return source === stdinArgument2 ? { error: interactiveRunnerMessages.stdinEmptySnippet, ok: false } : { error: interactiveRunnerMessages.snippetEmpty(source), ok: false };
|
|
36696
|
+
}
|
|
36697
|
+
return { code, ok: true };
|
|
36698
|
+
}
|
|
36699
|
+
async function handleRunnerExec(ctx, options, deps) {
|
|
36700
|
+
const code = await readCode(options.source, deps);
|
|
36701
|
+
if (!code.ok)
|
|
36702
|
+
return { error: code.error, exitCode: exitCodes.invalidArgs };
|
|
36703
|
+
const scope = await resolveSnippetScope(options.contextFile, deps);
|
|
36704
|
+
if (!scope.ok)
|
|
36705
|
+
return { error: scope.error, exitCode: exitCodes.invalidArgs };
|
|
36706
|
+
const resolved = await resolveRunner(ctx, { autoLaunch: true, runner: options.runner }, deps);
|
|
36707
|
+
if (resolved.type === "failed") {
|
|
36708
|
+
return { ...failureFields(resolved), exitCode: resolved.exitCode };
|
|
36709
|
+
}
|
|
36710
|
+
announceRunner(ctx, resolved);
|
|
36711
|
+
const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.evaluateSnippet, {
|
|
36712
|
+
code: code.code,
|
|
36713
|
+
id: resolved.runnerId,
|
|
36714
|
+
...scope.filePath === undefined ? {} : { filePath: scope.filePath },
|
|
36715
|
+
...scope.files === undefined ? {} : { files: scope.files }
|
|
36716
|
+
}, runnerCallOptions);
|
|
36717
|
+
if (!result.ok) {
|
|
36718
|
+
return runnerRequestFailure(result, resolved);
|
|
36719
|
+
}
|
|
36720
|
+
if (result.value.outcome === "failure") {
|
|
36721
|
+
return describeEvaluateSnippetFailure(result.value);
|
|
36722
|
+
}
|
|
36723
|
+
switch (result.value.result) {
|
|
36724
|
+
case "success":
|
|
36725
|
+
ctx.ui.output({ outcome: "evaluated", result: "success" }, interactiveRunnerMessages.snippetRan);
|
|
36726
|
+
return;
|
|
36727
|
+
case "stopped":
|
|
36728
|
+
return {
|
|
36729
|
+
error: interactiveRunnerMessages.snippetStopped,
|
|
36730
|
+
exitCode: exitCodes.testFailure
|
|
36731
|
+
};
|
|
36732
|
+
case "error":
|
|
36733
|
+
return {
|
|
36734
|
+
error: interactiveRunnerMessages.snippetErrored(result.value.errorMessage),
|
|
36735
|
+
exitCode: exitCodes.testFailure
|
|
36736
|
+
};
|
|
36737
|
+
}
|
|
35493
36738
|
}
|
|
35494
36739
|
|
|
35495
36740
|
// src/commands/runner/exec.register.ts
|
|
@@ -35761,7 +37006,7 @@ var blankInspectMobileFlags = {
|
|
|
35761
37006
|
x: undefined,
|
|
35762
37007
|
y: undefined
|
|
35763
37008
|
};
|
|
35764
|
-
function
|
|
37009
|
+
function toNumber2(value) {
|
|
35765
37010
|
return value.trim() === "" ? Number.NaN : Number(value);
|
|
35766
37011
|
}
|
|
35767
37012
|
var groupsOf = (flags) => ({
|
|
@@ -35797,8 +37042,8 @@ function buildInspectMobileRequest(what, flags) {
|
|
|
35797
37042
|
...flags.selector === undefined ? {} : { selector: flags.selector },
|
|
35798
37043
|
...flags.strategy === undefined ? {} : { strategy: flags.strategy },
|
|
35799
37044
|
...flags.text === undefined ? {} : { text: flags.text },
|
|
35800
|
-
...flags.x === undefined ? {} : { x:
|
|
35801
|
-
...flags.y === undefined ? {} : { y:
|
|
37045
|
+
...flags.x === undefined ? {} : { x: toNumber2(flags.x) },
|
|
37046
|
+
...flags.y === undefined ? {} : { y: toNumber2(flags.y) }
|
|
35802
37047
|
};
|
|
35803
37048
|
const parsed = inspectMobileRequestSchema.safeParse(candidate);
|
|
35804
37049
|
if (!parsed.success) {
|
|
@@ -35964,12 +37209,6 @@ function registerRunnerInspectCommands(runner, signals) {
|
|
|
35964
37209
|
registerRunnerInspectMobileCommands(inspect, signals);
|
|
35965
37210
|
}
|
|
35966
37211
|
|
|
35967
|
-
// src/core/sentences.ts
|
|
35968
|
-
function appendSentence(text, sentence) {
|
|
35969
|
-
const punctuated = /[.!?]$/.test(text.trimEnd()) ? text.trimEnd() : `${text.trimEnd()}.`;
|
|
35970
|
-
return `${punctuated} ${sentence}`;
|
|
35971
|
-
}
|
|
35972
|
-
|
|
35973
37212
|
// src/domains/interactiveRunner/performActionFailure.ts
|
|
35974
37213
|
function describePerformActionFailure(options) {
|
|
35975
37214
|
const { actionType, failure } = options;
|
|
@@ -36064,50 +37303,10 @@ async function describeFailureScreenshot(options, deps) {
|
|
|
36064
37303
|
return options.out === stdoutPath ? interactiveRunnerMessages.actionFailedScreenshotToStdout : interactiveRunnerMessages.actionFailedScreenshotWritten(options.out);
|
|
36065
37304
|
}
|
|
36066
37305
|
|
|
36067
|
-
// src/core/interactiveRunner/browserAction.ts
|
|
36068
|
-
function parseJsonPath(path) {
|
|
36069
|
-
try {
|
|
36070
|
-
return { ok: true, value: JSON.parse(path) };
|
|
36071
|
-
} catch {
|
|
36072
|
-
return {
|
|
36073
|
-
error: `--path must be a JSON array of points, for example '[{"x":10,"y":20},{"x":80,"y":90}]'.`,
|
|
36074
|
-
ok: false
|
|
36075
|
-
};
|
|
36076
|
-
}
|
|
36077
|
-
}
|
|
36078
|
-
function toNumber2(value) {
|
|
36079
|
-
return value.trim() === "" ? Number.NaN : Number(value);
|
|
36080
|
-
}
|
|
36081
|
-
function buildBrowserAction(type, flags) {
|
|
36082
|
-
const parsedPath = flags.path === undefined ? undefined : parseJsonPath(flags.path);
|
|
36083
|
-
if (parsedPath !== undefined && !parsedPath.ok)
|
|
36084
|
-
return parsedPath;
|
|
36085
|
-
const candidate = {
|
|
36086
|
-
type,
|
|
36087
|
-
...flags.button === undefined ? {} : { button: flags.button },
|
|
36088
|
-
...flags.keys === undefined ? {} : { keys: flags.keys },
|
|
36089
|
-
...parsedPath === undefined ? {} : { path: parsedPath.value },
|
|
36090
|
-
...flags.scrollX === undefined ? {} : { scroll_x: toNumber2(flags.scrollX) },
|
|
36091
|
-
...flags.scrollY === undefined ? {} : { scroll_y: toNumber2(flags.scrollY) },
|
|
36092
|
-
...flags.text === undefined ? {} : { text: flags.text },
|
|
36093
|
-
...flags.url === undefined ? {} : { url: flags.url },
|
|
36094
|
-
...flags.x === undefined ? {} : { x: toNumber2(flags.x) },
|
|
36095
|
-
...flags.y === undefined ? {} : { y: toNumber2(flags.y) }
|
|
36096
|
-
};
|
|
36097
|
-
return parseBrowserAction(candidate);
|
|
36098
|
-
}
|
|
36099
|
-
function parseBrowserAction(candidate) {
|
|
36100
|
-
const parsed = browserActionSchema.safeParse(candidate);
|
|
36101
|
-
if (!parsed.success) {
|
|
36102
|
-
return { error: prettifyError(parsed.error), ok: false };
|
|
36103
|
-
}
|
|
36104
|
-
return { action: parsed.data, ok: true };
|
|
36105
|
-
}
|
|
36106
|
-
|
|
36107
37306
|
// src/domains/interactiveRunner/readAction.ts
|
|
36108
|
-
var
|
|
37307
|
+
var stdinArgument3 = "-";
|
|
36109
37308
|
async function readAction(type, flags, deps) {
|
|
36110
|
-
if (type !==
|
|
37309
|
+
if (type !== stdinArgument3)
|
|
36111
37310
|
return buildBrowserAction(type, flags);
|
|
36112
37311
|
if (Object.values(flags).some((value) => value !== undefined)) {
|
|
36113
37312
|
return { error: interactiveRunnerMessages.actionFlagsWithStdin, ok: false };
|
|
@@ -37221,6 +38420,207 @@ function registerRunCommand(runner, signals) {
|
|
|
37221
38420
|
}, runnerDeps(ctx)))(opts, command));
|
|
37222
38421
|
}
|
|
37223
38422
|
|
|
38423
|
+
// src/core/messages/interactiveRunner/recording.ts
|
|
38424
|
+
var recordingMessages = {
|
|
38425
|
+
noRunnerIdForHistory: "Name the runner whose recordings to read with --runner <id> or QAWOLF_RUNNER_ID. History stays readable after the runner terminates.",
|
|
38426
|
+
unsupported: "Video recording requires a Playwright runner.",
|
|
38427
|
+
inProgress: "A recording is already in progress. Use runner record status to check its mode and id. Stop a manual recording by id, or wait for the run to finish an automatic recording.",
|
|
38428
|
+
notFound: "That recording was not found on the runner. Use runner record status for the active recording or runner list-recordings for published history.",
|
|
38429
|
+
idUsed: "That recording id has already been used. Start a new recording with a new UUID.",
|
|
38430
|
+
failed: "The recording operation failed. If this was a stop, retry it with the same recording id to finish stopping or publishing it. Check runner list-recordings for its published status.",
|
|
38431
|
+
failedCapture: "The recording failed. Check runner list-recordings for its published status.",
|
|
38432
|
+
recordingId: (id) => `Recording id: ${id}.`
|
|
38433
|
+
};
|
|
38434
|
+
|
|
38435
|
+
// src/domains/interactiveRunner/recordingOutput.ts
|
|
38436
|
+
function recordingFailure(reason) {
|
|
38437
|
+
switch (reason) {
|
|
38438
|
+
case "unsupported":
|
|
38439
|
+
return {
|
|
38440
|
+
error: recordingMessages.unsupported,
|
|
38441
|
+
exitCode: exitCodes.invalidArgs
|
|
38442
|
+
};
|
|
38443
|
+
case "screen-not-ready":
|
|
38444
|
+
return {
|
|
38445
|
+
error: interactiveRunnerMessages.screenNotReady,
|
|
38446
|
+
exitCode: exitCodes.network
|
|
38447
|
+
};
|
|
38448
|
+
case "recording-in-progress":
|
|
38449
|
+
return {
|
|
38450
|
+
error: recordingMessages.inProgress,
|
|
38451
|
+
exitCode: exitCodes.invalidArgs
|
|
38452
|
+
};
|
|
38453
|
+
case "recording-not-found":
|
|
38454
|
+
return {
|
|
38455
|
+
error: recordingMessages.notFound,
|
|
38456
|
+
exitCode: exitCodes.notFound
|
|
38457
|
+
};
|
|
38458
|
+
case "recording-id-used":
|
|
38459
|
+
return {
|
|
38460
|
+
error: recordingMessages.idUsed,
|
|
38461
|
+
exitCode: exitCodes.invalidArgs
|
|
38462
|
+
};
|
|
38463
|
+
case "runner-unreachable":
|
|
38464
|
+
return {
|
|
38465
|
+
error: interactiveRunnerMessages.runnerUnreachable,
|
|
38466
|
+
exitCode: exitCodes.network
|
|
38467
|
+
};
|
|
38468
|
+
case "recording-failed":
|
|
38469
|
+
return {
|
|
38470
|
+
error: recordingMessages.failed,
|
|
38471
|
+
exitCode: exitCodes.testFailure
|
|
38472
|
+
};
|
|
38473
|
+
}
|
|
38474
|
+
}
|
|
38475
|
+
function formatRecordingState(answer) {
|
|
38476
|
+
const { result, url } = answer;
|
|
38477
|
+
if (result.outcome === "failure")
|
|
38478
|
+
return recordingFailure(result.failureReason).error;
|
|
38479
|
+
const { active, auto } = result.state;
|
|
38480
|
+
const lines = [
|
|
38481
|
+
active ? `Recording ${active.id} (${active.mode}), started ${active.startedAt}.` : "No active recording.",
|
|
38482
|
+
`Automatic recording: ${auto}.`
|
|
38483
|
+
];
|
|
38484
|
+
if (result.recording) {
|
|
38485
|
+
lines.push(`Recording ${result.recording.id}: ${result.recording.status}.`);
|
|
38486
|
+
}
|
|
38487
|
+
return [...lines, url].join(`
|
|
38488
|
+
`);
|
|
38489
|
+
}
|
|
38490
|
+
function formatRecordings(answer) {
|
|
38491
|
+
const lines = answer.recordings.map((recording) => [
|
|
38492
|
+
`${recording.id} (${recording.mode}, ${recording.status})`,
|
|
38493
|
+
` ${recording.startedAt} to ${recording.endedAt}`,
|
|
38494
|
+
` ${recording.url}`,
|
|
38495
|
+
...recording.videoUrl ? [` Video: ${recording.videoUrl}`] : []
|
|
38496
|
+
].join(`
|
|
38497
|
+
`));
|
|
38498
|
+
if (lines.length === 0)
|
|
38499
|
+
lines.push("No published recordings found.");
|
|
38500
|
+
if (answer.nextPageToken !== undefined) {
|
|
38501
|
+
lines.push(`Next page token (pass with --page-token): ${answer.nextPageToken}`);
|
|
38502
|
+
}
|
|
38503
|
+
return lines.join(`
|
|
38504
|
+
`);
|
|
38505
|
+
}
|
|
38506
|
+
|
|
38507
|
+
// src/domains/interactiveRunner/recordingRequests.ts
|
|
38508
|
+
async function recordOnRunner(ctx, runner, command) {
|
|
38509
|
+
const contract = publicContractsV1.runner.record;
|
|
38510
|
+
const parsed = contract.input.safeParse({ id: runner.runnerId, command });
|
|
38511
|
+
if (!parsed.success) {
|
|
38512
|
+
return {
|
|
38513
|
+
ok: false,
|
|
38514
|
+
error: "Invalid recording request. Runner ids must be valid and recording ids must be UUIDs.",
|
|
38515
|
+
exitCode: exitCodes.invalidArgs
|
|
38516
|
+
};
|
|
38517
|
+
}
|
|
38518
|
+
const result = await ctx.platformClient.callPublicApi(contract, parsed.data, runnerCallOptions);
|
|
38519
|
+
return result.ok ? result : { ok: false, ...runnerRequestFailure(result, runner) };
|
|
38520
|
+
}
|
|
38521
|
+
async function readRunnerRecordings(ctx, runner, query) {
|
|
38522
|
+
const contract = publicContractsV1.runner.recordings;
|
|
38523
|
+
const parsed = contract.input.safeParse({ id: runner.runnerId, ...query });
|
|
38524
|
+
if (!parsed.success) {
|
|
38525
|
+
return {
|
|
38526
|
+
ok: false,
|
|
38527
|
+
error: "Invalid recordings request. Use a valid runner id, a UUID recording id, and a page token of at most 4096 characters.",
|
|
38528
|
+
exitCode: exitCodes.invalidArgs
|
|
38529
|
+
};
|
|
38530
|
+
}
|
|
38531
|
+
const result = await ctx.platformClient.callPublicApi(contract, parsed.data);
|
|
38532
|
+
return result.ok ? result : { ok: false, ...runnerRequestFailure(result, runner) };
|
|
38533
|
+
}
|
|
38534
|
+
|
|
38535
|
+
// src/domains/interactiveRunner/recording.ts
|
|
38536
|
+
async function handleRunnerRecord(ctx, options, deps) {
|
|
38537
|
+
const resolved = await resolveRunner(ctx, { autoLaunch: false, runner: options.runner }, deps);
|
|
38538
|
+
if (resolved.type === "failed")
|
|
38539
|
+
return { ...failureFields(resolved), exitCode: resolved.exitCode };
|
|
38540
|
+
const command = options.command.action === "start" ? {
|
|
38541
|
+
action: "start",
|
|
38542
|
+
recordingId: options.command.recordingId ?? deps.makeRecordingId()
|
|
38543
|
+
} : options.command;
|
|
38544
|
+
const response = await recordOnRunner(ctx, resolved, command);
|
|
38545
|
+
const idNote = "recordingId" in command ? recordingMessages.recordingId(command.recordingId) : undefined;
|
|
38546
|
+
if (!response.ok) {
|
|
38547
|
+
const fields = failureFields(response);
|
|
38548
|
+
return {
|
|
38549
|
+
...fields,
|
|
38550
|
+
...idNote ? { errorBody: [fields.errorBody, idNote].filter(Boolean).join(`
|
|
38551
|
+
`) } : {},
|
|
38552
|
+
exitCode: response.exitCode
|
|
38553
|
+
};
|
|
38554
|
+
}
|
|
38555
|
+
const answer = response.value;
|
|
38556
|
+
if (answer.result.outcome === "failure") {
|
|
38557
|
+
return {
|
|
38558
|
+
...recordingFailure(answer.result.failureReason),
|
|
38559
|
+
errorBody: [idNote, answer.url].filter(Boolean).join(`
|
|
38560
|
+
`)
|
|
38561
|
+
};
|
|
38562
|
+
}
|
|
38563
|
+
ctx.ui.output(answer, formatRecordingState(answer));
|
|
38564
|
+
if (answer.result.recording?.status === "failed") {
|
|
38565
|
+
return {
|
|
38566
|
+
error: recordingMessages.failedCapture,
|
|
38567
|
+
exitCode: exitCodes.testFailure,
|
|
38568
|
+
errorBody: [
|
|
38569
|
+
recordingMessages.recordingId(answer.result.recording.id),
|
|
38570
|
+
answer.url
|
|
38571
|
+
].join(`
|
|
38572
|
+
`)
|
|
38573
|
+
};
|
|
38574
|
+
}
|
|
38575
|
+
return;
|
|
38576
|
+
}
|
|
38577
|
+
async function handleRunnerRecordings(ctx, options, deps) {
|
|
38578
|
+
const resolved = await resolveRunner(ctx, {
|
|
38579
|
+
autoLaunch: false,
|
|
38580
|
+
noRunnerIdMessage: recordingMessages.noRunnerIdForHistory,
|
|
38581
|
+
runner: options.runner
|
|
38582
|
+
}, deps);
|
|
38583
|
+
if (resolved.type === "failed")
|
|
38584
|
+
return { ...failureFields(resolved), exitCode: resolved.exitCode };
|
|
38585
|
+
const response = await readRunnerRecordings(ctx, resolved, {
|
|
38586
|
+
...options.recordingId === undefined ? {} : { recordingId: options.recordingId },
|
|
38587
|
+
...options.pageToken === undefined ? {} : { pageToken: options.pageToken }
|
|
38588
|
+
});
|
|
38589
|
+
if (!response.ok)
|
|
38590
|
+
return { ...failureFields(response), exitCode: response.exitCode };
|
|
38591
|
+
ctx.ui.output(response.value, formatRecordings(response.value));
|
|
38592
|
+
return;
|
|
38593
|
+
}
|
|
38594
|
+
|
|
38595
|
+
// src/commands/runner/recording.register.ts
|
|
38596
|
+
function registerRunnerRecordingCommands(runner, signals) {
|
|
38597
|
+
const record = runner.command("record").description("Control video recording on a Playwright runner");
|
|
38598
|
+
declareCommandKind(record.command("start"), "write").description("Start manual video capture across runs. Requires a ready screen and suppresses automatic capture until stopped").option("--recording-id <uuid>", "Recording UUID. Generated when omitted; reuse it when retrying this start").option("--runner <id>", runnerFlagDescription).action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerRecord(ctx, {
|
|
38599
|
+
command: { action: "start", recordingId: opts.recordingId },
|
|
38600
|
+
runner: opts.runner
|
|
38601
|
+
}, runnerDeps(ctx)))(opts, command));
|
|
38602
|
+
declareCommandKind(record.command("stop <recording-id>"), "write").description("Stop the recording with this UUID and publish it. Retrying cannot stop a later recording").option("--runner <id>", runnerFlagDescription).action((recordingId, opts, command) => withAuthContext(signals, (ctx) => handleRunnerRecord(ctx, {
|
|
38603
|
+
command: { action: "stop", recordingId },
|
|
38604
|
+
runner: opts.runner
|
|
38605
|
+
}, runnerDeps(ctx)))(opts, command));
|
|
38606
|
+
declareCommandKind(record.command("status"), "read").description("Show the active recording and automatic recording setting").option("--runner <id>", runnerFlagDescription).action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerRecord(ctx, {
|
|
38607
|
+
command: { action: "status" },
|
|
38608
|
+
runner: opts.runner
|
|
38609
|
+
}, runnerDeps(ctx)))(opts, command));
|
|
38610
|
+
declareCommandKind(record.command("auto"), "write").addArgument(new Argument2("<setting>", "Automatic recording setting").choices([
|
|
38611
|
+
"on",
|
|
38612
|
+
"off"
|
|
38613
|
+
])).description("Enable or disable automatic recording for subsequent full runs").option("--runner <id>", runnerFlagDescription).action((setting, opts, command) => withAuthContext(signals, (ctx) => handleRunnerRecord(ctx, {
|
|
38614
|
+
command: { action: "auto", enabled: setting === "on" },
|
|
38615
|
+
runner: opts.runner
|
|
38616
|
+
}, runnerDeps(ctx)))(opts, command));
|
|
38617
|
+
declareCommandKind(runner.command("list-recordings"), "read").description("Read a page of published video recordings, including after the runner terminates. Platform URLs persist; video URLs expire").option("--runner <id>", runnerFlagDescription).option("--recording-id <uuid>", "Look up one recording; an empty result means it is not published or does not exist").option("--page-token <token>", "Continue from nextPageToken returned by the previous page").action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerRecordings(ctx, {
|
|
38618
|
+
runner: opts.runner,
|
|
38619
|
+
...opts.recordingId === undefined ? {} : { recordingId: opts.recordingId },
|
|
38620
|
+
...opts.pageToken === undefined ? {} : { pageToken: opts.pageToken }
|
|
38621
|
+
}, runnerDeps(ctx)))(opts, command));
|
|
38622
|
+
}
|
|
38623
|
+
|
|
37224
38624
|
// src/commands/runner/index.ts
|
|
37225
38625
|
function registerRunnerCommand(program, signals) {
|
|
37226
38626
|
const runner = program.command("runner").description("Drive an interactive runner on the QA Wolf platform");
|
|
@@ -37228,11 +38628,13 @@ function registerRunnerCommand(program, signals) {
|
|
|
37228
38628
|
registerRunCommand(runner, signals);
|
|
37229
38629
|
registerRunnerEventsCommand(runner, signals);
|
|
37230
38630
|
registerRunnerInteractCommands(runner, signals);
|
|
38631
|
+
registerRunnerActionsCommand(runner, signals);
|
|
37231
38632
|
registerRunnerExecCommand(runner, signals);
|
|
37232
38633
|
registerRunnerInspectCommands(runner, signals);
|
|
37233
38634
|
registerRunnerImportPackageCommand(runner, signals);
|
|
37234
38635
|
registerRunnerHighlightSelectorCommand(runner, signals);
|
|
37235
38636
|
registerRunnerPromoteSnapshotCommand(runner, signals);
|
|
38637
|
+
registerRunnerRecordingCommands(runner, signals);
|
|
37236
38638
|
}
|
|
37237
38639
|
|
|
37238
38640
|
// src/commands/program.ts
|
|
@@ -37263,4 +38665,4 @@ createProgram({ signals }).parseAsync().catch(() => {
|
|
|
37263
38665
|
process.exitCode = 1;
|
|
37264
38666
|
}).finally(() => exitWhenIdle(typeof process.exitCode === "number" ? process.exitCode : 0));
|
|
37265
38667
|
|
|
37266
|
-
//# debugId=
|
|
38668
|
+
//# debugId=5DFA383F9EE30BDB64756E2164756E21
|