@qawolf/cli 1.25.0 → 1.26.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/dist/cli.js +194 -109
- package/dist/runner-sdk.js +159 -76
- package/package.json +2 -2
- package/skills/qawolf-cli/SKILL.md +4 -2
package/dist/cli.js
CHANGED
|
@@ -9083,25 +9083,32 @@ function exit(code, message, proc = process) {
|
|
|
9083
9083
|
}
|
|
9084
9084
|
return proc.exit(code);
|
|
9085
9085
|
}
|
|
9086
|
-
|
|
9087
|
-
|
|
9086
|
+
var backstopMs = 2000;
|
|
9087
|
+
function exitWhenIdle(code, proc = process, scheduleBackstop = (fn) => {
|
|
9088
|
+
setTimeout(fn, backstopMs).unref();
|
|
9088
9089
|
}) {
|
|
9089
|
-
|
|
9090
|
-
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
|
|
9094
|
-
|
|
9095
|
-
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
|
|
9090
|
+
proc.exitCode = code;
|
|
9091
|
+
scheduleBackstop(() => proc.exit(code));
|
|
9092
|
+
}
|
|
9093
|
+
var signalExitCodes = {
|
|
9094
|
+
SIGINT: 130,
|
|
9095
|
+
SIGTERM: 143
|
|
9096
|
+
};
|
|
9097
|
+
var signalGraceMs = 150;
|
|
9098
|
+
function scheduleSignalGrace(fn) {
|
|
9099
|
+
return setTimeout(fn, signalGraceMs);
|
|
9100
|
+
}
|
|
9101
|
+
function createSignalExit(deps) {
|
|
9102
|
+
const proc = deps.proc ?? process;
|
|
9103
|
+
const scheduleGrace = deps.scheduleGrace ?? scheduleSignalGrace;
|
|
9104
|
+
let signalled = false;
|
|
9105
|
+
return (signal) => () => {
|
|
9106
|
+
const code = signalExitCodes[signal];
|
|
9107
|
+
if (signalled)
|
|
9108
|
+
proc.exit(code);
|
|
9109
|
+
signalled = true;
|
|
9110
|
+
deps.shutdown(signal).catch(() => {}).finally(() => exitWhenIdle(code, proc, scheduleGrace));
|
|
9101
9111
|
};
|
|
9102
|
-
proc.stdout.write("", onFlushed);
|
|
9103
|
-
proc.stderr.write("", onFlushed);
|
|
9104
|
-
scheduleBackstop(exitOnce);
|
|
9105
9112
|
}
|
|
9106
9113
|
|
|
9107
9114
|
// src/shell/fs.ts
|
|
@@ -16384,6 +16391,10 @@ var makeEmailResourceSchema = () => resource({
|
|
|
16384
16391
|
replyTo: array(namedAddressSchema).optional(),
|
|
16385
16392
|
text: string2().optional().describe("The plain text body.")
|
|
16386
16393
|
}, { urlFieldDescription: emailUrlFieldDescription });
|
|
16394
|
+
var makeEmailAddressResourceSchema = () => resource({
|
|
16395
|
+
address: string2().describe("The inbox address. Mail sent to any plus-suffixed form, such as inbox+abc@example.com, lands in the same inbox."),
|
|
16396
|
+
isDefault: boolean2().describe("Whether this is the workspace's default inbox address.")
|
|
16397
|
+
}, { urlFieldDescription: emailUrlFieldDescription });
|
|
16387
16398
|
|
|
16388
16399
|
// node_modules/@qawolf/api-contracts/dist/v1/identity/organization.js
|
|
16389
16400
|
var identityOrganization = object({
|
|
@@ -16607,6 +16618,8 @@ var runnerNameSchema = _enum([
|
|
|
16607
16618
|
"android",
|
|
16608
16619
|
"ios"
|
|
16609
16620
|
]);
|
|
16621
|
+
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.";
|
|
16622
|
+
var runnerWorkspaceIdDescription = `The workspace the runner belongs to. ${runnerWorkspaceRequirement}`;
|
|
16610
16623
|
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.");
|
|
16611
16624
|
var makeRunnerSchema = () => object({
|
|
16612
16625
|
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."),
|
|
@@ -16634,10 +16647,11 @@ var inspectRequestSchema = discriminatedUnion("what", [
|
|
|
16634
16647
|
what: literal("variable")
|
|
16635
16648
|
})
|
|
16636
16649
|
]);
|
|
16637
|
-
var makeInspectOnRunnerContract = () => {
|
|
16650
|
+
var makeInspectOnRunnerContract = (ids) => {
|
|
16638
16651
|
const input = object({
|
|
16639
16652
|
id: runnerIdSchema.describe("Id of the runner to inspect."),
|
|
16640
|
-
request: inspectRequestSchema
|
|
16653
|
+
request: inspectRequestSchema,
|
|
16654
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
16641
16655
|
});
|
|
16642
16656
|
const output = discriminatedUnion("outcome", [
|
|
16643
16657
|
object({
|
|
@@ -16784,10 +16798,11 @@ var inspectMobileAnswerSchema = discriminatedUnion("what", [
|
|
|
16784
16798
|
what: literal("elements")
|
|
16785
16799
|
})
|
|
16786
16800
|
]);
|
|
16787
|
-
var makeInspectMobileOnRunnerContract = () => {
|
|
16801
|
+
var makeInspectMobileOnRunnerContract = (ids) => {
|
|
16788
16802
|
const input = object({
|
|
16789
16803
|
id: runnerIdSchema.describe("Id of the runner to inspect."),
|
|
16790
|
-
request: inspectMobileRequestSchema
|
|
16804
|
+
request: inspectMobileRequestSchema,
|
|
16805
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
16791
16806
|
});
|
|
16792
16807
|
const output = discriminatedUnion("outcome", [
|
|
16793
16808
|
inspectMobileAnswerSchema,
|
|
@@ -16871,20 +16886,26 @@ var screenFailureReasons = [
|
|
|
16871
16886
|
var screenNotReadyDescription = "`screen-not-ready` if the runner has a screen that cannot serve this instant. Retry in a second or two: the desktop restarts when a run changes the display size, and it serves one see-or-act request at a time, so a screenshot or action already in flight is the usual reason. Do not submit a run to clear this — a run may restart the display and discard what is on it.";
|
|
16872
16887
|
var screenNeedsARunDescription = "`screen-needs-a-run` if the runner's virtual desktop has never started. Waiting will not change this and retrying is pointless — call `runner.runFlow` with a flow that opens a browser, then ask for the screen again. On a runner image with a browser that has never run anything, any `runner.performAction` also starts the browser itself. Evaluating a snippet does not start the desktop.";
|
|
16873
16888
|
var runnerHasNoScreenDescription = "`runner-has-no-screen` if this runner is not one that runs a browser on a virtual desktop. Nothing about it can be seen or driven, and retrying will never help — launch a `playwright` runner instead.";
|
|
16889
|
+
var screenshotImageSchema = string2().min(1).describe("The screenshot, as a base64-encoded JPEG. Decode it and write the bytes to a `.jpg` file — writing this string to the file leaves you with base64 text rather than an image.");
|
|
16874
16890
|
|
|
16875
16891
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/performAction.js
|
|
16876
16892
|
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.";
|
|
16877
16893
|
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.";
|
|
16894
|
+
var withScreenshotDescription = "With `withScreenshot: true`, the answer also carries `imageJpegBase64`: a screenshot taken after the action, once the screen has changed from just before it or half a second has passed, whichever comes first. One call instead of `runner.performAction` followed by `runner.takeScreenshot`, with no fixed wait in between. Anything animating on the page counts as a change. 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.";
|
|
16878
16895
|
var maxActionErrorMessageLength = 1000;
|
|
16879
|
-
var makePerformActionOnRunnerContract = () => {
|
|
16896
|
+
var makePerformActionOnRunnerContract = (ids) => {
|
|
16880
16897
|
const input = object({
|
|
16881
16898
|
action: browserActionSchema.describe("The action to perform. Coordinates are pixels on the screenshot's own coordinate space."),
|
|
16882
|
-
id: runnerIdSchema.describe("Id of the runner to act on.")
|
|
16899
|
+
id: runnerIdSchema.describe("Id of the runner to act on."),
|
|
16900
|
+
withScreenshot: boolean2().optional().describe("Also answer with a screenshot taken after the action, once the screen has changed or half a second has passed. Saves the separate `runner.takeScreenshot` call and its fixed wait."),
|
|
16901
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
16883
16902
|
});
|
|
16903
|
+
const screenshotAfterAction = screenshotImageSchema.optional();
|
|
16884
16904
|
const failure = discriminatedUnion("failureReason", [
|
|
16885
16905
|
object({
|
|
16886
16906
|
errorMessage: string2().max(maxActionErrorMessageLength).describe("What stopped the action from taking effect."),
|
|
16887
16907
|
failureReason: literal("action-failed"),
|
|
16908
|
+
imageJpegBase64: screenshotAfterAction,
|
|
16888
16909
|
outcome: literal("failure")
|
|
16889
16910
|
}),
|
|
16890
16911
|
makeRunnerFailureSchema([
|
|
@@ -16894,11 +16915,14 @@ var makePerformActionOnRunnerContract = () => {
|
|
|
16894
16915
|
])
|
|
16895
16916
|
]);
|
|
16896
16917
|
const output = discriminatedUnion("outcome", [
|
|
16897
|
-
object({
|
|
16918
|
+
object({
|
|
16919
|
+
imageJpegBase64: screenshotAfterAction,
|
|
16920
|
+
outcome: literal("success")
|
|
16921
|
+
}),
|
|
16898
16922
|
failure
|
|
16899
16923
|
]);
|
|
16900
16924
|
return {
|
|
16901
|
-
description: `Perform one raw browser action on an interactive runner: click, double_click, move, drag, scroll, keypress, type, or navigate. Coordinates are whole pixels on the runner's virtual desktop, in the same space as \`runner.takeScreenshot\`. One action per request, and the runner serves one at a time. The action shapes follow the computer-use vocabulary, minus \`screenshot\` (use \`runner.takeScreenshot\`) and \`wait\` (delay on the caller's side). 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} A \`navigate\` does not go through the screen, so a screen that is not ready does not stop it. ${runnerHasNoScreenDescription} ${notSupportedOnMobileDescription} ${unreachableDescription}`,
|
|
16925
|
+
description: `Perform one raw browser action on an interactive runner: click, double_click, move, drag, scroll, keypress, type, or navigate. Coordinates are whole pixels on the runner's virtual desktop, in the same space as \`runner.takeScreenshot\`. One action per request, and the runner serves one at a time. The action shapes follow the computer-use vocabulary, minus \`screenshot\` (use \`runner.takeScreenshot\`) and \`wait\` (delay on the caller's side). 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} A \`navigate\` does not go through the screen, so a screen that is not ready does not stop it. ${runnerHasNoScreenDescription} ${notSupportedOnMobileDescription} ${withScreenshotDescription} ${unreachableDescription}`,
|
|
16902
16926
|
input,
|
|
16903
16927
|
kind: "write",
|
|
16904
16928
|
name: "runner.performAction",
|
|
@@ -16924,9 +16948,11 @@ var makeAgentGetContract = (ids) => {
|
|
|
16924
16948
|
replies: array(agentReplySchema).describe("Everything the AI has said so far, oldest first. A reply carrying choices is a question the work is blocked on."),
|
|
16925
16949
|
sessionId: ids.chatSession,
|
|
16926
16950
|
status: agentSessionStatusSchema
|
|
16927
|
-
}, {
|
|
16951
|
+
}, {
|
|
16952
|
+
urlFieldDescription: "Absolute URL of the live session in the QA Wolf app."
|
|
16953
|
+
});
|
|
16928
16954
|
return {
|
|
16929
|
-
description: '
|
|
16955
|
+
description: 'Monitor a QA Wolf AI session by reading its status and replies. After agent.send, share the returned session URL before monitoring. Wait 30 to 60 seconds between checks; do not call this in a tight loop. Replies accumulate, so compare them with what you have already seen. Continue monitoring silently when the status and replies are unchanged; 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.',
|
|
16930
16956
|
input,
|
|
16931
16957
|
kind: "read",
|
|
16932
16958
|
name: "agent.get",
|
|
@@ -16945,9 +16971,11 @@ var makeAgentSendContract = (ids) => {
|
|
|
16945
16971
|
const output = resource({
|
|
16946
16972
|
sessionId: ids.chatSession.describe("Follow the work by passing this to agent.get."),
|
|
16947
16973
|
status: agentSessionStatusSchema
|
|
16948
|
-
}, {
|
|
16974
|
+
}, {
|
|
16975
|
+
urlFieldDescription: "Absolute URL of the live session in the QA Wolf app."
|
|
16976
|
+
});
|
|
16949
16977
|
return {
|
|
16950
|
-
description: "
|
|
16978
|
+
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, make the next action a normal user-visible assistant message containing the exact returned url, before any tool call or wait. 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.",
|
|
16951
16979
|
input,
|
|
16952
16980
|
kind: "write",
|
|
16953
16981
|
name: "agent.send",
|
|
@@ -17050,10 +17078,7 @@ var makeListEmailAddressesContract = (ids) => {
|
|
|
17050
17078
|
...makePaginationInputFields({ defaultLimit: 20, maxLimit: 50 })
|
|
17051
17079
|
});
|
|
17052
17080
|
const output = object({
|
|
17053
|
-
addresses: array(
|
|
17054
|
-
address: string2().describe("The inbox address. Mail sent to any plus-suffixed form, such as inbox+abc@example.com, lands in the same inbox."),
|
|
17055
|
-
isDefault: boolean2().describe("Whether this is the workspace's default inbox address.")
|
|
17056
|
-
}, { urlFieldDescription: emailUrlFieldDescription })).describe("The workspace's permanent inbox addresses, alphabetical."),
|
|
17081
|
+
addresses: array(makeEmailAddressResourceSchema()).describe("The workspace's permanent inbox addresses, alphabetical."),
|
|
17057
17082
|
nextCursor: nextCursorSchema
|
|
17058
17083
|
});
|
|
17059
17084
|
return {
|
|
@@ -17065,6 +17090,28 @@ var makeListEmailAddressesContract = (ids) => {
|
|
|
17065
17090
|
};
|
|
17066
17091
|
};
|
|
17067
17092
|
|
|
17093
|
+
// node_modules/@qawolf/api-contracts/dist/v1/email/registerAddress.js
|
|
17094
|
+
var hasNoPlusSuffix = (address) => {
|
|
17095
|
+
const [localPart] = address.split("@");
|
|
17096
|
+
return localPart !== undefined && !localPart.includes("+");
|
|
17097
|
+
};
|
|
17098
|
+
var makeRegisterEmailAddressContract = (ids) => {
|
|
17099
|
+
const input = object({
|
|
17100
|
+
address: email2().refine(hasNoPlusSuffix, {
|
|
17101
|
+
message: "Register the base address. Mail to a plus-suffixed form lands in the base inbox."
|
|
17102
|
+
}).describe("The inbox address to register. Its domain must be one QA Wolf serves for the workspace."),
|
|
17103
|
+
workspaceId: ids.workspace.optional().describe("The workspace to register the address in. Required when authenticating with an organization or user API key.")
|
|
17104
|
+
});
|
|
17105
|
+
const output = makeEmailAddressResourceSchema();
|
|
17106
|
+
return {
|
|
17107
|
+
description: "Register an inbox address for the workspace. Registering an address the workspace already has changes nothing. A refusal names the domains the workspace can use.",
|
|
17108
|
+
input,
|
|
17109
|
+
kind: "write",
|
|
17110
|
+
name: "email.registerAddress",
|
|
17111
|
+
output
|
|
17112
|
+
};
|
|
17113
|
+
};
|
|
17114
|
+
|
|
17068
17115
|
// node_modules/@qawolf/api-contracts/dist/v1/email/send.js
|
|
17069
17116
|
var recipientsSchema = array(email2()).max(50);
|
|
17070
17117
|
var maxAttachments = 10;
|
|
@@ -17705,14 +17752,31 @@ var makeReattemptRunContract = (ids) => {
|
|
|
17705
17752
|
};
|
|
17706
17753
|
};
|
|
17707
17754
|
|
|
17755
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/stop.js
|
|
17756
|
+
var makeStopRunContract = (ids) => {
|
|
17757
|
+
const input = object({ runId: ids.run });
|
|
17758
|
+
const output = resource({
|
|
17759
|
+
runId: ids.run,
|
|
17760
|
+
status: _enum(["accepted", "already-finished", "execution-not-found"]).describe('"accepted" means stopping was requested; poll run.get for the final result. "already-finished" means the run has already finished and its result is unchanged. "execution-not-found" means the run exists but no execution was found to stop; it may still be starting or may no longer be available. No stop request was accepted in that case.')
|
|
17761
|
+
}, { urlFieldDescription: "Absolute URL of the run page." });
|
|
17762
|
+
return {
|
|
17763
|
+
description: "Stop a run, including its queued flows and automatic retries. Stopping is asynchronous. Repeated requests are safe, and finished runs keep their results. A run that is still being created returns not found; retry once run.get returns the run. If run.get returns a different runId, use that ID. Poll run.get for results.",
|
|
17764
|
+
input,
|
|
17765
|
+
kind: "write",
|
|
17766
|
+
name: "run.stop",
|
|
17767
|
+
output
|
|
17768
|
+
};
|
|
17769
|
+
};
|
|
17770
|
+
|
|
17708
17771
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/evaluateSnippet.js
|
|
17709
17772
|
var maxSnippetCodeLength = 64 * 1024;
|
|
17710
|
-
var makeEvaluateSnippetOnRunnerContract = () => {
|
|
17773
|
+
var makeEvaluateSnippetOnRunnerContract = (ids) => {
|
|
17711
17774
|
const input = object({
|
|
17712
17775
|
code: string2().min(1).max(maxSnippetCodeLength).describe("The code to evaluate against the runner's live page, in the scope of `filePath` when one is given."),
|
|
17713
17776
|
filePath: runFilePathSchema.optional().describe("Path of the file the snippet is evaluated in the scope of. Omit to evaluate a snippet that imports nothing of yours."),
|
|
17714
17777
|
files: runFilesSchema.optional().describe("The file named by `filePath` and everything it imports. A runner holds no copy of your project, so a snippet's scope has to travel with it."),
|
|
17715
|
-
id: runnerIdSchema.describe("Id of the runner to evaluate the snippet on.")
|
|
17778
|
+
id: runnerIdSchema.describe("Id of the runner to evaluate the snippet on."),
|
|
17779
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
17716
17780
|
}).refine((request) => request.filePath === undefined || Object.hasOwn(request.files ?? {}, request.filePath), {
|
|
17717
17781
|
error: "A snippet naming a file must carry that file in files.",
|
|
17718
17782
|
path: ["files"]
|
|
@@ -17738,9 +17802,10 @@ var makeEvaluateSnippetOnRunnerContract = () => {
|
|
|
17738
17802
|
};
|
|
17739
17803
|
|
|
17740
17804
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/get.js
|
|
17741
|
-
var makeGetRunnerContract = () => {
|
|
17805
|
+
var makeGetRunnerContract = (ids) => {
|
|
17742
17806
|
const input = object({
|
|
17743
|
-
id: runnerIdSchema.describe("Id of the runner to look up.")
|
|
17807
|
+
id: runnerIdSchema.describe("Id of the runner to look up."),
|
|
17808
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
17744
17809
|
});
|
|
17745
17810
|
const output = object({
|
|
17746
17811
|
id: runnerIdSchema.describe("Id of the runner the call addressed."),
|
|
@@ -17758,10 +17823,11 @@ var makeGetRunnerContract = () => {
|
|
|
17758
17823
|
|
|
17759
17824
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/highlightSelector.js
|
|
17760
17825
|
var maxHighlightSelectorLength = 2000;
|
|
17761
|
-
var makeHighlightSelectorOnRunnerContract = () => {
|
|
17826
|
+
var makeHighlightSelectorOnRunnerContract = (ids) => {
|
|
17762
17827
|
const input = object({
|
|
17763
17828
|
id: runnerIdSchema.describe("Id of the runner to highlight on."),
|
|
17764
|
-
selector: string2().max(maxHighlightSelectorLength).describe("The selector to highlight. An empty string clears whatever is highlighted.")
|
|
17829
|
+
selector: string2().max(maxHighlightSelectorLength).describe("The selector to highlight. An empty string clears whatever is highlighted."),
|
|
17830
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
17765
17831
|
});
|
|
17766
17832
|
const output = discriminatedUnion("outcome", [
|
|
17767
17833
|
object({
|
|
@@ -17796,12 +17862,13 @@ var makeHighlightSelectorOnRunnerContract = () => {
|
|
|
17796
17862
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/importPackage.js
|
|
17797
17863
|
var maxPackageNameLength = 214;
|
|
17798
17864
|
var maxPackageVersionLength = 256;
|
|
17799
|
-
var makeImportPackageOnRunnerContract = () => {
|
|
17865
|
+
var makeImportPackageOnRunnerContract = (ids) => {
|
|
17800
17866
|
const input = object({
|
|
17801
17867
|
id: runnerIdSchema.describe("Id of the runner to install into."),
|
|
17802
17868
|
npmDependencies: record(string2(), string2()).describe("The run's current npm dependencies, so the install resolves against them."),
|
|
17803
17869
|
packageName: string2().min(1).max(maxPackageNameLength),
|
|
17804
|
-
packageVersion: string2().min(1).max(maxPackageVersionLength)
|
|
17870
|
+
packageVersion: string2().min(1).max(maxPackageVersionLength),
|
|
17871
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
17805
17872
|
});
|
|
17806
17873
|
const output = discriminatedUnion("outcome", [
|
|
17807
17874
|
object({ outcome: literal("success") }),
|
|
@@ -17821,10 +17888,11 @@ var makeImportPackageOnRunnerContract = () => {
|
|
|
17821
17888
|
};
|
|
17822
17889
|
|
|
17823
17890
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/index.js
|
|
17824
|
-
var makeLaunchRunnerContract = () => {
|
|
17891
|
+
var makeLaunchRunnerContract = (ids) => {
|
|
17825
17892
|
const input = object({
|
|
17826
17893
|
id: runnerIdSchema.describe("Id to launch the runner under, chosen by the caller. Launching the same id again returns the runner already running under it."),
|
|
17827
|
-
runnerName: runnerNameSchema.optional().describe("The runner family to launch. Defaults to the standard web family, playwright.")
|
|
17894
|
+
runnerName: runnerNameSchema.optional().describe("The runner family to launch. Defaults to the standard web family, playwright."),
|
|
17895
|
+
workspaceId: ids.workspace.optional().describe(`The workspace to launch the runner in. ${runnerWorkspaceRequirement}`)
|
|
17828
17896
|
});
|
|
17829
17897
|
const output = makeRunnerSchema().extend({
|
|
17830
17898
|
alreadyRunning: boolean2().describe("True when a runner was already running under this id and was returned unchanged, so no second runner was started and nothing new is billed. Not an error either way."),
|
|
@@ -17838,9 +17906,10 @@ var makeLaunchRunnerContract = () => {
|
|
|
17838
17906
|
output
|
|
17839
17907
|
};
|
|
17840
17908
|
};
|
|
17841
|
-
var makeTerminateRunnerContract = () => {
|
|
17909
|
+
var makeTerminateRunnerContract = (ids) => {
|
|
17842
17910
|
const input = object({
|
|
17843
|
-
id: runnerIdSchema.describe("Id of the runner to terminate.")
|
|
17911
|
+
id: runnerIdSchema.describe("Id of the runner to terminate."),
|
|
17912
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
17844
17913
|
});
|
|
17845
17914
|
const output = object({
|
|
17846
17915
|
id: runnerIdSchema.describe("Id of the runner the call addressed."),
|
|
@@ -17857,14 +17926,16 @@ var makeTerminateRunnerContract = () => {
|
|
|
17857
17926
|
};
|
|
17858
17927
|
|
|
17859
17928
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/list.js
|
|
17860
|
-
var makeListRunnersContract = () => {
|
|
17861
|
-
const input = object({
|
|
17929
|
+
var makeListRunnersContract = (ids) => {
|
|
17930
|
+
const input = object({
|
|
17931
|
+
workspaceId: ids.workspace.optional().describe(`The workspace whose runners to list. ${runnerWorkspaceRequirement}`)
|
|
17932
|
+
});
|
|
17862
17933
|
const output = object({
|
|
17863
17934
|
outcome: literal("success"),
|
|
17864
17935
|
runners: array(makeRunnerSchema()).describe("The runners running right now, in no particular order. Each entry carries the same fields a launch reports, so any of them can be addressed like a runner you launched yourself.")
|
|
17865
17936
|
});
|
|
17866
17937
|
return {
|
|
17867
|
-
description: "List the runners running on the caller's team right now.
|
|
17938
|
+
description: "List the runners running on the caller's team right now. A team API key already names the team and sends nothing; a credential bound to no single workspace names one in `workspaceId`. A runner that was terminated, or that terminated on its own after inactivity, is not listed, because the run system keeps no record of a runner once it is gone. 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. It is how to find a runner launched from another machine or in an earlier session, when the id it was launched under is no longer at hand.",
|
|
17868
17939
|
input,
|
|
17869
17940
|
kind: "read",
|
|
17870
17941
|
name: "runner.list",
|
|
@@ -17874,11 +17945,12 @@ var makeListRunnersContract = () => {
|
|
|
17874
17945
|
|
|
17875
17946
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/promoteSnapshot.js
|
|
17876
17947
|
var maxSnapshotPathLength = 1024;
|
|
17877
|
-
var makePromoteSnapshotOnRunnerContract = () => {
|
|
17948
|
+
var makePromoteSnapshotOnRunnerContract = (ids) => {
|
|
17878
17949
|
const input = object({
|
|
17879
17950
|
baselinePath: string2().min(1).max(maxSnapshotPathLength).describe("The baseline to replace, as named in the image diff."),
|
|
17880
17951
|
id: runnerIdSchema.describe("Id of the runner holding the screenshot."),
|
|
17881
|
-
screenshotPath: string2().min(1).max(maxSnapshotPathLength).describe("The screenshot to promote, as named in the image diff.")
|
|
17952
|
+
screenshotPath: string2().min(1).max(maxSnapshotPathLength).describe("The screenshot to promote, as named in the image diff."),
|
|
17953
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
17882
17954
|
});
|
|
17883
17955
|
const output = discriminatedUnion("outcome", [
|
|
17884
17956
|
object({ outcome: literal("success") }),
|
|
@@ -17901,10 +17973,11 @@ var makePromoteSnapshotOnRunnerContract = () => {
|
|
|
17901
17973
|
};
|
|
17902
17974
|
|
|
17903
17975
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/readJournal.js
|
|
17904
|
-
var makeReadRunnerJournalContract = () => {
|
|
17976
|
+
var makeReadRunnerJournalContract = (ids) => {
|
|
17905
17977
|
const input = readJournalRequestSchema.extend({
|
|
17906
17978
|
id: runnerIdSchema.describe("Id of the runner to read the journal of."),
|
|
17907
|
-
stream: journalStreamSchema.describe(`The stream to read. QA Wolf writes ${knownJournalStreams.join(", ")}; an unwritten stream reads as empty rather than as an error.`)
|
|
17979
|
+
stream: journalStreamSchema.describe(`The stream to read. QA Wolf writes ${knownJournalStreams.join(", ")}; an unwritten stream reads as empty rather than as an error.`),
|
|
17980
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
17908
17981
|
});
|
|
17909
17982
|
const output = discriminatedUnion("outcome", [
|
|
17910
17983
|
readJournalResponseSchema.extend({ outcome: literal("success") }),
|
|
@@ -17939,7 +18012,8 @@ var makeRunFlowOnRunnerContract = (ids) => {
|
|
|
17939
18012
|
files: runFilesSchema.describe("Every file the run needs keyed by its path — the flow file, everything it imports, package.json and tsconfig.json. A runner holds no copy of your project, so what runs is exactly what is sent here."),
|
|
17940
18013
|
id: runnerIdSchema.describe("Id of the runner to run the flow on."),
|
|
17941
18014
|
selection: runSelectionSchema.optional().describe("Run only these lines, inside the page the runner is already on, instead of running the whole flow from a fresh browser. Omit to run the whole entry point."),
|
|
17942
|
-
unchangedFiles: unchangedFilesSchema.optional().describe("Files this runner already holds from an earlier run, keyed by path with a hex SHA-256 of the content it should hold. Send this to make `files` only what changed. The runner refuses with `needs-full-sync` if it holds none of a referenced path, so it never runs something other than what was asked for. Omit it to send every file.")
|
|
18015
|
+
unchangedFiles: unchangedFilesSchema.optional().describe("Files this runner already holds from an earlier run, keyed by path with a hex SHA-256 of the content it should hold. Send this to make `files` only what changed. The runner refuses with `needs-full-sync` if it holds none of a referenced path, so it never runs something other than what was asked for. Omit it to send every file."),
|
|
18016
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
17943
18017
|
}).refine((request) => request.env === undefined || request.environmentId === undefined, {
|
|
17944
18018
|
error: "Send env or environmentId, not both. An environment's variables are taken as they are stored, so there is no order in which the two would combine.",
|
|
17945
18019
|
path: ["environmentId"]
|
|
@@ -17991,9 +18065,10 @@ var makeRunFlowOnRunnerContract = (ids) => {
|
|
|
17991
18065
|
};
|
|
17992
18066
|
|
|
17993
18067
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/stopRun.js
|
|
17994
|
-
var makeStopRunOnRunnerContract = () => {
|
|
18068
|
+
var makeStopRunOnRunnerContract = (ids) => {
|
|
17995
18069
|
const input = object({
|
|
17996
|
-
id: runnerIdSchema.describe("Id of the runner to stop the run on.")
|
|
18070
|
+
id: runnerIdSchema.describe("Id of the runner to stop the run on."),
|
|
18071
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
17997
18072
|
});
|
|
17998
18073
|
const output = discriminatedUnion("outcome", [
|
|
17999
18074
|
object({
|
|
@@ -18012,13 +18087,14 @@ var makeStopRunOnRunnerContract = () => {
|
|
|
18012
18087
|
};
|
|
18013
18088
|
|
|
18014
18089
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/takeScreenshot.js
|
|
18015
|
-
var makeTakeScreenshotOnRunnerContract = () => {
|
|
18090
|
+
var makeTakeScreenshotOnRunnerContract = (ids) => {
|
|
18016
18091
|
const input = object({
|
|
18017
|
-
id: runnerIdSchema.describe("Id of the runner to take a screenshot of.")
|
|
18092
|
+
id: runnerIdSchema.describe("Id of the runner to take a screenshot of."),
|
|
18093
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
18018
18094
|
});
|
|
18019
18095
|
const output = discriminatedUnion("outcome", [
|
|
18020
18096
|
object({
|
|
18021
|
-
imageJpegBase64:
|
|
18097
|
+
imageJpegBase64: screenshotImageSchema,
|
|
18022
18098
|
outcome: literal("success")
|
|
18023
18099
|
}),
|
|
18024
18100
|
makeRunnerFailureSchema([
|
|
@@ -18072,6 +18148,7 @@ var makeContractsV1 = (ids) => {
|
|
|
18072
18148
|
get: makeGetEmailContract(resolvedIds),
|
|
18073
18149
|
getAttachment: makeGetEmailAttachmentContract(resolvedIds),
|
|
18074
18150
|
listAddresses: makeListEmailAddressesContract(resolvedIds),
|
|
18151
|
+
registerAddress: makeRegisterEmailAddressContract(resolvedIds),
|
|
18075
18152
|
send: makeSendEmailContract(resolvedIds)
|
|
18076
18153
|
},
|
|
18077
18154
|
environment: {
|
|
@@ -18103,24 +18180,25 @@ var makeContractsV1 = (ids) => {
|
|
|
18103
18180
|
diagnose: makeDiagnoseRunContract(resolvedIds),
|
|
18104
18181
|
find: makeFindRunsContract(resolvedIds),
|
|
18105
18182
|
get: makeGetRunContract(resolvedIds),
|
|
18106
|
-
reattempt: makeReattemptRunContract(resolvedIds)
|
|
18183
|
+
reattempt: makeReattemptRunContract(resolvedIds),
|
|
18184
|
+
stop: makeStopRunContract(resolvedIds)
|
|
18107
18185
|
},
|
|
18108
18186
|
runner: {
|
|
18109
|
-
evaluateSnippet: makeEvaluateSnippetOnRunnerContract(),
|
|
18110
|
-
get: makeGetRunnerContract(),
|
|
18111
|
-
highlightSelector: makeHighlightSelectorOnRunnerContract(),
|
|
18112
|
-
importPackage: makeImportPackageOnRunnerContract(),
|
|
18113
|
-
inspect: makeInspectOnRunnerContract(),
|
|
18114
|
-
inspectMobile: makeInspectMobileOnRunnerContract(),
|
|
18115
|
-
launch: makeLaunchRunnerContract(),
|
|
18116
|
-
list: makeListRunnersContract(),
|
|
18117
|
-
performAction: makePerformActionOnRunnerContract(),
|
|
18118
|
-
promoteSnapshot: makePromoteSnapshotOnRunnerContract(),
|
|
18119
|
-
readJournal: makeReadRunnerJournalContract(),
|
|
18187
|
+
evaluateSnippet: makeEvaluateSnippetOnRunnerContract(resolvedIds),
|
|
18188
|
+
get: makeGetRunnerContract(resolvedIds),
|
|
18189
|
+
highlightSelector: makeHighlightSelectorOnRunnerContract(resolvedIds),
|
|
18190
|
+
importPackage: makeImportPackageOnRunnerContract(resolvedIds),
|
|
18191
|
+
inspect: makeInspectOnRunnerContract(resolvedIds),
|
|
18192
|
+
inspectMobile: makeInspectMobileOnRunnerContract(resolvedIds),
|
|
18193
|
+
launch: makeLaunchRunnerContract(resolvedIds),
|
|
18194
|
+
list: makeListRunnersContract(resolvedIds),
|
|
18195
|
+
performAction: makePerformActionOnRunnerContract(resolvedIds),
|
|
18196
|
+
promoteSnapshot: makePromoteSnapshotOnRunnerContract(resolvedIds),
|
|
18197
|
+
readJournal: makeReadRunnerJournalContract(resolvedIds),
|
|
18120
18198
|
runFlow: makeRunFlowOnRunnerContract(resolvedIds),
|
|
18121
|
-
stopRun: makeStopRunOnRunnerContract(),
|
|
18122
|
-
takeScreenshot: makeTakeScreenshotOnRunnerContract(),
|
|
18123
|
-
terminate: makeTerminateRunnerContract()
|
|
18199
|
+
stopRun: makeStopRunOnRunnerContract(resolvedIds),
|
|
18200
|
+
takeScreenshot: makeTakeScreenshotOnRunnerContract(resolvedIds),
|
|
18201
|
+
terminate: makeTerminateRunnerContract(resolvedIds)
|
|
18124
18202
|
},
|
|
18125
18203
|
tag: {
|
|
18126
18204
|
create: makeCreateTagContract(resolvedIds),
|
|
@@ -18523,6 +18601,34 @@ async function writeTeamStorageAssets(args, deps) {
|
|
|
18523
18601
|
}
|
|
18524
18602
|
}
|
|
18525
18603
|
|
|
18604
|
+
// src/shell/platform/teamStorageMethods.ts
|
|
18605
|
+
function createTeamStorageMethods(trpc, deps, fs, getIdentity) {
|
|
18606
|
+
async function list() {
|
|
18607
|
+
if (deps.workspaceId !== undefined) {
|
|
18608
|
+
return listTeamStorageFiles(trpc, { teamId: deps.workspaceId }, deps);
|
|
18609
|
+
}
|
|
18610
|
+
const identity = await getIdentity();
|
|
18611
|
+
if (!identity.ok)
|
|
18612
|
+
return identity;
|
|
18613
|
+
if (!("team" in identity.value)) {
|
|
18614
|
+
return {
|
|
18615
|
+
ok: false,
|
|
18616
|
+
error: flowsMessages.pull.teamStorageRequiresTeamKey
|
|
18617
|
+
};
|
|
18618
|
+
}
|
|
18619
|
+
return listTeamStorageFiles(trpc, { teamId: identity.value.team.id }, deps);
|
|
18620
|
+
}
|
|
18621
|
+
return {
|
|
18622
|
+
listTeamStorageFiles: list,
|
|
18623
|
+
async syncTeamStorageAssets(assetsAbs, opts) {
|
|
18624
|
+
const files = await list();
|
|
18625
|
+
if (!files.ok)
|
|
18626
|
+
return files;
|
|
18627
|
+
return downloadTeamStorageAssets({ assetsAbs, files: files.value }, { fetch: deps.fetch, fs, onProgress: opts?.onProgress });
|
|
18628
|
+
}
|
|
18629
|
+
};
|
|
18630
|
+
}
|
|
18631
|
+
|
|
18526
18632
|
// src/shell/platform/createPlatformClient.ts
|
|
18527
18633
|
var requestBackoffMs2 = [500, 1500];
|
|
18528
18634
|
function createPlatformClient(apiKey, deps) {
|
|
@@ -18539,8 +18645,10 @@ function createPlatformClient(apiKey, deps) {
|
|
|
18539
18645
|
return result;
|
|
18540
18646
|
return { ok: true, value: { signedUrl: result.value.url } };
|
|
18541
18647
|
}
|
|
18648
|
+
const identityMethods = createIdentityMethods(apiKey, deps, requestBackoffMs2);
|
|
18542
18649
|
return {
|
|
18543
|
-
...
|
|
18650
|
+
...identityMethods,
|
|
18651
|
+
...createTeamStorageMethods(trpc, deps, fs, identityMethods.getIdentity),
|
|
18544
18652
|
getFlowsBundleUrl: getFlowsBundleUrlImpl,
|
|
18545
18653
|
callPublicApi: makeCallPublicApiMethod(trpc, deps, requestBackoffMs2),
|
|
18546
18654
|
async getEnvVars(envId) {
|
|
@@ -18554,24 +18662,6 @@ function createPlatformClient(apiKey, deps) {
|
|
|
18554
18662
|
return result;
|
|
18555
18663
|
return { ok: true, value: result.value.environmentVariables };
|
|
18556
18664
|
},
|
|
18557
|
-
async listTeamStorageFiles() {
|
|
18558
|
-
const identity = await this.getIdentity();
|
|
18559
|
-
if (!identity.ok)
|
|
18560
|
-
return identity;
|
|
18561
|
-
if (!("team" in identity.value)) {
|
|
18562
|
-
return {
|
|
18563
|
-
ok: false,
|
|
18564
|
-
error: flowsMessages.pull.teamStorageRequiresTeamKey
|
|
18565
|
-
};
|
|
18566
|
-
}
|
|
18567
|
-
return listTeamStorageFiles(trpc, { teamId: identity.value.team.id }, deps);
|
|
18568
|
-
},
|
|
18569
|
-
async syncTeamStorageAssets(assetsAbs, opts) {
|
|
18570
|
-
const files = await this.listTeamStorageFiles();
|
|
18571
|
-
if (!files.ok)
|
|
18572
|
-
return files;
|
|
18573
|
-
return downloadTeamStorageAssets({ assetsAbs, files: files.value }, { fetch: deps.fetch, fs, onProgress: opts?.onProgress });
|
|
18574
|
-
},
|
|
18575
18665
|
async downloadBundle(envId) {
|
|
18576
18666
|
const urlResult = await getFlowsBundleUrlImpl(envId);
|
|
18577
18667
|
if (!urlResult.ok)
|
|
@@ -20372,9 +20462,11 @@ var registryUrl = "https://registry.npmjs.org";
|
|
|
20372
20462
|
var timeoutMs4 = 3000;
|
|
20373
20463
|
async function fetchLatestVersion(packageName, deps = {}) {
|
|
20374
20464
|
const fetchFn = deps.fetchFn ?? globalThis.fetch;
|
|
20465
|
+
const deadline = AbortSignal.timeout(timeoutMs4);
|
|
20466
|
+
const signal = deps.signal ? AbortSignal.any([deps.signal, deadline]) : deadline;
|
|
20375
20467
|
try {
|
|
20376
20468
|
const response = await fetchFn(`${registryUrl}/${packageName}/latest`, {
|
|
20377
|
-
signal
|
|
20469
|
+
signal
|
|
20378
20470
|
});
|
|
20379
20471
|
if (!response.ok)
|
|
20380
20472
|
return;
|
|
@@ -20423,14 +20515,16 @@ function startUpdateCheck(deps) {
|
|
|
20423
20515
|
if (deps.env["QAWOLF_NO_UPDATE_CHECK"]) {
|
|
20424
20516
|
return noopNotifier;
|
|
20425
20517
|
}
|
|
20518
|
+
const controller = new AbortController;
|
|
20426
20519
|
let latest;
|
|
20427
|
-
deps.fetchLatestVersion().then((version) => {
|
|
20520
|
+
deps.fetchLatestVersion(controller.signal).then((version) => {
|
|
20428
20521
|
latest = version;
|
|
20429
20522
|
}, () => {
|
|
20430
20523
|
return;
|
|
20431
20524
|
});
|
|
20432
20525
|
return {
|
|
20433
20526
|
async notifyIfOutdated() {
|
|
20527
|
+
controller.abort();
|
|
20434
20528
|
if (latest === undefined)
|
|
20435
20529
|
return;
|
|
20436
20530
|
if (!isNewerVersion(deps.currentVersion, latest))
|
|
@@ -20457,7 +20551,7 @@ function startUpdateCheck(deps) {
|
|
|
20457
20551
|
// package.json
|
|
20458
20552
|
var package_default = {
|
|
20459
20553
|
name: "@qawolf/cli",
|
|
20460
|
-
version: "1.
|
|
20554
|
+
version: "1.26.0",
|
|
20461
20555
|
description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
20462
20556
|
keywords: [
|
|
20463
20557
|
"automation",
|
|
@@ -20528,7 +20622,7 @@ var package_default = {
|
|
|
20528
20622
|
"@clack/prompts": "1.5.1",
|
|
20529
20623
|
"@napi-rs/keyring": "1.3.0",
|
|
20530
20624
|
"@oxc-node/core": "0.1.0",
|
|
20531
|
-
"@qawolf/api-contracts": "0.
|
|
20625
|
+
"@qawolf/api-contracts": "0.52.0",
|
|
20532
20626
|
"@qawolf/emails": "1.1.1",
|
|
20533
20627
|
"@qawolf/flow-targets": "1.0.0",
|
|
20534
20628
|
"@qawolf/flows": "0.1.4",
|
|
@@ -20594,7 +20688,7 @@ function buildBaseContext(command, signals) {
|
|
|
20594
20688
|
currentVersion: package_default.version,
|
|
20595
20689
|
configDir: getConfigDir(),
|
|
20596
20690
|
fs,
|
|
20597
|
-
fetchLatestVersion: () => fetchLatestVersion(package_default.name),
|
|
20691
|
+
fetchLatestVersion: (signal) => fetchLatestVersion(package_default.name, { signal }),
|
|
20598
20692
|
renderNotice: (body, title) => ui.note(body, title)
|
|
20599
20693
|
});
|
|
20600
20694
|
return {
|
|
@@ -34541,21 +34635,12 @@ function createProgram({
|
|
|
34541
34635
|
|
|
34542
34636
|
// src/main.ts
|
|
34543
34637
|
var signals = createSignalRegistry();
|
|
34544
|
-
var
|
|
34545
|
-
var onSignal = (sig) => () => {
|
|
34546
|
-
const code = sig === "SIGINT" ? 130 : 143;
|
|
34547
|
-
if (forced)
|
|
34548
|
-
process.exit(code);
|
|
34549
|
-
forced = true;
|
|
34550
|
-
signals.shutdown(sig).finally(() => {
|
|
34551
|
-
process.exit(code);
|
|
34552
|
-
});
|
|
34553
|
-
};
|
|
34638
|
+
var onSignal = createSignalExit({ shutdown: (sig) => signals.shutdown(sig) });
|
|
34554
34639
|
process.on("SIGINT", onSignal("SIGINT"));
|
|
34555
34640
|
process.on("SIGTERM", onSignal("SIGTERM"));
|
|
34556
34641
|
createProgram({ signals }).parseAsync().catch(() => {
|
|
34557
34642
|
if (process.exitCode === undefined)
|
|
34558
34643
|
process.exitCode = 1;
|
|
34559
|
-
}).finally(() =>
|
|
34644
|
+
}).finally(() => exitWhenIdle(typeof process.exitCode === "number" ? process.exitCode : 0));
|
|
34560
34645
|
|
|
34561
|
-
//# debugId=
|
|
34646
|
+
//# debugId=D197CB13AEBF087464756E2164756E21
|
package/dist/runner-sdk.js
CHANGED
|
@@ -6993,6 +6993,10 @@ var makeEmailResourceSchema = () => resource({
|
|
|
6993
6993
|
replyTo: array(namedAddressSchema).optional(),
|
|
6994
6994
|
text: string2().optional().describe("The plain text body.")
|
|
6995
6995
|
}, { urlFieldDescription: emailUrlFieldDescription });
|
|
6996
|
+
var makeEmailAddressResourceSchema = () => resource({
|
|
6997
|
+
address: string2().describe("The inbox address. Mail sent to any plus-suffixed form, such as inbox+abc@example.com, lands in the same inbox."),
|
|
6998
|
+
isDefault: boolean2().describe("Whether this is the workspace's default inbox address.")
|
|
6999
|
+
}, { urlFieldDescription: emailUrlFieldDescription });
|
|
6996
7000
|
|
|
6997
7001
|
// node_modules/@qawolf/api-contracts/dist/v1/identity/organization.js
|
|
6998
7002
|
var identityOrganization = object({
|
|
@@ -7217,6 +7221,8 @@ var runnerNameSchema = _enum([
|
|
|
7217
7221
|
"android",
|
|
7218
7222
|
"ios"
|
|
7219
7223
|
]);
|
|
7224
|
+
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.";
|
|
7225
|
+
var runnerWorkspaceIdDescription = `The workspace the runner belongs to. ${runnerWorkspaceRequirement}`;
|
|
7220
7226
|
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.");
|
|
7221
7227
|
var makeRunnerSchema = () => object({
|
|
7222
7228
|
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."),
|
|
@@ -7245,10 +7251,11 @@ var inspectRequestSchema = discriminatedUnion("what", [
|
|
|
7245
7251
|
what: literal("variable")
|
|
7246
7252
|
})
|
|
7247
7253
|
]);
|
|
7248
|
-
var makeInspectOnRunnerContract = () => {
|
|
7254
|
+
var makeInspectOnRunnerContract = (ids) => {
|
|
7249
7255
|
const input = object({
|
|
7250
7256
|
id: runnerIdSchema.describe("Id of the runner to inspect."),
|
|
7251
|
-
request: inspectRequestSchema
|
|
7257
|
+
request: inspectRequestSchema,
|
|
7258
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
7252
7259
|
});
|
|
7253
7260
|
const output = discriminatedUnion("outcome", [
|
|
7254
7261
|
object({
|
|
@@ -7396,10 +7403,11 @@ var inspectMobileAnswerSchema = discriminatedUnion("what", [
|
|
|
7396
7403
|
what: literal("elements")
|
|
7397
7404
|
})
|
|
7398
7405
|
]);
|
|
7399
|
-
var makeInspectMobileOnRunnerContract = () => {
|
|
7406
|
+
var makeInspectMobileOnRunnerContract = (ids) => {
|
|
7400
7407
|
const input = object({
|
|
7401
7408
|
id: runnerIdSchema.describe("Id of the runner to inspect."),
|
|
7402
|
-
request: inspectMobileRequestSchema
|
|
7409
|
+
request: inspectMobileRequestSchema,
|
|
7410
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
7403
7411
|
});
|
|
7404
7412
|
const output = discriminatedUnion("outcome", [
|
|
7405
7413
|
inspectMobileAnswerSchema,
|
|
@@ -7485,20 +7493,26 @@ var screenFailureReasons = [
|
|
|
7485
7493
|
var screenNotReadyDescription = "`screen-not-ready` if the runner has a screen that cannot serve this instant. Retry in a second or two: the desktop restarts when a run changes the display size, and it serves one see-or-act request at a time, so a screenshot or action already in flight is the usual reason. Do not submit a run to clear this — a run may restart the display and discard what is on it.";
|
|
7486
7494
|
var screenNeedsARunDescription = "`screen-needs-a-run` if the runner's virtual desktop has never started. Waiting will not change this and retrying is pointless — call `runner.runFlow` with a flow that opens a browser, then ask for the screen again. On a runner image with a browser that has never run anything, any `runner.performAction` also starts the browser itself. Evaluating a snippet does not start the desktop.";
|
|
7487
7495
|
var runnerHasNoScreenDescription = "`runner-has-no-screen` if this runner is not one that runs a browser on a virtual desktop. Nothing about it can be seen or driven, and retrying will never help — launch a `playwright` runner instead.";
|
|
7496
|
+
var screenshotImageSchema = string2().min(1).describe("The screenshot, as a base64-encoded JPEG. Decode it and write the bytes to a `.jpg` file — writing this string to the file leaves you with base64 text rather than an image.");
|
|
7488
7497
|
|
|
7489
7498
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/performAction.js
|
|
7490
7499
|
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.";
|
|
7491
7500
|
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.";
|
|
7501
|
+
var withScreenshotDescription = "With `withScreenshot: true`, the answer also carries `imageJpegBase64`: a screenshot taken after the action, once the screen has changed from just before it or half a second has passed, whichever comes first. One call instead of `runner.performAction` followed by `runner.takeScreenshot`, with no fixed wait in between. Anything animating on the page counts as a change. 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.";
|
|
7492
7502
|
var maxActionErrorMessageLength = 1000;
|
|
7493
|
-
var makePerformActionOnRunnerContract = () => {
|
|
7503
|
+
var makePerformActionOnRunnerContract = (ids) => {
|
|
7494
7504
|
const input = object({
|
|
7495
7505
|
action: browserActionSchema.describe("The action to perform. Coordinates are pixels on the screenshot's own coordinate space."),
|
|
7496
|
-
id: runnerIdSchema.describe("Id of the runner to act on.")
|
|
7506
|
+
id: runnerIdSchema.describe("Id of the runner to act on."),
|
|
7507
|
+
withScreenshot: boolean2().optional().describe("Also answer with a screenshot taken after the action, once the screen has changed or half a second has passed. Saves the separate `runner.takeScreenshot` call and its fixed wait."),
|
|
7508
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
7497
7509
|
});
|
|
7510
|
+
const screenshotAfterAction = screenshotImageSchema.optional();
|
|
7498
7511
|
const failure = discriminatedUnion("failureReason", [
|
|
7499
7512
|
object({
|
|
7500
7513
|
errorMessage: string2().max(maxActionErrorMessageLength).describe("What stopped the action from taking effect."),
|
|
7501
7514
|
failureReason: literal("action-failed"),
|
|
7515
|
+
imageJpegBase64: screenshotAfterAction,
|
|
7502
7516
|
outcome: literal("failure")
|
|
7503
7517
|
}),
|
|
7504
7518
|
makeRunnerFailureSchema([
|
|
@@ -7508,11 +7522,14 @@ var makePerformActionOnRunnerContract = () => {
|
|
|
7508
7522
|
])
|
|
7509
7523
|
]);
|
|
7510
7524
|
const output = discriminatedUnion("outcome", [
|
|
7511
|
-
object({
|
|
7525
|
+
object({
|
|
7526
|
+
imageJpegBase64: screenshotAfterAction,
|
|
7527
|
+
outcome: literal("success")
|
|
7528
|
+
}),
|
|
7512
7529
|
failure
|
|
7513
7530
|
]);
|
|
7514
7531
|
return {
|
|
7515
|
-
description: `Perform one raw browser action on an interactive runner: click, double_click, move, drag, scroll, keypress, type, or navigate. Coordinates are whole pixels on the runner's virtual desktop, in the same space as \`runner.takeScreenshot\`. One action per request, and the runner serves one at a time. The action shapes follow the computer-use vocabulary, minus \`screenshot\` (use \`runner.takeScreenshot\`) and \`wait\` (delay on the caller's side). 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} A \`navigate\` does not go through the screen, so a screen that is not ready does not stop it. ${runnerHasNoScreenDescription} ${notSupportedOnMobileDescription} ${unreachableDescription}`,
|
|
7532
|
+
description: `Perform one raw browser action on an interactive runner: click, double_click, move, drag, scroll, keypress, type, or navigate. Coordinates are whole pixels on the runner's virtual desktop, in the same space as \`runner.takeScreenshot\`. One action per request, and the runner serves one at a time. The action shapes follow the computer-use vocabulary, minus \`screenshot\` (use \`runner.takeScreenshot\`) and \`wait\` (delay on the caller's side). 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} A \`navigate\` does not go through the screen, so a screen that is not ready does not stop it. ${runnerHasNoScreenDescription} ${notSupportedOnMobileDescription} ${withScreenshotDescription} ${unreachableDescription}`,
|
|
7516
7533
|
input,
|
|
7517
7534
|
kind: "write",
|
|
7518
7535
|
name: "runner.performAction",
|
|
@@ -7538,9 +7555,11 @@ var makeAgentGetContract = (ids) => {
|
|
|
7538
7555
|
replies: array(agentReplySchema).describe("Everything the AI has said so far, oldest first. A reply carrying choices is a question the work is blocked on."),
|
|
7539
7556
|
sessionId: ids.chatSession,
|
|
7540
7557
|
status: agentSessionStatusSchema
|
|
7541
|
-
}, {
|
|
7558
|
+
}, {
|
|
7559
|
+
urlFieldDescription: "Absolute URL of the live session in the QA Wolf app."
|
|
7560
|
+
});
|
|
7542
7561
|
return {
|
|
7543
|
-
description: '
|
|
7562
|
+
description: 'Monitor a QA Wolf AI session by reading its status and replies. After agent.send, share the returned session URL before monitoring. Wait 30 to 60 seconds between checks; do not call this in a tight loop. Replies accumulate, so compare them with what you have already seen. Continue monitoring silently when the status and replies are unchanged; 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.',
|
|
7544
7563
|
input,
|
|
7545
7564
|
kind: "read",
|
|
7546
7565
|
name: "agent.get",
|
|
@@ -7559,9 +7578,11 @@ var makeAgentSendContract = (ids) => {
|
|
|
7559
7578
|
const output = resource({
|
|
7560
7579
|
sessionId: ids.chatSession.describe("Follow the work by passing this to agent.get."),
|
|
7561
7580
|
status: agentSessionStatusSchema
|
|
7562
|
-
}, {
|
|
7581
|
+
}, {
|
|
7582
|
+
urlFieldDescription: "Absolute URL of the live session in the QA Wolf app."
|
|
7583
|
+
});
|
|
7563
7584
|
return {
|
|
7564
|
-
description: "
|
|
7585
|
+
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, make the next action a normal user-visible assistant message containing the exact returned url, before any tool call or wait. 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.",
|
|
7565
7586
|
input,
|
|
7566
7587
|
kind: "write",
|
|
7567
7588
|
name: "agent.send",
|
|
@@ -7664,10 +7685,7 @@ var makeListEmailAddressesContract = (ids) => {
|
|
|
7664
7685
|
...makePaginationInputFields({ defaultLimit: 20, maxLimit: 50 })
|
|
7665
7686
|
});
|
|
7666
7687
|
const output = object({
|
|
7667
|
-
addresses: array(
|
|
7668
|
-
address: string2().describe("The inbox address. Mail sent to any plus-suffixed form, such as inbox+abc@example.com, lands in the same inbox."),
|
|
7669
|
-
isDefault: boolean2().describe("Whether this is the workspace's default inbox address.")
|
|
7670
|
-
}, { urlFieldDescription: emailUrlFieldDescription })).describe("The workspace's permanent inbox addresses, alphabetical."),
|
|
7688
|
+
addresses: array(makeEmailAddressResourceSchema()).describe("The workspace's permanent inbox addresses, alphabetical."),
|
|
7671
7689
|
nextCursor: nextCursorSchema
|
|
7672
7690
|
});
|
|
7673
7691
|
return {
|
|
@@ -7679,6 +7697,28 @@ var makeListEmailAddressesContract = (ids) => {
|
|
|
7679
7697
|
};
|
|
7680
7698
|
};
|
|
7681
7699
|
|
|
7700
|
+
// node_modules/@qawolf/api-contracts/dist/v1/email/registerAddress.js
|
|
7701
|
+
var hasNoPlusSuffix = (address) => {
|
|
7702
|
+
const [localPart] = address.split("@");
|
|
7703
|
+
return localPart !== undefined && !localPart.includes("+");
|
|
7704
|
+
};
|
|
7705
|
+
var makeRegisterEmailAddressContract = (ids) => {
|
|
7706
|
+
const input = object({
|
|
7707
|
+
address: email2().refine(hasNoPlusSuffix, {
|
|
7708
|
+
message: "Register the base address. Mail to a plus-suffixed form lands in the base inbox."
|
|
7709
|
+
}).describe("The inbox address to register. Its domain must be one QA Wolf serves for the workspace."),
|
|
7710
|
+
workspaceId: ids.workspace.optional().describe("The workspace to register the address in. Required when authenticating with an organization or user API key.")
|
|
7711
|
+
});
|
|
7712
|
+
const output = makeEmailAddressResourceSchema();
|
|
7713
|
+
return {
|
|
7714
|
+
description: "Register an inbox address for the workspace. Registering an address the workspace already has changes nothing. A refusal names the domains the workspace can use.",
|
|
7715
|
+
input,
|
|
7716
|
+
kind: "write",
|
|
7717
|
+
name: "email.registerAddress",
|
|
7718
|
+
output
|
|
7719
|
+
};
|
|
7720
|
+
};
|
|
7721
|
+
|
|
7682
7722
|
// node_modules/@qawolf/api-contracts/dist/v1/email/send.js
|
|
7683
7723
|
var recipientsSchema = array(email2()).max(50);
|
|
7684
7724
|
var maxAttachments = 10;
|
|
@@ -8319,14 +8359,31 @@ var makeReattemptRunContract = (ids) => {
|
|
|
8319
8359
|
};
|
|
8320
8360
|
};
|
|
8321
8361
|
|
|
8362
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/stop.js
|
|
8363
|
+
var makeStopRunContract = (ids) => {
|
|
8364
|
+
const input = object({ runId: ids.run });
|
|
8365
|
+
const output = resource({
|
|
8366
|
+
runId: ids.run,
|
|
8367
|
+
status: _enum(["accepted", "already-finished", "execution-not-found"]).describe('"accepted" means stopping was requested; poll run.get for the final result. "already-finished" means the run has already finished and its result is unchanged. "execution-not-found" means the run exists but no execution was found to stop; it may still be starting or may no longer be available. No stop request was accepted in that case.')
|
|
8368
|
+
}, { urlFieldDescription: "Absolute URL of the run page." });
|
|
8369
|
+
return {
|
|
8370
|
+
description: "Stop a run, including its queued flows and automatic retries. Stopping is asynchronous. Repeated requests are safe, and finished runs keep their results. A run that is still being created returns not found; retry once run.get returns the run. If run.get returns a different runId, use that ID. Poll run.get for results.",
|
|
8371
|
+
input,
|
|
8372
|
+
kind: "write",
|
|
8373
|
+
name: "run.stop",
|
|
8374
|
+
output
|
|
8375
|
+
};
|
|
8376
|
+
};
|
|
8377
|
+
|
|
8322
8378
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/evaluateSnippet.js
|
|
8323
8379
|
var maxSnippetCodeLength = 64 * 1024;
|
|
8324
|
-
var makeEvaluateSnippetOnRunnerContract = () => {
|
|
8380
|
+
var makeEvaluateSnippetOnRunnerContract = (ids) => {
|
|
8325
8381
|
const input = object({
|
|
8326
8382
|
code: string2().min(1).max(maxSnippetCodeLength).describe("The code to evaluate against the runner's live page, in the scope of `filePath` when one is given."),
|
|
8327
8383
|
filePath: runFilePathSchema.optional().describe("Path of the file the snippet is evaluated in the scope of. Omit to evaluate a snippet that imports nothing of yours."),
|
|
8328
8384
|
files: runFilesSchema.optional().describe("The file named by `filePath` and everything it imports. A runner holds no copy of your project, so a snippet's scope has to travel with it."),
|
|
8329
|
-
id: runnerIdSchema.describe("Id of the runner to evaluate the snippet on.")
|
|
8385
|
+
id: runnerIdSchema.describe("Id of the runner to evaluate the snippet on."),
|
|
8386
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8330
8387
|
}).refine((request) => request.filePath === undefined || Object.hasOwn(request.files ?? {}, request.filePath), {
|
|
8331
8388
|
error: "A snippet naming a file must carry that file in files.",
|
|
8332
8389
|
path: ["files"]
|
|
@@ -8352,9 +8409,10 @@ var makeEvaluateSnippetOnRunnerContract = () => {
|
|
|
8352
8409
|
};
|
|
8353
8410
|
|
|
8354
8411
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/get.js
|
|
8355
|
-
var makeGetRunnerContract = () => {
|
|
8412
|
+
var makeGetRunnerContract = (ids) => {
|
|
8356
8413
|
const input = object({
|
|
8357
|
-
id: runnerIdSchema.describe("Id of the runner to look up.")
|
|
8414
|
+
id: runnerIdSchema.describe("Id of the runner to look up."),
|
|
8415
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8358
8416
|
});
|
|
8359
8417
|
const output = object({
|
|
8360
8418
|
id: runnerIdSchema.describe("Id of the runner the call addressed."),
|
|
@@ -8372,10 +8430,11 @@ var makeGetRunnerContract = () => {
|
|
|
8372
8430
|
|
|
8373
8431
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/highlightSelector.js
|
|
8374
8432
|
var maxHighlightSelectorLength = 2000;
|
|
8375
|
-
var makeHighlightSelectorOnRunnerContract = () => {
|
|
8433
|
+
var makeHighlightSelectorOnRunnerContract = (ids) => {
|
|
8376
8434
|
const input = object({
|
|
8377
8435
|
id: runnerIdSchema.describe("Id of the runner to highlight on."),
|
|
8378
|
-
selector: string2().max(maxHighlightSelectorLength).describe("The selector to highlight. An empty string clears whatever is highlighted.")
|
|
8436
|
+
selector: string2().max(maxHighlightSelectorLength).describe("The selector to highlight. An empty string clears whatever is highlighted."),
|
|
8437
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8379
8438
|
});
|
|
8380
8439
|
const output = discriminatedUnion("outcome", [
|
|
8381
8440
|
object({
|
|
@@ -8410,12 +8469,13 @@ var makeHighlightSelectorOnRunnerContract = () => {
|
|
|
8410
8469
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/importPackage.js
|
|
8411
8470
|
var maxPackageNameLength = 214;
|
|
8412
8471
|
var maxPackageVersionLength = 256;
|
|
8413
|
-
var makeImportPackageOnRunnerContract = () => {
|
|
8472
|
+
var makeImportPackageOnRunnerContract = (ids) => {
|
|
8414
8473
|
const input = object({
|
|
8415
8474
|
id: runnerIdSchema.describe("Id of the runner to install into."),
|
|
8416
8475
|
npmDependencies: record(string2(), string2()).describe("The run's current npm dependencies, so the install resolves against them."),
|
|
8417
8476
|
packageName: string2().min(1).max(maxPackageNameLength),
|
|
8418
|
-
packageVersion: string2().min(1).max(maxPackageVersionLength)
|
|
8477
|
+
packageVersion: string2().min(1).max(maxPackageVersionLength),
|
|
8478
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8419
8479
|
});
|
|
8420
8480
|
const output = discriminatedUnion("outcome", [
|
|
8421
8481
|
object({ outcome: literal("success") }),
|
|
@@ -8435,10 +8495,11 @@ var makeImportPackageOnRunnerContract = () => {
|
|
|
8435
8495
|
};
|
|
8436
8496
|
|
|
8437
8497
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/index.js
|
|
8438
|
-
var makeLaunchRunnerContract = () => {
|
|
8498
|
+
var makeLaunchRunnerContract = (ids) => {
|
|
8439
8499
|
const input = object({
|
|
8440
8500
|
id: runnerIdSchema.describe("Id to launch the runner under, chosen by the caller. Launching the same id again returns the runner already running under it."),
|
|
8441
|
-
runnerName: runnerNameSchema.optional().describe("The runner family to launch. Defaults to the standard web family, playwright.")
|
|
8501
|
+
runnerName: runnerNameSchema.optional().describe("The runner family to launch. Defaults to the standard web family, playwright."),
|
|
8502
|
+
workspaceId: ids.workspace.optional().describe(`The workspace to launch the runner in. ${runnerWorkspaceRequirement}`)
|
|
8442
8503
|
});
|
|
8443
8504
|
const output = makeRunnerSchema().extend({
|
|
8444
8505
|
alreadyRunning: boolean2().describe("True when a runner was already running under this id and was returned unchanged, so no second runner was started and nothing new is billed. Not an error either way."),
|
|
@@ -8452,9 +8513,10 @@ var makeLaunchRunnerContract = () => {
|
|
|
8452
8513
|
output
|
|
8453
8514
|
};
|
|
8454
8515
|
};
|
|
8455
|
-
var makeTerminateRunnerContract = () => {
|
|
8516
|
+
var makeTerminateRunnerContract = (ids) => {
|
|
8456
8517
|
const input = object({
|
|
8457
|
-
id: runnerIdSchema.describe("Id of the runner to terminate.")
|
|
8518
|
+
id: runnerIdSchema.describe("Id of the runner to terminate."),
|
|
8519
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8458
8520
|
});
|
|
8459
8521
|
const output = object({
|
|
8460
8522
|
id: runnerIdSchema.describe("Id of the runner the call addressed."),
|
|
@@ -8471,14 +8533,16 @@ var makeTerminateRunnerContract = () => {
|
|
|
8471
8533
|
};
|
|
8472
8534
|
|
|
8473
8535
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/list.js
|
|
8474
|
-
var makeListRunnersContract = () => {
|
|
8475
|
-
const input = object({
|
|
8536
|
+
var makeListRunnersContract = (ids) => {
|
|
8537
|
+
const input = object({
|
|
8538
|
+
workspaceId: ids.workspace.optional().describe(`The workspace whose runners to list. ${runnerWorkspaceRequirement}`)
|
|
8539
|
+
});
|
|
8476
8540
|
const output = object({
|
|
8477
8541
|
outcome: literal("success"),
|
|
8478
8542
|
runners: array(makeRunnerSchema()).describe("The runners running right now, in no particular order. Each entry carries the same fields a launch reports, so any of them can be addressed like a runner you launched yourself.")
|
|
8479
8543
|
});
|
|
8480
8544
|
return {
|
|
8481
|
-
description: "List the runners running on the caller's team right now.
|
|
8545
|
+
description: "List the runners running on the caller's team right now. A team API key already names the team and sends nothing; a credential bound to no single workspace names one in `workspaceId`. A runner that was terminated, or that terminated on its own after inactivity, is not listed, because the run system keeps no record of a runner once it is gone. 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. It is how to find a runner launched from another machine or in an earlier session, when the id it was launched under is no longer at hand.",
|
|
8482
8546
|
input,
|
|
8483
8547
|
kind: "read",
|
|
8484
8548
|
name: "runner.list",
|
|
@@ -8488,11 +8552,12 @@ var makeListRunnersContract = () => {
|
|
|
8488
8552
|
|
|
8489
8553
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/promoteSnapshot.js
|
|
8490
8554
|
var maxSnapshotPathLength = 1024;
|
|
8491
|
-
var makePromoteSnapshotOnRunnerContract = () => {
|
|
8555
|
+
var makePromoteSnapshotOnRunnerContract = (ids) => {
|
|
8492
8556
|
const input = object({
|
|
8493
8557
|
baselinePath: string2().min(1).max(maxSnapshotPathLength).describe("The baseline to replace, as named in the image diff."),
|
|
8494
8558
|
id: runnerIdSchema.describe("Id of the runner holding the screenshot."),
|
|
8495
|
-
screenshotPath: string2().min(1).max(maxSnapshotPathLength).describe("The screenshot to promote, as named in the image diff.")
|
|
8559
|
+
screenshotPath: string2().min(1).max(maxSnapshotPathLength).describe("The screenshot to promote, as named in the image diff."),
|
|
8560
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8496
8561
|
});
|
|
8497
8562
|
const output = discriminatedUnion("outcome", [
|
|
8498
8563
|
object({ outcome: literal("success") }),
|
|
@@ -8515,10 +8580,11 @@ var makePromoteSnapshotOnRunnerContract = () => {
|
|
|
8515
8580
|
};
|
|
8516
8581
|
|
|
8517
8582
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/readJournal.js
|
|
8518
|
-
var makeReadRunnerJournalContract = () => {
|
|
8583
|
+
var makeReadRunnerJournalContract = (ids) => {
|
|
8519
8584
|
const input = readJournalRequestSchema.extend({
|
|
8520
8585
|
id: runnerIdSchema.describe("Id of the runner to read the journal of."),
|
|
8521
|
-
stream: journalStreamSchema.describe(`The stream to read. QA Wolf writes ${knownJournalStreams.join(", ")}; an unwritten stream reads as empty rather than as an error.`)
|
|
8586
|
+
stream: journalStreamSchema.describe(`The stream to read. QA Wolf writes ${knownJournalStreams.join(", ")}; an unwritten stream reads as empty rather than as an error.`),
|
|
8587
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8522
8588
|
});
|
|
8523
8589
|
const output = discriminatedUnion("outcome", [
|
|
8524
8590
|
readJournalResponseSchema.extend({ outcome: literal("success") }),
|
|
@@ -8553,7 +8619,8 @@ var makeRunFlowOnRunnerContract = (ids) => {
|
|
|
8553
8619
|
files: runFilesSchema.describe("Every file the run needs keyed by its path — the flow file, everything it imports, package.json and tsconfig.json. A runner holds no copy of your project, so what runs is exactly what is sent here."),
|
|
8554
8620
|
id: runnerIdSchema.describe("Id of the runner to run the flow on."),
|
|
8555
8621
|
selection: runSelectionSchema.optional().describe("Run only these lines, inside the page the runner is already on, instead of running the whole flow from a fresh browser. Omit to run the whole entry point."),
|
|
8556
|
-
unchangedFiles: unchangedFilesSchema.optional().describe("Files this runner already holds from an earlier run, keyed by path with a hex SHA-256 of the content it should hold. Send this to make `files` only what changed. The runner refuses with `needs-full-sync` if it holds none of a referenced path, so it never runs something other than what was asked for. Omit it to send every file.")
|
|
8622
|
+
unchangedFiles: unchangedFilesSchema.optional().describe("Files this runner already holds from an earlier run, keyed by path with a hex SHA-256 of the content it should hold. Send this to make `files` only what changed. The runner refuses with `needs-full-sync` if it holds none of a referenced path, so it never runs something other than what was asked for. Omit it to send every file."),
|
|
8623
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8557
8624
|
}).refine((request) => request.env === undefined || request.environmentId === undefined, {
|
|
8558
8625
|
error: "Send env or environmentId, not both. An environment's variables are taken as they are stored, so there is no order in which the two would combine.",
|
|
8559
8626
|
path: ["environmentId"]
|
|
@@ -8605,9 +8672,10 @@ var makeRunFlowOnRunnerContract = (ids) => {
|
|
|
8605
8672
|
};
|
|
8606
8673
|
|
|
8607
8674
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/stopRun.js
|
|
8608
|
-
var makeStopRunOnRunnerContract = () => {
|
|
8675
|
+
var makeStopRunOnRunnerContract = (ids) => {
|
|
8609
8676
|
const input = object({
|
|
8610
|
-
id: runnerIdSchema.describe("Id of the runner to stop the run on.")
|
|
8677
|
+
id: runnerIdSchema.describe("Id of the runner to stop the run on."),
|
|
8678
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8611
8679
|
});
|
|
8612
8680
|
const output = discriminatedUnion("outcome", [
|
|
8613
8681
|
object({
|
|
@@ -8626,13 +8694,14 @@ var makeStopRunOnRunnerContract = () => {
|
|
|
8626
8694
|
};
|
|
8627
8695
|
|
|
8628
8696
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/takeScreenshot.js
|
|
8629
|
-
var makeTakeScreenshotOnRunnerContract = () => {
|
|
8697
|
+
var makeTakeScreenshotOnRunnerContract = (ids) => {
|
|
8630
8698
|
const input = object({
|
|
8631
|
-
id: runnerIdSchema.describe("Id of the runner to take a screenshot of.")
|
|
8699
|
+
id: runnerIdSchema.describe("Id of the runner to take a screenshot of."),
|
|
8700
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8632
8701
|
});
|
|
8633
8702
|
const output = discriminatedUnion("outcome", [
|
|
8634
8703
|
object({
|
|
8635
|
-
imageJpegBase64:
|
|
8704
|
+
imageJpegBase64: screenshotImageSchema,
|
|
8636
8705
|
outcome: literal("success")
|
|
8637
8706
|
}),
|
|
8638
8707
|
makeRunnerFailureSchema([
|
|
@@ -8686,6 +8755,7 @@ var makeContractsV1 = (ids) => {
|
|
|
8686
8755
|
get: makeGetEmailContract(resolvedIds),
|
|
8687
8756
|
getAttachment: makeGetEmailAttachmentContract(resolvedIds),
|
|
8688
8757
|
listAddresses: makeListEmailAddressesContract(resolvedIds),
|
|
8758
|
+
registerAddress: makeRegisterEmailAddressContract(resolvedIds),
|
|
8689
8759
|
send: makeSendEmailContract(resolvedIds)
|
|
8690
8760
|
},
|
|
8691
8761
|
environment: {
|
|
@@ -8717,24 +8787,25 @@ var makeContractsV1 = (ids) => {
|
|
|
8717
8787
|
diagnose: makeDiagnoseRunContract(resolvedIds),
|
|
8718
8788
|
find: makeFindRunsContract(resolvedIds),
|
|
8719
8789
|
get: makeGetRunContract(resolvedIds),
|
|
8720
|
-
reattempt: makeReattemptRunContract(resolvedIds)
|
|
8790
|
+
reattempt: makeReattemptRunContract(resolvedIds),
|
|
8791
|
+
stop: makeStopRunContract(resolvedIds)
|
|
8721
8792
|
},
|
|
8722
8793
|
runner: {
|
|
8723
|
-
evaluateSnippet: makeEvaluateSnippetOnRunnerContract(),
|
|
8724
|
-
get: makeGetRunnerContract(),
|
|
8725
|
-
highlightSelector: makeHighlightSelectorOnRunnerContract(),
|
|
8726
|
-
importPackage: makeImportPackageOnRunnerContract(),
|
|
8727
|
-
inspect: makeInspectOnRunnerContract(),
|
|
8728
|
-
inspectMobile: makeInspectMobileOnRunnerContract(),
|
|
8729
|
-
launch: makeLaunchRunnerContract(),
|
|
8730
|
-
list: makeListRunnersContract(),
|
|
8731
|
-
performAction: makePerformActionOnRunnerContract(),
|
|
8732
|
-
promoteSnapshot: makePromoteSnapshotOnRunnerContract(),
|
|
8733
|
-
readJournal: makeReadRunnerJournalContract(),
|
|
8794
|
+
evaluateSnippet: makeEvaluateSnippetOnRunnerContract(resolvedIds),
|
|
8795
|
+
get: makeGetRunnerContract(resolvedIds),
|
|
8796
|
+
highlightSelector: makeHighlightSelectorOnRunnerContract(resolvedIds),
|
|
8797
|
+
importPackage: makeImportPackageOnRunnerContract(resolvedIds),
|
|
8798
|
+
inspect: makeInspectOnRunnerContract(resolvedIds),
|
|
8799
|
+
inspectMobile: makeInspectMobileOnRunnerContract(resolvedIds),
|
|
8800
|
+
launch: makeLaunchRunnerContract(resolvedIds),
|
|
8801
|
+
list: makeListRunnersContract(resolvedIds),
|
|
8802
|
+
performAction: makePerformActionOnRunnerContract(resolvedIds),
|
|
8803
|
+
promoteSnapshot: makePromoteSnapshotOnRunnerContract(resolvedIds),
|
|
8804
|
+
readJournal: makeReadRunnerJournalContract(resolvedIds),
|
|
8734
8805
|
runFlow: makeRunFlowOnRunnerContract(resolvedIds),
|
|
8735
|
-
stopRun: makeStopRunOnRunnerContract(),
|
|
8736
|
-
takeScreenshot: makeTakeScreenshotOnRunnerContract(),
|
|
8737
|
-
terminate: makeTerminateRunnerContract()
|
|
8806
|
+
stopRun: makeStopRunOnRunnerContract(resolvedIds),
|
|
8807
|
+
takeScreenshot: makeTakeScreenshotOnRunnerContract(resolvedIds),
|
|
8808
|
+
terminate: makeTerminateRunnerContract(resolvedIds)
|
|
8738
8809
|
},
|
|
8739
8810
|
tag: {
|
|
8740
8811
|
create: makeCreateTagContract(resolvedIds),
|
|
@@ -12323,6 +12394,34 @@ async function writeTeamStorageAssets(args, deps) {
|
|
|
12323
12394
|
}
|
|
12324
12395
|
}
|
|
12325
12396
|
|
|
12397
|
+
// src/shell/platform/teamStorageMethods.ts
|
|
12398
|
+
function createTeamStorageMethods(trpc, deps, fs, getIdentity) {
|
|
12399
|
+
async function list() {
|
|
12400
|
+
if (deps.workspaceId !== undefined) {
|
|
12401
|
+
return listTeamStorageFiles(trpc, { teamId: deps.workspaceId }, deps);
|
|
12402
|
+
}
|
|
12403
|
+
const identity = await getIdentity();
|
|
12404
|
+
if (!identity.ok)
|
|
12405
|
+
return identity;
|
|
12406
|
+
if (!("team" in identity.value)) {
|
|
12407
|
+
return {
|
|
12408
|
+
ok: false,
|
|
12409
|
+
error: flowsMessages.pull.teamStorageRequiresTeamKey
|
|
12410
|
+
};
|
|
12411
|
+
}
|
|
12412
|
+
return listTeamStorageFiles(trpc, { teamId: identity.value.team.id }, deps);
|
|
12413
|
+
}
|
|
12414
|
+
return {
|
|
12415
|
+
listTeamStorageFiles: list,
|
|
12416
|
+
async syncTeamStorageAssets(assetsAbs, opts) {
|
|
12417
|
+
const files = await list();
|
|
12418
|
+
if (!files.ok)
|
|
12419
|
+
return files;
|
|
12420
|
+
return downloadTeamStorageAssets({ assetsAbs, files: files.value }, { fetch: deps.fetch, fs, onProgress: opts?.onProgress });
|
|
12421
|
+
}
|
|
12422
|
+
};
|
|
12423
|
+
}
|
|
12424
|
+
|
|
12326
12425
|
// src/shell/platform/createPlatformClient.ts
|
|
12327
12426
|
var requestBackoffMs2 = [500, 1500];
|
|
12328
12427
|
function createPlatformClient(apiKey, deps) {
|
|
@@ -12339,8 +12438,10 @@ function createPlatformClient(apiKey, deps) {
|
|
|
12339
12438
|
return result;
|
|
12340
12439
|
return { ok: true, value: { signedUrl: result.value.url } };
|
|
12341
12440
|
}
|
|
12441
|
+
const identityMethods = createIdentityMethods(apiKey, deps, requestBackoffMs2);
|
|
12342
12442
|
return {
|
|
12343
|
-
...
|
|
12443
|
+
...identityMethods,
|
|
12444
|
+
...createTeamStorageMethods(trpc, deps, fs, identityMethods.getIdentity),
|
|
12344
12445
|
getFlowsBundleUrl: getFlowsBundleUrlImpl,
|
|
12345
12446
|
callPublicApi: makeCallPublicApiMethod(trpc, deps, requestBackoffMs2),
|
|
12346
12447
|
async getEnvVars(envId) {
|
|
@@ -12354,24 +12455,6 @@ function createPlatformClient(apiKey, deps) {
|
|
|
12354
12455
|
return result;
|
|
12355
12456
|
return { ok: true, value: result.value.environmentVariables };
|
|
12356
12457
|
},
|
|
12357
|
-
async listTeamStorageFiles() {
|
|
12358
|
-
const identity = await this.getIdentity();
|
|
12359
|
-
if (!identity.ok)
|
|
12360
|
-
return identity;
|
|
12361
|
-
if (!("team" in identity.value)) {
|
|
12362
|
-
return {
|
|
12363
|
-
ok: false,
|
|
12364
|
-
error: flowsMessages.pull.teamStorageRequiresTeamKey
|
|
12365
|
-
};
|
|
12366
|
-
}
|
|
12367
|
-
return listTeamStorageFiles(trpc, { teamId: identity.value.team.id }, deps);
|
|
12368
|
-
},
|
|
12369
|
-
async syncTeamStorageAssets(assetsAbs, opts) {
|
|
12370
|
-
const files = await this.listTeamStorageFiles();
|
|
12371
|
-
if (!files.ok)
|
|
12372
|
-
return files;
|
|
12373
|
-
return downloadTeamStorageAssets({ assetsAbs, files: files.value }, { fetch: deps.fetch, fs, onProgress: opts?.onProgress });
|
|
12374
|
-
},
|
|
12375
12458
|
async downloadBundle(envId) {
|
|
12376
12459
|
const urlResult = await getFlowsBundleUrlImpl(envId);
|
|
12377
12460
|
if (!urlResult.ok)
|
|
@@ -13094,4 +13177,4 @@ export {
|
|
|
13094
13177
|
createRunnerSdk
|
|
13095
13178
|
};
|
|
13096
13179
|
|
|
13097
|
-
//# debugId=
|
|
13180
|
+
//# debugId=1D5281D81CC3C81164756E2164756E21
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qawolf/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.0",
|
|
4
4
|
"description": "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"automation",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"@clack/prompts": "1.5.1",
|
|
72
72
|
"@napi-rs/keyring": "1.3.0",
|
|
73
73
|
"@oxc-node/core": "0.1.0",
|
|
74
|
-
"@qawolf/api-contracts": "0.
|
|
74
|
+
"@qawolf/api-contracts": "0.52.0",
|
|
75
75
|
"@qawolf/emails": "1.1.1",
|
|
76
76
|
"@qawolf/flow-targets": "1.0.0",
|
|
77
77
|
"@qawolf/flows": "0.1.4",
|
|
@@ -125,8 +125,8 @@ that `url`; never guess a route and never send a repository link in its place.
|
|
|
125
125
|
<!-- prettier-ignore -->
|
|
126
126
|
| Command | Kind | What it does |
|
|
127
127
|
| --- | --- | --- |
|
|
128
|
-
| `qawolf agent get` | read |
|
|
129
|
-
| `qawolf agent send` | write |
|
|
128
|
+
| `qawolf agent get` | read | Monitor a QA Wolf AI session by reading its status and replies. After agent.send, share the returned session URL before monitoring. Wait 30 to 60 seconds between checks; do not call this in a tight loop. Replies accumulate, so compare them with what you have already seen. Continue monitoring silently when the status and replies are unchanged; 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. |
|
|
129
|
+
| `qawolf agent send` | write | 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, make the next action a normal user-visible assistant message containing the exact returned url, before any tool call or wait. 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. |
|
|
130
130
|
| `qawolf auth login` | local | Authenticate with QA Wolf in a browser or with an API key |
|
|
131
131
|
| `qawolf auth logout` | local | Remove stored credentials |
|
|
132
132
|
| `qawolf auth switch` | local | Choose which workspace to work in |
|
|
@@ -137,6 +137,7 @@ that `url`; never guess a route and never send a repository link in its place.
|
|
|
137
137
|
| `qawolf email get` | read | Read one email of the workspace, with its plain text and HTML bodies. Use it to pull a sign-in code or a verification link out of a message. |
|
|
138
138
|
| `qawolf email getAttachment` | read | Read one attachment of a workspace email as base64 content, by file name or by position. email.get lists both. |
|
|
139
139
|
| `qawolf email listAddresses` | read | List the workspace's inbox addresses, alphabetical. A flow can sign up with a plus-suffixed form of any of them, and email.find reads what arrives. |
|
|
140
|
+
| `qawolf email registerAddress` | write | Register an inbox address for the workspace. Registering an address the workspace already has changes nothing. A refusal names the domains the workspace can use. |
|
|
140
141
|
| `qawolf email send` | write | Send an email from one of the workspace's inbox addresses, for example to exercise a flow that reacts to incoming mail. Returns the sent email; read it back with email.get. |
|
|
141
142
|
| `qawolf environment create` | write | Create an environment on the caller's team and return it in the environment.get shape. |
|
|
142
143
|
| `qawolf environment deleteVariable` | write | Remove one environment variable by name. Succeeds whether or not the variable existed. |
|
|
@@ -168,6 +169,7 @@ that `url`; never guess a route and never send a repository link in its place.
|
|
|
168
169
|
| `qawolf run find` | read | List an environment's recent runs, newest first. |
|
|
169
170
|
| `qawolf run get` | read | Get a run's status, per-flow results, and links. |
|
|
170
171
|
| `qawolf run reattempt` | write | Request new attempts for a run's flows, in the same run. A flow is eligible once its result is failed or canceled and QA Wolf's automatic retries have finished. A fully investigated run no longer accepts reattempts. Attempts run with the latest flow code. Poll run.get for results. |
|
|
172
|
+
| `qawolf run stop` | write | Stop a run, including its queued flows and automatic retries. Stopping is asynchronous. Repeated requests are safe, and finished runs keep their results. A run that is still being created returns not found; retry once run.get returns the run. If run.get returns a different runId, use that ID. Poll run.get for results. |
|
|
171
173
|
| `qawolf runner act` | write | Perform one raw action on a runner's screen: click, double_click, scroll, move, drag, keypress, navigate or type. Use - to read a whole action as JSON from stdin. On a mobile runner only click (button left), drag and type have a touchscreen equivalent; the rest answer action-not-supported-on-mobile |
|
|
172
174
|
| `qawolf runner events` | read | Print a runner's journal, one entry per line. QA Wolf writes console, recorder, run-events, run-logs, run-status |
|
|
173
175
|
| `qawolf runner exec` | write | Evaluate a snippet against a runner's live page. Use - to read the snippet from stdin |
|