@qawolf/cli 1.29.0 → 1.31.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.
@@ -11,6 +11,12 @@ export type RunnerSdkOptions = {
11
11
  };
12
12
  export type SdkResult<Value> = {
13
13
  error: string;
14
+ /**
15
+ * What the platform said beyond the headline, and what to do about it:
16
+ * the reason a runner is gone, which of `--runner`, `QAWOLF_RUNNER_ID` or
17
+ * the stored default named it, the command that brings one back.
18
+ */
19
+ errorDetail?: string;
14
20
  ok: false;
15
21
  } | {
16
22
  ok: true;
@@ -124,6 +130,8 @@ export type ListedRunner = {
124
130
  isDefault: boolean;
125
131
  launchedHere: boolean;
126
132
  runnerName: string;
133
+ /** The QA Wolf page for this runner, showing its screen once it has one. */
134
+ url: string;
127
135
  };
128
136
  export type KeptAlive = {
129
137
  id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qawolf/cli",
3
- "version": "1.29.0",
3
+ "version": "1.31.0",
4
4
  "description": "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
5
5
  "keywords": [
6
6
  "automation",
@@ -75,7 +75,7 @@
75
75
  "@qawolf/emails": "1.1.1",
76
76
  "@qawolf/flow-targets": "1.0.0",
77
77
  "@qawolf/flows": "0.1.4",
78
- "@qawolf/testkit": "1.1.1",
78
+ "@qawolf/testkit": "1.2.1",
79
79
  "commander": "14.0.3",
80
80
  "env-paths": "4.0.0",
81
81
  "picomatch": "4.0.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 | 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. 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. |
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. |
128
+ | `qawolf agent get` | read | Read what the QA Wolf AI has said and whether it is still working |
129
+ | `qawolf agent send` | write | Ask the QA Wolf AI to do a piece of work, such as covering a journey or fixing a broken flow |
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 |
@@ -209,6 +209,60 @@ changes team state; `local` only affects this machine. A parenthesized
209
209
  note like `local (read with --remote)` means that flag makes the command
210
210
  call the QA Wolf API and require auth.
211
211
 
212
+ ## Asking QA Wolf to do the work: the `agent` group
213
+
214
+ `qawolf agent send "<what you want>"` is the one verb that starts work from
215
+ nothing. Every other write acts on a flow, run or issue that already exists.
216
+ Name the journey, the part of the app it covers, and anything the AI cannot
217
+ discover for itself, such as a test account, a feature flag, or how to reach a
218
+ staging environment. For a long message, put it in a file and pass
219
+ `"$(cat prompt.md)"`, so shell quoting cannot split it into arguments.
220
+
221
+ `--environment-id <id-or-alias>` picks the environment and reads
222
+ `QAWOLF_ENVIRONMENT`. A credential that is not bound to one workspace, such as
223
+ an organization or user API key, needs `--workspace-id`.
224
+
225
+ To give the AI a file, such as a spreadsheet of journeys, upload it first with
226
+ `qawolf file requestUpload --file-name <name>`, PUT the bytes to the returned
227
+ URL with the returned content type, then pass the returned path as
228
+ `--file-paths <path>` on the send. The AI reads it from storage, so a plan of
229
+ hundreds of journeys costs nothing to send. Up to 20 paths per send.
230
+
231
+ The work runs for minutes to tens of minutes. **Pass `--follow` and do not poll.**
232
+ The CLI reads the session for you, prints each reply once as it arrives, and
233
+ exits when the session settles: 0 when the work is complete, non-zero when it
234
+ failed or was cancelled. Looping `qawolf agent get` yourself costs a round trip
235
+ per tick and shows you replies you have already seen.
236
+
237
+ A session can stop and ask a question. With `--follow` in `--agent` or `--json`
238
+ mode the CLI prints the question and **exits 0** — a session that asked something
239
+ has handed the work back, it has not failed. Answer it, then pick the session
240
+ back up:
241
+
242
+ ```bash
243
+ qawolf agent send "<your answer>" --session <sessionId>
244
+ qawolf agent get --follow
245
+ ```
246
+
247
+ In `--agent` or `--json` mode a follow ends with one JSON line holding the whole
248
+ session: `sessionId`, `status`, `url` and `replies`. It is the same object a
249
+ plain `qawolf agent get` answers with. Read the final `status` from that line;
250
+ the exit code alone does not tell a blocked session from a completed one.
251
+
252
+ `agent send` remembers the session it started, so a later `agent get --follow`
253
+ in the same directory needs no id. `--session <id>` or `QAWOLF_SESSION_ID`
254
+ override that.
255
+
256
+ Every session has a `url`, which the CLI prints when it starts or attaches.
257
+ A session opens in whichever workspace the credential is pointed at, which is not
258
+ always the one the user has open in the app, so send that `url` when you report
259
+ on a session rather than describing where it went.
260
+
261
+ Expect quiet stretches. QA Wolf reports a session as working and says nothing
262
+ more until the AI speaks, so several minutes with no output is the session
263
+ working, not the command hanging. `--timeout` bounds the wait; it is 30 minutes
264
+ by default.
265
+
212
266
  ## Driving a browser: the `runner` group
213
267
 
214
268
  The `runner` commands drive a live cloud browser: `launch` one, `screenshot` to
@@ -25,6 +25,14 @@ run
25
25
  `runId` in the response is canonical and can differ from the id you asked for.
26
26
  Use the returned value for follow-up calls.
27
27
 
28
+ `run get` resolves platform runs only. A run id printed by `qawolf runner run`
29
+ belongs to that runner, so `run get` answers exit `8` and no such run on this
30
+ team. Read one of those with `qawolf runner events run-status --run <id>`.
31
+
32
+ A run that has been requested but not yet created answers exit `8` too, and
33
+ says it is still being created. That one clears on its own, so read the message
34
+ rather than the code before deciding whether to poll.
35
+
28
36
  Poll `status` until it reaches `passed`, `failed` or `canceled`. The other
29
37
  values mean the run is still going.
30
38
 
@@ -13,6 +13,8 @@ of starting and billing a second one, and the answer says which happened: read
13
13
  cheap and safe pattern, and the same id with a different `--name` is refused
14
14
  rather than silently ignored.
15
15
 
16
+ Either answer carries a `url`, which `qawolf runner launch` prints, as does a command that launched its own runner. It is a QA Wolf page showing what the runner is doing, where a person can also take over with their own mouse and keyboard. Hand it to a person who asks what your runner is up to. The page opens for anyone on the runner's team, however the runner was launched. There is nothing to see until the runner's first run starts its screen, so the page waits until then. You read the screen with `screenshot`, not with the page.
17
+
16
18
  Commands that target a runner find one in this order: `--runner`, then
17
19
  `QAWOLF_RUNNER_ID`, then the runner stored for the current directory (which
18
20
  `qawolf runner launch` sets). Setting the environment variable once is the most
@@ -28,8 +30,8 @@ then launched without `--id` ends up with a pod it is not addressing. Pass
28
30
  And a runner id that is set is treated as found, whether or not anything is
29
31
  running under it. So exporting `QAWOLF_RUNNER_ID=agent-1` turns off the
30
32
  auto-launch described next: instead of starting `agent-1`, commands try to reach
31
- it and fail with exit code `4`, which reads as "retry" and never succeeds.
32
- Launch that id once yourself and the rest follows.
33
+ it and fail with exit code `8`, naming the id and saying the variable is what
34
+ chose it. Launch that id once yourself and the rest follows.
33
35
 
34
36
  And launching an id that differs from `QAWOLF_RUNNER_ID` prints a warning on
35
37
  stderr naming both ids: the variable still outranks the directory default, so
@@ -83,6 +85,12 @@ directory did not launch it, so a harness handed a runner sees it alongside the
83
85
  ones it started itself. Use the `id` column with `--runner` to address any of
84
86
  them; addressing one does not make it the default.
85
87
 
88
+ The table leaves out the page address, which beside a 63-character id outgrows a terminal. `--json` carries it as `url` on every runner:
89
+
90
+ ```sh
91
+ qawolf runner list --json | jq -r '.[] | [.id, .url] | @tsv'
92
+ ```
93
+
86
94
  ## The order that matters
87
95
 
88
96
  A freshly launched runner has no screen. The virtual desktop starts with the
@@ -111,11 +119,14 @@ and says so on stderr. Everything else on this page waits for a run.
111
119
 
112
120
  Retry on the exit code, not on the message text:
113
121
 
114
- - `4` is usually transient. The screen is up but cannot serve this instant:
115
- restarting after a display-size change, or busy with another request. Retry in
116
- a second or two — but bound the retries, because `4` also covers a runner that
117
- was reaped after inactivity, which no amount of retrying brings back. If `4`
118
- persists past a few tries, relaunch the id.
122
+ - `4` is transient. The screen is up but cannot serve this instant: restarting
123
+ after a display-size change, or busy with another request. Retry in a second
124
+ or two, and bound the retries.
125
+ - `8` means there is no such runner. It was never launched, or it was
126
+ terminated, or it idled out. Retrying never brings one back, so stop and
127
+ launch the id or name one that is running. The message says which runner was
128
+ meant and whether `--runner`, `QAWOLF_RUNNER_ID` or this directory's stored
129
+ default chose it — read that line before you pick an id to launch.
119
130
  - `2` will not clear on its own. Either nothing has run on this runner yet, so
120
131
  run a flow, or the runner has no browser at all, so launch with
121
132
  `--name playwright` instead. The message says which.
@@ -216,7 +227,8 @@ what the page shows.
216
227
  One failure covers three causes, because a runner cannot tell them apart: no
217
228
  live page, no element matching the selector, no variable under that name. All
218
229
  three exit `2` and none clears by waiting, so read the message, which carries
219
- whatever the runner said. An unreachable runner exits `4` and is worth retrying.
230
+ whatever the runner said. An unreachable runner exits `4` and is worth retrying;
231
+ a runner that is not running at all exits `8` and is not.
220
232
 
221
233
  Use `inspect` before reaching for `exec`. Reading a value through a snippet
222
234
  means printing it and then fishing it back out of the `console` stream, which is
@@ -586,7 +598,7 @@ the screen, so it is not optional even though the goal here is to drive by hand.
586
598
  export QAWOLF_API_KEY=... # the only credential
587
599
  export QAWOLF_RUNNER_ID=agent-1 # so no command below needs --runner
588
600
 
589
- qawolf runner launch --id agent-1 --json # --id, not the variable; read .alreadyRunning
601
+ qawolf runner launch --id agent-1 --json # --id, not the variable; read .alreadyRunning and .url
590
602
  qawolf runner run flows/smoke.flow.ts --follow # starts the screen; exit 1 if it failed
591
603
 
592
604
  qawolf runner act navigate --url https://example.com/login --screenshot step-1.jpg # then read step-1.jpg yourself