@revyl/verify 0.5.0 → 0.6.1
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/CHANGELOG.md +53 -3
- package/README.md +21 -19
- package/dist/api/client.js +21 -2
- package/dist/cli/init.js +11 -8
- package/dist/cli/main.js +1 -1
- package/dist/computer-session.d.ts +18 -4
- package/dist/computer-session.js +168 -17
- package/dist/session.d.ts +5 -4
- package/dist/session.js +1 -1
- package/docs/README.md +1 -1
- package/docs/agents.md +6 -6
- package/docs/api.md +20 -18
- package/docs/ci.md +3 -3
- package/docs/computers.md +73 -4
- package/docs/configuration.md +4 -4
- package/docs/end-to-end.md +2 -0
- package/docs/errors.md +29 -6
- package/docs/getting-started.md +1 -1
- package/docs/llms.txt +5 -5
- package/docs/mobile.md +18 -16
- package/docs/own-model.md +4 -4
- package/docs/web.md +10 -7
- package/package.json +1 -1
package/docs/computers.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Mac computers
|
|
1
|
+
# Mac computers
|
|
2
2
|
|
|
3
|
-
Verify opens a Revyl cloud macOS computer for commands, files, processes, and
|
|
3
|
+
Verify opens a Revyl cloud macOS computer for commands, files, processes, screenshots, and clicks and keys on its screen. It does not open an assigned physical computer. Mac computers are available to organizations on a current paid Revyl plan. If yours can't start one, ask [support@revyl.ai](mailto:support@revyl.ai).
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
export REVYL_API_KEY=...
|
|
@@ -47,6 +47,75 @@ Paths are relative to the computer's workspace: no leading slash, empty componen
|
|
|
47
47
|
|
|
48
48
|
Operation `timeout` is milliseconds, defaults to 30,000, and must be an integer from 1 to 300,000. It is also bounded by the run's remaining lifetime. A computer has a hard lifetime **from create** (two hours today; `expiresAt` is authoritative), not from readiness or last activity; `idleTimeoutSeconds` and the agent `stepTimeout` do not configure computer lifetime or commands. `expiresAt` is a `Date` when the API returns `expires_at`, and otherwise undefined (including on devices and older APIs). Verify refuses new operations after that deadline and bounds operation/screenshot observation by it; expiry does not prevent explicit close or recording retrieval. Revyl allows at most 256 operations per run.
|
|
49
49
|
|
|
50
|
+
## Screen
|
|
51
|
+
|
|
52
|
+
**Early access**, since TypeScript 0.6.0, Python 0.4.0 and Go v0.5.0. A script can click, type and press keys on the computer's screen, at points it reads off `screenshot()` or at elements it describes:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { open } from '@revyl/verify';
|
|
56
|
+
|
|
57
|
+
const computer = await open({ platform: 'mac', startTimeout: 600_000 });
|
|
58
|
+
try {
|
|
59
|
+
await computer.writeFile('notes.txt', 'Notes\n');
|
|
60
|
+
await computer.exec(['open', '-a', 'TextEdit', 'notes.txt']); // exec runs in the workspace
|
|
61
|
+
await new Promise((resolve) => setTimeout(resolve, 3_000)); // let the window open
|
|
62
|
+
await computer.screenshot({ path: '.verify/mac.png' }); // open it and find the text
|
|
63
|
+
await computer.tap({ x: 400, y: 300 }); // the point as you read it
|
|
64
|
+
await computer.typeFocused('First line\nSecond line', { clear: false });
|
|
65
|
+
await computer.pressKey('a', { modifiers: ['meta'] }); // Command+A selects all
|
|
66
|
+
await computer.typeFocused('Replacement', { clear: false }); // typing replaces the selection
|
|
67
|
+
await computer.screenshot({ path: '.verify/mac-after.png' });
|
|
68
|
+
} finally {
|
|
69
|
+
await computer.close();
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
| Call | What it does |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| `screenshot({ path? })` | A PNG of the whole screen. Its pixels are the coordinates the other calls take |
|
|
76
|
+
| `screenSize()` | The screenshot's `{ width, height }`: x is 0 to width - 1, y is 0 to height - 1 |
|
|
77
|
+
| `tap({ x, y })` | Clicks the point with the left button and resolves with it. Whole numbers from the top-left corner of the screenshot |
|
|
78
|
+
| `tap({ target })` | Revyl finds the described element on a fresh screenshot, clicks it, and resolves with the point it clicked, in screenshot pixels. The description is 1 to 500 characters. Takes several seconds |
|
|
79
|
+
| `typeFocused(text, { clear: false })` | Types the text where the cursor is, in the app that has focus. `{ clear: false }` is required, because Verify doesn't clear anything first: to replace text, select it with `pressKey('a', { modifiers: ['meta'] })` and type over it. Up to 16,384 characters, including line breaks (`\n`) and tabs (`\t`); other control characters are refused before anything is typed. Long text goes in pieces of up to 500 characters, in order |
|
|
80
|
+
| `pressKey(key, { modifiers })` | Presses a key: `'enter'`, `'backspace'`, `'tab'`, `'escape'`, `'delete'`, `'space'`, `'arrowup'`, `'arrowdown'`, `'arrowleft'`, `'arrowright'`, `'home'`, `'end'`, `'pageup'` or `'pagedown'`, with up to four `modifiers`: `'meta'` (Command), `'ctrl'`, `'alt'` (Option) and `'shift'`. With modifiers a lowercase letter or digit works too, so Command+A is `pressKey('a', { modifiers: ['meta'] })`. On Web the shortcut key is `'ctrl'` instead |
|
|
81
|
+
|
|
82
|
+
`locate()` isn't available on computers, because Revyl can't find a point there without clicking it; `tap({ target })` returns the point it clicked instead. `doubleTap`, `longPress`, `type(location, text)`, `swipe`, `drag`, `back`, `goHome`, `openUrl` and `launchApp` aren't available on computers either, and a description works only in `tap`. They throw `NOT_AVAILABLE_ON_PLATFORM` before anything is sent; open apps and files with `exec()`, such as `exec(['open', '-a', 'TextEdit', 'notes.txt'])`. For TextEdit, open a file rather than the bare app: opened without a file, it may show a file picker instead of a document.
|
|
83
|
+
|
|
84
|
+
### Described clicks
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const point = await computer.tap({ target: 'the Help menu in the menu bar' }); // several seconds
|
|
88
|
+
await computer.screenshot({ path: '.verify/help.png' }); // check what it clicked
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
A described click takes several seconds, because Revyl takes a screenshot and finds the element first. It can pick a look-alike, and when the target isn't on the screen it can still click something else instead of failing. So check the point it returns or take a screenshot afterwards, and for anything that's hard to undo, read the point off `screenshot()` and use `tap({ x, y })`.
|
|
92
|
+
|
|
93
|
+
### Behaviour and errors
|
|
94
|
+
|
|
95
|
+
Each call returns when Revyl has accepted the action, not when the app has finished reacting, so take a screenshot before you rely on what changed. Verify never repeats a screen action, with one exception: when Revyl answers `screen_starting`, nothing happened, so Verify waits as long as Revyl asks and tries again, for up to 30 seconds. Window positions and the desktop size aren't fixed, so for a script that runs unattended, run it once up to the screenshot, read the points off that image, and then write them into the script.
|
|
96
|
+
|
|
97
|
+
A script that controls the screen keeps control for 120 seconds after its last action. During that time a teammate can't take control of the screen in the Revyl viewer. The other way round, while someone controls the screen in the viewer, the script's actions throw `SESSION_BUSY`.
|
|
98
|
+
|
|
99
|
+
| What happened | Error | What to do |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| A point past the screen's edge (`screen_point_outside`), or a negative or fractional one | `INVALID_ARGUMENT`, and nothing was clicked | The message names the valid range |
|
|
102
|
+
| `typeFocused` without `{ clear: false }`, with control characters other than `\n` and `\t`, or with more than 16,384 characters | `INVALID_ARGUMENT` before anything is sent | The message says how to fix the call |
|
|
103
|
+
| The computer can't type some of the characters (`screen_text_unsupported`) | `INVALID_ARGUMENT`, and nothing was typed | Type those characters another way, such as writing a file with `writeFile` |
|
|
104
|
+
| Someone controls the screen in the viewer (`screen_control_held`) | `SESSION_BUSY`, and nothing happened | Try again after they release it; control frees itself after 2 minutes without input |
|
|
105
|
+
| Control of the screen changed hands before the action was sent (`screen_control_lost`) | `SESSION_BUSY`, and nothing happened | Try again; if someone controls the screen in the viewer, wait until they release it |
|
|
106
|
+
| The computer refused a click at a point (`invalid_request` on `tap({ x, y })`), as when the point is no longer on its screen after its display size changed | `INVALID_ARGUMENT`, and nothing was clicked | Take a new screenshot and pick the point from it |
|
|
107
|
+
| The computer refused typing or a key (`invalid_request`) | `INVALID_ARGUMENT`; part of it may have run | Take a `screenshot()` before you try again |
|
|
108
|
+
| The computer can't press that key (`screen_key_unsupported`) | `INVALID_ARGUMENT`, and nothing was pressed | Use a key `pressKey` lists, or `typeFocused` for a printable character |
|
|
109
|
+
| The screen is still starting (`screen_starting`) for more than 30 seconds | `SESSION_BUSY`, and nothing happened | Try again after the number of seconds the message names |
|
|
110
|
+
| Revyl didn't find the described target (`screen_target_not_found`) | `ACTION_FAILED`, and nothing was clicked | Take a screenshot and pass a point to `tap({ x, y })`, or describe the element differently |
|
|
111
|
+
| Revyl couldn't look the target up just then (`screen_target_unavailable`) | `API_ERROR`, and nothing was clicked | It is safe to try again |
|
|
112
|
+
| The backend doesn't have described clicks yet (`invalid_request` on a described tap) | `NOT_AVAILABLE_ON_PLATFORM`, and nothing was clicked | Pass a point to `tap({ x, y })` |
|
|
113
|
+
| Revyl can't confirm the action (`screen_input_uncertain`, `screen_unavailable`, `screen_input_unavailable` or `owner_unavailable`, any other HTTP 5xx, or no answer at all) | `ACTION_UNCERTAIN` | It may have happened. Take a `screenshot()` before you do it again |
|
|
114
|
+
| The API key may not control the screen (HTTP 403) | `AUTH_REJECTED` | The key's owner needs the Member role or above. CI runner keys can't control the screen yet |
|
|
115
|
+
| Revyl couldn't check the key (HTTP 503 `authentication_unavailable`) | `API_ERROR`, and nothing happened | It is safe to try again |
|
|
116
|
+
|
|
117
|
+
When long text fails part way, the error says how many characters were certainly typed and whether the piece that failed may have been typed, counting characters as Unicode code points, so an emoji is one. Every other refusal names its reason and what to do next, such as starting a new computer after `run_closed`.
|
|
118
|
+
|
|
50
119
|
## Recording metadata
|
|
51
120
|
|
|
52
121
|
After terminal state **and** confirmed release, `await computer.recording()` returns `{ url, durationSeconds?, startedAt? }` or `null` if the recording is unavailable or still processing. `durationSeconds` is a finite nonnegative number; `startedAt` is a `Date`. Missing or nullable optional fields are omitted. Calling before terminal state and confirmed release throws `SESSION_BUSY`; Verify does not wait for the recording or download it. The method works after `close()` and checks the report before requesting the recording.
|
|
@@ -55,11 +124,11 @@ The HTTPS URL is signed and short-lived. Keep it private, including its query st
|
|
|
55
124
|
|
|
56
125
|
## Availability and errors
|
|
57
126
|
|
|
58
|
-
Mac computers are
|
|
127
|
+
Mac computers are available to organizations on a current paid Revyl plan. A `503 runtime_unavailable` or typed `computer_not_enabled` becomes `NOT_AVAILABLE_ON_PLATFORM` with this availability guidance. A `429 computer_limit` becomes `COMPUTER_LIMIT`: queued work (including iOS) or the concurrency limit can cause it, and the response does not identify which. Verify does not retry the create or send a stop after these definite admission refusals. `operation_limit` remains `SESSION_BUSY`. Availability is determined by the server.
|
|
59
128
|
|
|
60
129
|
The default `startTimeout` remains 600000 ms; override it explicitly if needed. An unclaimed start can transition to `failed` with `failure_reason: 'no_host'` after 120 seconds from create. Verify stops readiness polling and throws `SESSION_START_FAILED` with that context, then attempts bounded cleanup. This is not a 120-second timeout for every start: claimed guests may take longer. `device_limit` also yields `SESSION_START_FAILED`, with queued-work/concurrency context. A start timeout or unconfirmed close does not prove resources were released. Inspect the session and contact support rather than blindly repeating a start or stop.
|
|
61
130
|
|
|
62
|
-
`instruction()` and `validation()` throw `NOT_AVAILABLE_ON_PLATFORM` on computers without sending agent requests. Screenshots are supported, but screenshot readiness is separate from command readiness.
|
|
131
|
+
`instruction()` and `validation()` throw `NOT_AVAILABLE_ON_PLATFORM` on computers without sending agent requests. Screenshots are supported, but screenshot readiness is separate from command readiness. For the screen, use the calls in [Screen](#screen). On web, iOS, and Android, shell/file/process methods throw `NOT_AVAILABLE_ON_PLATFORM`.
|
|
63
132
|
|
|
64
133
|
For the first 30 seconds after a computer becomes active, `screenshot()` retries exact structured `screen_unavailable` responses (HTTP 409 or 503) and `screen_starting` (HTTP 409). Backoff starts at 500 ms and doubles up to 2 seconds; the fixed window does not restart on each call. Cancellation, close, expiry, and the observation deadline still bound the wait. Exhausting that window yields `SESSION_BUSY` with a desktop-not-ready message, rather than requiring a caller sleep. Authentication failures, unrelated errors, malformed responses, and transport failures are not replayed. Commands and other mutations are never retried by this screenshot behavior.
|
|
65
134
|
|
package/docs/configuration.md
CHANGED
|
@@ -8,8 +8,8 @@ import { defineConfig } from '@revyl/verify';
|
|
|
8
8
|
export default defineConfig({
|
|
9
9
|
targets: {
|
|
10
10
|
web: { platform: 'web', app: { appId: '…', url: 'https://example.com' }, viewport: { width: 1280, height: 800 } },
|
|
11
|
-
ios: { platform: 'ios', app: { appId: '…' }, device: { model: 'iPhone 16', osVersion: '18.5' } },
|
|
12
|
-
pixel: { platform: 'android', app: { buildId: '…' }, device: { model: 'Pixel
|
|
11
|
+
ios: { platform: 'ios', app: { appId: '…' }, device: { model: 'iPhone 16', osVersion: 'iOS 18.5' } },
|
|
12
|
+
pixel: { platform: 'android', app: { buildId: '…' }, device: { model: 'Pixel 7', osVersion: 'Android 14' } },
|
|
13
13
|
mac: { platform: 'mac' },
|
|
14
14
|
},
|
|
15
15
|
stepTimeout: 240_000,
|
|
@@ -20,7 +20,7 @@ Unknown keys are rejected, so a typo fails loudly instead of being ignored.
|
|
|
20
20
|
|
|
21
21
|
## Top-level options
|
|
22
22
|
|
|
23
|
-
Mac
|
|
23
|
+
Mac computers are available to organizations on a current paid Revyl plan. See [computer availability](computers.md#availability-and-errors).
|
|
24
24
|
|
|
25
25
|
| Option | Default | Meaning |
|
|
26
26
|
| --- | --- | --- |
|
|
@@ -50,7 +50,7 @@ Mac targets are early access. Ask support@revyl.ai about access. See [computer a
|
|
|
50
50
|
| `app.buildId` | An exact Revyl build to install. |
|
|
51
51
|
| `app.artifactUrl` | An HTTPS URL of an `.apk`, `.app.zip`, or `.ipa` to install. |
|
|
52
52
|
| `app.bundleId` | An app already on the device to launch instead, such as a system app. |
|
|
53
|
-
| `device` | `{ model?, osVersion? }`, for example `{ model: 'Pixel
|
|
53
|
+
| `device` | `{ model?, osVersion? }`, written as Revyl names them, for example `{ model: 'iPhone 16', osVersion: 'iOS 18.5' }` or `{ model: 'Pixel 7', osVersion: 'Android 14' }`. Revyl picks its default for a value you leave out. A model or OS version Revyl doesn't offer, or a pair that doesn't go together, throws `INVALID_ARGUMENT` listing the values it accepts, and no session starts |
|
|
54
54
|
| `startTimeout` | Milliseconds to wait for the device. Default 600000. |
|
|
55
55
|
| `idleTimeoutSeconds` | Default 600. |
|
|
56
56
|
|
package/docs/end-to-end.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Verify working end to end
|
|
2
2
|
|
|
3
|
+
**Historical record.** This page documents the 0.1.0 and 0.2.0 runs of October 2026 and is kept for its method. It does not describe the current release: the [changelog](https://github.com/RevylAI/verify/blob/main/CHANGELOG.md) and the guides do.
|
|
4
|
+
|
|
3
5
|
These live runs were made on **2026-10-27**. The baseline uses source commit [`6029513369f13e73914b550d0cd26ece615b4e13`](https://github.com/RevylAI/verify/tree/6029513369f13e73914b550d0cd26ece615b4e13); the separate published-package check used `@revyl/verify@0.1.0`. The [re-verification below](#re-verification-at-061e5cfa) uses `061e5cfa31c5489aa304c2b640bbaf40e3d88561`, including an unpublished packed 0.2.0 tarball. Neither is a publication announcement.
|
|
4
6
|
|
|
5
7
|
Only reviewed public scripts, sanitized transcripts, result projections, and two reviewed screenshots were used here. No private reports, device identifiers, session links, signed recording URLs, or recordings are published.
|
package/docs/errors.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
`web_runtime_unavailable` does not distinguish full capacity from an unavailable runtime. `computer_limit` does not distinguish queued work from concurrency exhaustion. Inspect these fields to classify refusal without parsing prose; do not infer a specific cause or automatically start a replacement session.
|
|
20
20
|
|
|
21
|
-
Ordinary start errors retain recognized safe server diagnostic codes such as `web_app_required` in the message even when they are not typed refusal reasons.
|
|
21
|
+
Ordinary start errors retain recognized safe server diagnostic codes such as `web_app_required` in the message even when they are not typed refusal reasons. An iOS or Android start that Revyl refuses as invalid (HTTP 400 or 422), such as a device model or OS version it doesn't offer, throws `INVALID_ARGUMENT` with Revyl's explanation, which lists the values it accepts; nothing was started. Other server messages and payloads, including every Web and hosted start's, are not copied into startup diagnostics.
|
|
22
22
|
|
|
23
23
|
Web navigation timeouts expose `reason: 'request_timeout'` with HTTP 504 and `code: 'ACTION_UNCERTAIN'`: the action budget expired before navigation was confirmed, but the request may still commit and the session remains open. A normalized `protocol_error` also preserves uncertainty and its HTTP status. Neither error causes Verify to replay navigation or close the session. Inspect the live page before deciding whether to navigate again.
|
|
24
24
|
|
|
@@ -37,23 +37,23 @@ try {
|
|
|
37
37
|
|
|
38
38
|
| Code | What happened | What to do |
|
|
39
39
|
| --- | --- | --- |
|
|
40
|
-
| `INVALID_ARGUMENT` | A call got a bad value: an unknown target name, an empty step, two steps at once on one session, a point outside the screenshot | The message names the argument |
|
|
40
|
+
| `INVALID_ARGUMENT` | A call got a bad value: an unknown target name, an empty step, two steps at once on one session, a point outside the screenshot, `typeFocused` on a computer without `{ clear: false }`, or a device model or OS version Revyl doesn't offer | The message names the argument, and for a device, the values Revyl accepts |
|
|
41
41
|
| `CONFIG_NOT_FOUND` | `open('name')` found no `verify.config.ts` at or above the current folder | Run `npx @revyl/verify init`, pass `options.config`, or pass a target object |
|
|
42
42
|
| `CONFIG_INVALID` | The config failed validation or did not load | The message names the exact option and the rule |
|
|
43
43
|
| `AUTH_MISSING` | No `REVYL_API_KEY` and no saved login | Run `npx @revyl/verify login` in a terminal, or set `REVYL_API_KEY` |
|
|
44
|
-
| `AUTH_REJECTED` | Revyl rejected the credentials | Create a new API key, or log in again |
|
|
44
|
+
| `AUTH_REJECTED` | Revyl rejected the credentials, or refused a computer screen action with HTTP 403 | Create a new API key, or log in again. For a screen action, the key's owner needs the Member role or above, and CI runner keys can't control the screen yet |
|
|
45
45
|
| `EDGE_BLOCKED` | An HTTP 403 has a `text/html` content type and nonempty `cf-ray`, without a valid JSON body | Check network access or contact support; Verify does not retry the request |
|
|
46
46
|
| `API_ERROR` | Revyl returned an unexpected response | Try again; after an exact action, check the session first, since the action may have run. If it persists, report it with the session link |
|
|
47
47
|
| `SESSION_START_FAILED` | The browser, device, or computer did not start | Computer `no_host` reports the 120-second unclaimed-start context; `device_limit` reports queued-work/concurrency context. Inspect the session and cleanup before starting again. |
|
|
48
48
|
| `SESSION_ENDED` | The session was closed, stopped, or expired, or Revyl answered 404 for the session itself | Close explicitly; computer expiry does not prove release |
|
|
49
|
-
| `SESSION_BUSY` | A step conflict, computer operation limit, or recording requested before terminal state and confirmed release | Inspect the state before another call; a terminal computer alone does not prove release |
|
|
49
|
+
| `SESSION_BUSY` | A step conflict, computer operation limit, someone controlling a computer's screen in the Revyl viewer, or recording requested before terminal state and confirmed release | Inspect the state before another call; a terminal computer alone does not prove release |
|
|
50
50
|
| `COMPUTER_LIMIT` | Computer admission was refused due to queued work (including iOS) or the concurrency limit | Wait for capacity; Verify does not retry or stop a definitely refused start |
|
|
51
51
|
| `NOT_AVAILABLE_ON_PLATFORM` | The call is unavailable: computer agent steps, device shell/file/recording methods, an exact action the platform lacks (such as `back()` on iOS, `goHome()` or `launchApp()` on Web, `typeFocused()` or keys other than Enter and Backspace on iOS and Android), computer access or runtime unavailable, or a backend without web steps | See [web.md](web.md) and [computers.md](computers.md#availability-and-errors) |
|
|
52
52
|
| `COMMAND_FAILED` | A computer command exited nonzero | Read its reason; use `exec(argv, { check: false })` to inspect the exit code and output instead |
|
|
53
53
|
| `NOT_FOUND` | A computer file or resource was not found | Check the workspace-relative path |
|
|
54
54
|
| `ACCESS_DENIED` | The computer refused access | Check permissions; do not assume host/root access |
|
|
55
55
|
| `ACTION_FAILED` | Revyl refused or failed a request on the session | Read the reason in the message |
|
|
56
|
-
| `ACTION_UNCERTAIN` | The connection failed mid-request, so the step may or may not have run | Verify does not repeat it; check the session link before deciding what to do |
|
|
56
|
+
| `ACTION_UNCERTAIN` | The connection failed mid-request, or Revyl couldn't confirm a computer screen action, so the step may or may not have run | Verify does not repeat it; check the session link before deciding what to do |
|
|
57
57
|
| `INSTRUCTION_FAILED` | Revyl's agent could not do what the instruction asked | Make the instruction more specific, or split it into smaller steps |
|
|
58
58
|
| `VALIDATION_FAILED` | The validation did not hold | The message has Revyl's reason; watch the recording at the session link |
|
|
59
59
|
| `STEP_FAILED` | Revyl could not run the step to an outcome: it errored, it was cancelled on Revyl, a validation came back without a verdict, or Revyl accepted the step but still answered "Live step not found" about 10 seconds later (`httpStatus` 404). This says nothing about your app, and `soft` does not cover it. Verify leaves the session open. | Watch the session at the link. After "Live step not found", the step may still be running, so wait for it to finish (another step meanwhile gets `SESSION_BUSY`), then run the step again |
|
|
@@ -65,9 +65,32 @@ try {
|
|
|
65
65
|
|
|
66
66
|
Computer operations use `TIMEOUT` for bounded runtime with confirmed termination, and `ACTION_UNCERTAIN` when termination, execution, or cleanup is unconfirmed. An observed successful operation can return its result while logging pending/failed evidence. Timeout never proves rollback, and failed evidence does not prove the command never ran. Never automatically retry a mutation with a fresh request id; inspect the computer first. See [computers.md](computers.md).
|
|
67
67
|
|
|
68
|
+
### Computer screen actions
|
|
69
|
+
|
|
70
|
+
Clicks, typing and keys on a Mac computer's screen map Revyl's answers to these codes, read from the answer's `detail`, never from its HTTP status alone. Verify never repeats a screen action, except after `screen_starting`, which it tries again for up to 30 seconds. See [computers.md](computers.md#screen).
|
|
71
|
+
|
|
72
|
+
| Revyl's answer | `code` | What it means |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| 409 `screen_control_held` | `SESSION_BUSY` | Nothing happened: someone controls the screen in the viewer. Try again after they release it; control frees itself after 2 minutes without input |
|
|
75
|
+
| 409 `screen_control_lost` | `SESSION_BUSY` | Nothing happened: control of the screen changed hands before the action was sent. Try again; if someone controls the screen in the viewer, wait until they release it |
|
|
76
|
+
| 409 `screen_starting` for more than 30 seconds | `SESSION_BUSY` | Nothing happened: the screen is still starting. Try again after the number of seconds the message names |
|
|
77
|
+
| 422 `screen_target_not_found` | `ACTION_FAILED` | Nothing was clicked: Revyl didn't find the described target. A target that isn't there can also be clicked as a look-alike, so check the point `tap({ target })` returns |
|
|
78
|
+
| 503 `screen_target_unavailable` | `API_ERROR` | Nothing was clicked: Revyl couldn't look the target up just then. It is safe to try again |
|
|
79
|
+
| 422 `invalid_request` on a described tap | `NOT_AVAILABLE_ON_PLATFORM` | Nothing was clicked: this backend doesn't have described clicks yet. Pass a point |
|
|
80
|
+
| 422 `screen_point_outside` | `INVALID_ARGUMENT` | Nothing was clicked: the point is past the screen's edge. The message names the valid range |
|
|
81
|
+
| 422 `invalid_request` on a tap at a point | `INVALID_ARGUMENT` | Nothing was clicked: the computer refused the point, as when it is no longer on the screen after the display size changed. Take a new `screenshot()` and pick the point from it |
|
|
82
|
+
| 422 `invalid_request` on typing or a key | `INVALID_ARGUMENT` | Part of the action may have run. Take a `screenshot()` before you try again |
|
|
83
|
+
| 422 `screen_key_unsupported` | `INVALID_ARGUMENT` | Nothing was pressed: the computer can't press that key. Use a key `pressKey` lists, or `typeFocused` for a printable character |
|
|
84
|
+
| `screen_text_unsupported` | `INVALID_ARGUMENT` | Nothing was typed: the computer can't type some of the characters |
|
|
85
|
+
| `screen_input_uncertain`, `screen_unavailable`, `screen_input_unavailable` or `owner_unavailable` on an action (409 or 503), any other 5xx, or no answer at all | `ACTION_UNCERTAIN` | The action may have happened. Take a `screenshot()` before you do it again |
|
|
86
|
+
| 403 | `AUTH_REJECTED` | The key's owner needs the Member role or above; CI runner keys can't control the screen yet |
|
|
87
|
+
| 503 `authentication_unavailable` | `API_ERROR` | Nothing happened: Revyl couldn't check the key. It is safe to try again |
|
|
88
|
+
|
|
89
|
+
Any other refusal of a computer request names its reason and what to do next: `run_closed` (`SESSION_ENDED`) says to start a new computer, `inaccessible` (`NOT_FOUND`) says to check the id and the key's organization, a 401 or 403 (`AUTH_REJECTED`) says to check the key and its owner's role, and an answer Verify can't classify (`API_ERROR`) says to try again.
|
|
90
|
+
|
|
68
91
|
## Troubleshooting
|
|
69
92
|
|
|
70
|
-
- **`NOT_AVAILABLE_ON_PLATFORM`:** the requested method is not available for that target. Computer targets do not run instruction or validation steps
|
|
93
|
+
- **`NOT_AVAILABLE_ON_PLATFORM`:** the requested method is not available for that target. Computer targets do not run instruction or validation steps or `locate()`; Web and mobile targets do not expose computer commands or files.
|
|
71
94
|
- **`COMPUTER_LIMIT`:** computer admission was refused because of queued work or a concurrency limit. Verify does not retry creation or stop a definitely refused start.
|
|
72
95
|
- **Blank first screen:** mobile startup uses a bounded, best-effort blank-frame observation, not a rendered-readiness guarantee. Begin with an instruction that reaches the screen your workflow needs. See [mobile.md](mobile.md#steps).
|
|
73
96
|
- **A validation report looks successful but the assertion failed:** inspect each step's `effective_status` and `validation_result`, and compare `validations_passed` with `total_validations` rather than transport counters. See [reading the verdict](end-to-end.md#reading-the-verdict).
|
package/docs/getting-started.md
CHANGED
|
@@ -86,7 +86,7 @@ The generated iOS and Android targets open Settings. To test your own app, repla
|
|
|
86
86
|
|
|
87
87
|
- **Web:** create a Web app in Revyl (see [web.md](web.md#create-the-web-app)) and set `app.url` to a public HTTPS URL. Revyl's cloud browser cannot reach `localhost`.
|
|
88
88
|
- **iOS and Android:** upload a build to the app in Revyl, for example with `revyl build upload`; each session installs the newest one. See [mobile.md](mobile.md).
|
|
89
|
-
- **Mac:** computers are
|
|
89
|
+
- **Mac:** computers are available to organizations on a current paid Revyl plan. Agent instruction/validation are unavailable. See [computers.md](computers.md#availability-and-errors).
|
|
90
90
|
|
|
91
91
|
## Run the script
|
|
92
92
|
|
package/docs/llms.txt
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# Verify
|
|
2
2
|
|
|
3
|
-
> Verify (@revyl/verify) is an open-source TypeScript library for Revyl. It opens cloud browsers and iOS/Android devices for agent instruction/validation steps, or Mac computers for commands, files, processes, screenshots, and optional private recording metadata after release. Call it from a plain script, coding agent, CI, or any test runner. It has no test runner of its own.
|
|
3
|
+
> Verify (@revyl/verify) is an open-source TypeScript library for Revyl. It opens cloud browsers and iOS/Android devices for agent instruction/validation steps, or Mac computers for commands, files, processes, screenshots, clicks and keys on the screen, and optional private recording metadata after release. Call it from a plain script, coding agent, CI, or any test runner. It has no test runner of its own.
|
|
4
4
|
|
|
5
5
|
## Docs
|
|
6
6
|
|
|
7
|
-
Mac computers are
|
|
7
|
+
Mac computers are available to organizations on a current paid Revyl plan. See computers.md#availability-and-errors.
|
|
8
8
|
|
|
9
9
|
- [Getting started](getting-started.md): install, authenticate, init, run a script
|
|
10
10
|
- [API](api.md): open(), attach(), hosted children, navigate(), report(), agent steps, screenshot(), exec(), file/process methods, close()
|
|
11
11
|
- [Configuration](configuration.md): verify.config.ts targets, environment variables, the init and whoami commands
|
|
12
12
|
- [Web](web.md): Revyl web sessions, URLs and builds, sign-in, steps, and exact actions
|
|
13
13
|
- [Mobile](mobile.md): iOS and Android devices, installing apps, and exact actions
|
|
14
|
-
- [Computers](computers.md#availability-and-errors): Mac
|
|
14
|
+
- [Computers](computers.md#availability-and-errors): Mac availability, commands and files, the screen (clicks, typing and keys), expiry, capacity errors, recording metadata, and confirmed release
|
|
15
15
|
- [Verify working end to end](end-to-end.md): live verification scope, observed outcomes, and limits of the evidence
|
|
16
16
|
- [CI and test runners](ci.md): GitHub Actions, node:test, Vitest, Jest, Playwright Test
|
|
17
17
|
- [Use your own model](own-model.md): run instructions and validations on Claude, GPT, Gemini, a local model or any OpenAI-compatible endpoint; providers, limits, privacy and troubleshooting
|
|
@@ -20,6 +20,6 @@ Mac computers are early access; ask support@revyl.ai about access. See computers
|
|
|
20
20
|
|
|
21
21
|
## Exact actions and coordinates
|
|
22
22
|
|
|
23
|
-
Prefer a description, such as `tap({ target: 'Sign in button' })`: Revyl finds the element on the current screen, and you never handle coordinates. Otherwise a point is `{ x, y }` in screenshot pixels on every platform: whole numbers from the top-left corner of the image `screenshot()` returns. So a position you read off a screenshot goes to `tap()`, `type()`, `swipe()` and every other exact action unchanged. `screenSize()` returns the screenshot's width and height, and `locate()` and the points `tap()`, `doubleTap()`, `longPress()` and `type()` return use the same pixels. A point outside the screenshot is refused with `INVALID_ARGUMENT` before any action is sent, and the message names the valid range. On iOS, Verify converts the pixels to the screen's points for you. On Web they equal CSS pixels at the default device scale factor of 1, and you still pass screenshot pixels at any other. This holds from TypeScript 0.4.1, Python 0.2.1 and Go v0.3.1; earlier versions took iOS points. See [For coding agents](agents.md#exact-actions-and-coordinates) for a runnable example.
|
|
23
|
+
Prefer a description, such as `tap({ target: 'Sign in button' })`: Revyl finds the element on the current screen, and you never handle coordinates. Otherwise a point is `{ x, y }` in screenshot pixels on every platform: whole numbers from the top-left corner of the image `screenshot()` returns. So a position you read off a screenshot goes to `tap()`, `type()`, `swipe()` and every other exact action unchanged. `screenSize()` returns the screenshot's width and height, and `locate()` and the points `tap()`, `doubleTap()`, `longPress()` and `type()` return use the same pixels. A point outside the screenshot is refused with `INVALID_ARGUMENT` before any action is sent, and the message names the valid range. On iOS, Verify converts the pixels to the screen's points for you. On Web they equal CSS pixels at the default device scale factor of 1, and you still pass screenshot pixels at any other. This holds from TypeScript 0.4.1, Python 0.2.1 and Go v0.3.1; earlier versions took iOS points. On a Mac computer a point is in screenshot pixels too, and `tap({ target })` returns the point it clicked in the same pixels. Verify refuses a negative or fractional point before sending it, and Revyl refuses one past the screen's edge without clicking; both throw `INVALID_ARGUMENT` naming the valid range. See [For coding agents](agents.md#exact-actions-and-coordinates) for a runnable example.
|
|
24
24
|
|
|
25
|
-
Exact actions are `tap`, `doubleTap`, `longPress`, `type`, `swipe`, `drag`, `pressKey`, `locate` and `screenSize` on Web, iOS and Android. Web adds `typeFocused(text)`, which types into the field that has focus, and `pressKey` keys such as `'escape'`, `'tab'` and arrows with `modifiers`. iOS and Android add `goHome()`; their `pressKey` takes only `'enter'` and `'backspace'`, and you type with `type(location, text)`. A call the platform lacks throws `NOT_AVAILABLE_ON_PLATFORM` before anything is sent, naming what to use instead. See [api.md](api.md#exact-actions) for every call.
|
|
25
|
+
Exact actions are `tap`, `doubleTap`, `longPress`, `type`, `swipe`, `drag`, `pressKey`, `locate` and `screenSize` on Web, iOS and Android. Web adds `typeFocused(text)`, which types into the field that has focus, and `pressKey` keys such as `'escape'`, `'tab'` and arrows with `modifiers`. iOS and Android add `goHome()`; their `pressKey` takes only `'enter'` and `'backspace'`, and you type with `type(location, text)`. A Mac computer's screen takes `tap({ x, y })`, `tap({ target })` (which returns the point it clicked; check it, because grounding can click a look-alike or something else when the target isn't there), `screenSize()`, `typeFocused(text, { clear: false })` and `pressKey` with the Web keys, where `'meta'` is Command: select all with `pressKey('a', { modifiers: ['meta'] })` there and `pressKey('a', { modifiers: ['ctrl'] })` on Web. See [computers.md](computers.md#screen). A call the platform lacks throws `NOT_AVAILABLE_ON_PLATFORM` before anything is sent, naming what to use instead. See [api.md](api.md#exact-actions) for every call.
|
package/docs/mobile.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
An `ios` or `android` target opens your app on a Revyl cloud simulator or emulator.
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
|
-
{ platform: 'ios', app: { appId: '…' }, device: { model: 'iPhone 16', osVersion: '18.5' } }
|
|
7
|
-
{ platform: 'android', app: { buildId: '…' }, device: { model: 'Pixel
|
|
6
|
+
{ platform: 'ios', app: { appId: '…' }, device: { model: 'iPhone 16', osVersion: 'iOS 18.5' } }
|
|
7
|
+
{ platform: 'android', app: { buildId: '…' }, device: { model: 'Pixel 7', osVersion: 'Android 14' } }
|
|
8
8
|
{ platform: 'android', app: { artifactUrl: 'https://ci.example.com/app-release.apk' } }
|
|
9
9
|
{ platform: 'ios', app: { bundleId: 'com.apple.Preferences' } } // an app already on the device
|
|
10
10
|
```
|
|
@@ -18,17 +18,17 @@ An `ios` or `android` target opens your app on a Revyl cloud simulator or emulat
|
|
|
18
18
|
|
|
19
19
|
## Devices
|
|
20
20
|
|
|
21
|
-
`device.model` and `device.osVersion` pick the simulator or emulator. Without them, Revyl picks its default for the platform. To run the same script on several devices, give each its own target and open them one after another or in parallel:
|
|
21
|
+
`device.model` and `device.osVersion` pick the simulator or emulator. Write them as Revyl names them: `'iOS 18.5'` and `'Android 14'`, not `'18.5'` or `'14'`. Without them, Revyl picks its default for the platform, and with only a model, its default OS version, which that model must support. A value Revyl doesn't offer throws `INVALID_ARGUMENT` before any session starts, and the message lists the models or versions it accepts. To run the same script on several devices, give each its own target and open them one after another or in parallel:
|
|
22
22
|
|
|
23
23
|
```ts
|
|
24
24
|
targets: {
|
|
25
|
-
'iphone-
|
|
26
|
-
'iphone-16': { platform: 'ios', app: { appId }, device: { model: 'iPhone 16' } },
|
|
25
|
+
'iphone-air': { platform: 'ios', app: { appId }, device: { model: 'iPhone Air' } },
|
|
26
|
+
'iphone-16': { platform: 'ios', app: { appId }, device: { model: 'iPhone 16', osVersion: 'iOS 18.5' } },
|
|
27
27
|
},
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
|
-
for (const name of ['iphone-
|
|
31
|
+
for (const name of ['iphone-air', 'iphone-16']) {
|
|
32
32
|
const session = await open(name);
|
|
33
33
|
try {
|
|
34
34
|
await session.validation('The onboarding screen fits without overlapping text');
|
|
@@ -46,11 +46,11 @@ After connection and any explicit bundle launch, `open()` makes a best-effort fi
|
|
|
46
46
|
|
|
47
47
|
The generated mobile example starts with an instruction to wait for the first screen before validating it. For your own scripts, make the first instruction describe the screen or action you actually need; a screenshot with content can still show a loading screen or another app.
|
|
48
48
|
|
|
49
|
-
For Android Settings, instruct the agent to scroll or navigate until the OS version is visible before validating it
|
|
49
|
+
For Android Settings (`bundleId: 'com.android.settings'`), instruct the agent to scroll or navigate until the OS version is visible before validating it, as in `instruction('Open About phone, then scroll or navigate until the Android version is visible')`. Opening About alone can leave the version offscreen.
|
|
50
50
|
|
|
51
51
|
## Exact actions
|
|
52
52
|
|
|
53
|
-
**Early access.** Since TypeScript (`@revyl/verify`) 0.4.0, Python (`revyl-verify`) 0.2.0 and Go v0.3.0. `doubleTap`, `longPress`, `drag`, `typeFocused`, `goHome`, and Web keys and modifiers in `pressKey`, are new in TypeScript 0.5.0, Python 0.3.0 and Go v0.4.0. The [Python](https://github.com/RevylAI/verify/blob/v0.
|
|
53
|
+
**Early access.** Since TypeScript (`@revyl/verify`) 0.4.0, Python (`revyl-verify`) 0.2.0 and Go v0.3.0. `doubleTap`, `longPress`, `drag`, `typeFocused`, `goHome`, and Web keys and modifiers in `pressKey`, are new in TypeScript 0.5.0, Python 0.3.0 and Go v0.4.0. The [Python](https://github.com/RevylAI/verify/blob/v0.6.1/python/README.md#exact-actions) and [Go](https://github.com/RevylAI/verify/blob/v0.6.1/go/README.md#exact-actions) READMEs show them with each language's names. When you know exactly what to press, skip the agent. Exact actions run on the device as given, with no model, and mix freely with `instruction()` and `validation()` in one script:
|
|
54
54
|
|
|
55
55
|
```ts
|
|
56
56
|
import { open } from '@revyl/verify';
|
|
@@ -66,10 +66,10 @@ try {
|
|
|
66
66
|
await session.swipe('down'); // the finger moves down, so the list scrolls to the top
|
|
67
67
|
const search = await session.locate('Search field'); // a point in screenshot pixels
|
|
68
68
|
await session.tap(search); // a tap by coordinates
|
|
69
|
-
await session.type(search, '
|
|
69
|
+
await session.type(search, 'Accessibility');
|
|
70
70
|
await session.pressKey('enter');
|
|
71
|
-
await session.instruction('Open the
|
|
72
|
-
await session.validation('The
|
|
71
|
+
await session.instruction('Open the Accessibility result'); // hand the fuzzy part to the agent
|
|
72
|
+
await session.validation('The Accessibility settings are shown');
|
|
73
73
|
} finally {
|
|
74
74
|
await session.close();
|
|
75
75
|
}
|
|
@@ -103,19 +103,21 @@ On Android, `back()` presses the system back button. On both platforms, `goHome(
|
|
|
103
103
|
|
|
104
104
|
| Call | Web | iOS | Android | Mac computer |
|
|
105
105
|
| --- | --- | --- | --- | --- |
|
|
106
|
-
| `tap
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
106
|
+
| `tap({ x, y })`, `tap({ target })`, `screenSize` | Yes | Yes | Yes | Yes |
|
|
107
|
+
| `locate` | Yes | Yes | Yes | No, use `tap({ target })`, which returns the point it clicked, or read the point off `screenshot()` |
|
|
108
|
+
| `doubleTap`, `longPress`, `type`, `swipe`, `drag` | Yes | Yes | Yes | No |
|
|
109
|
+
| `pressKey(key, { modifiers })` | Yes: `'enter'`, `'backspace'`, `'tab'`, `'escape'`, arrows and more, with modifiers | `'enter'` and `'backspace'` only | `'enter'` and `'backspace'` only | Yes: the Web keys and modifiers, where `'meta'` is Command |
|
|
110
|
+
| `typeFocused(text, { clear })` | Yes, types into the focused field | No, use `type(location, text)` | No, use `type(location, text)` | Yes, with `{ clear: false }` |
|
|
109
111
|
| `back()` | Yes, browser history | No, tap the on-screen back control | Yes, the system back button | No |
|
|
110
112
|
| `goHome()` | No, use `openUrl` or `navigate` | Yes, the home screen | Yes, the home screen | No |
|
|
111
113
|
| `openUrl(url)` | Yes, loads an http(s) URL in the tab | Yes, a URL or deep link | Yes, a URL or deep link | No |
|
|
112
114
|
| `launchApp(bundleId)` | No, use `openUrl` or `navigate` | Yes | Yes | No |
|
|
113
115
|
| `navigate(url)` | Yes | No, use `openUrl` | No, use `openUrl` | No |
|
|
114
|
-
| `instruction`, `validation`, your own model | Yes | Yes | Yes | No, use `exec()` |
|
|
116
|
+
| `instruction`, `validation`, your own model | Yes | Yes | Yes | No, use `exec()` and the screen calls above |
|
|
115
117
|
| `screenshot()` | Yes | Yes | Yes | Yes |
|
|
116
118
|
| `exec`, files, processes | No, open a Mac computer | No, open a Mac computer | No, open a Mac computer | Yes |
|
|
117
119
|
|
|
118
|
-
**Coordinates.** Prefer a description, such as `tap({ target: 'Sign in button' })`: Revyl finds the element on the current screen, and you never handle coordinates. Otherwise a point is `{ x, y }` in screenshot pixels on every platform: whole numbers from the top-left corner of the image `screenshot()` returns. So a position you read off a screenshot goes to `tap()`, `type()`, `swipe()` and every other exact action unchanged. `screenSize()` returns the screenshot's width and height, and `locate()` and the points `tap()`, `doubleTap()`, `longPress()` and `type()` return use the same pixels. A point outside the screenshot is refused with `INVALID_ARGUMENT` before any action is sent, and the message names the valid range. On iOS, Verify converts the pixels to the screen's points for you. On Web they equal CSS pixels at the default device scale factor of 1, and you still pass screenshot pixels at any other. This holds from TypeScript 0.4.1, Python 0.2.1 and Go v0.3.1; earlier versions took iOS points.
|
|
120
|
+
**Coordinates.** Prefer a description, such as `tap({ target: 'Sign in button' })`: Revyl finds the element on the current screen, and you never handle coordinates. Otherwise a point is `{ x, y }` in screenshot pixels on every platform: whole numbers from the top-left corner of the image `screenshot()` returns. So a position you read off a screenshot goes to `tap()`, `type()`, `swipe()` and every other exact action unchanged. `screenSize()` returns the screenshot's width and height, and `locate()` and the points `tap()`, `doubleTap()`, `longPress()` and `type()` return use the same pixels. A point outside the screenshot is refused with `INVALID_ARGUMENT` before any action is sent, and the message names the valid range. On iOS, Verify converts the pixels to the screen's points for you. On Web they equal CSS pixels at the default device scale factor of 1, and you still pass screenshot pixels at any other. This holds from TypeScript 0.4.1, Python 0.2.1 and Go v0.3.1; earlier versions took iOS points. On a Mac computer a point is in screenshot pixels too, and `tap({ target })` returns the point it clicked in the same pixels. Verify refuses a negative or fractional point before sending it, and Revyl refuses one past the screen's edge without clicking; both throw `INVALID_ARGUMENT` naming the valid range.
|
|
119
121
|
|
|
120
122
|
A call a platform doesn't support throws `NOT_AVAILABLE_ON_PLATFORM`, and its message names the alternative.
|
|
121
123
|
|
package/docs/own-model.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Use your own model
|
|
2
2
|
|
|
3
|
-
**Early access.** Since TypeScript (`@revyl/verify`) 0.4.0, Python (`revyl-verify`) 0.2.0 and Go v0.3.0; the [Python](https://github.com/RevylAI/verify/blob/v0.
|
|
3
|
+
**Early access.** Since TypeScript (`@revyl/verify`) 0.4.0, Python (`revyl-verify`) 0.2.0 and Go v0.3.0; the [Python](https://github.com/RevylAI/verify/blob/v0.6.1/python/README.md#your-own-model) and [Go](https://github.com/RevylAI/verify/blob/v0.6.1/go/README.md#your-own-model) READMEs show them with each language's names. Verify normally runs `instruction()` and `validation()` on Revyl's agent. Pass a `model` and the agent loop runs in your process instead, on a model you choose, with your own key. Revyl still provides the cloud browser or device and, by default, finds the elements your model names on screen.
|
|
4
4
|
|
|
5
5
|
Use it when you want a specific model, your own prompts and key, or faster steps on a cheap model. Leave `model` out and nothing changes.
|
|
6
6
|
|
|
@@ -52,7 +52,7 @@ log.Println(step.Model.ID, len(step.Model.Turns), step.Model.InputTokens, step.M
|
|
|
52
52
|
_, err = s.Validation(ctx, "The screen shows the iOS version")
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
The [Python](https://github.com/RevylAI/verify/blob/v0.
|
|
55
|
+
The [Python](https://github.com/RevylAI/verify/blob/v0.6.1/python/README.md#your-own-model) and [Go](https://github.com/RevylAI/verify/blob/v0.6.1/go/README.md#your-own-model) READMEs have the full reference for each.
|
|
56
56
|
|
|
57
57
|
Set `model` on `open()` to run every step on it, or on one step to override it:
|
|
58
58
|
|
|
@@ -67,7 +67,7 @@ The string is `'provider/model'`. Everything after the first slash is the provid
|
|
|
67
67
|
|
|
68
68
|
| Prefix | Key variable | Endpoint | Status |
|
|
69
69
|
| --- | --- | --- | --- |
|
|
70
|
-
| `anthropic/` | `ANTHROPIC_API_KEY` | Anthropic Messages API |
|
|
70
|
+
| `anthropic/` | `ANTHROPIC_API_KEY` | Anthropic Messages API | Run live with Claude Sonnet 5.5 on Revyl iOS, Android and Web sessions in TypeScript, Python and Go, including `grounding: 'model'` on iOS |
|
|
71
71
|
| `openai/` | `OPENAI_API_KEY` | `https://api.openai.com/v1` | Request shape checked; not yet run live |
|
|
72
72
|
| `google/` | `GOOGLE_API_KEY`, or `GEMINI_API_KEY` when it is unset | `https://generativelanguage.googleapis.com/v1beta/openai/` | Request shape checked; not yet run live |
|
|
73
73
|
| `openrouter/` | `OPENROUTER_API_KEY` | `https://openrouter.ai/api/v1` | Request shape checked; not yet run live |
|
|
@@ -97,7 +97,7 @@ Measured in production on Cerebras `qwen-3.8-27b`, a fast vision model (about 0.
|
|
|
97
97
|
| | your model, `grounding: 'revyl'` | pass, 21.2 s | pass, 1.3 s | 4 + 1 | 7,854/355 + 1,380/136 |
|
|
98
98
|
| | your model, `grounding: 'model'` | fail after 20 turns, 64.4 s | | 20 | 52,726 total |
|
|
99
99
|
|
|
100
|
-
Each local turn costs one model call plus one device action: about 3.7 s per turn on iOS and 5.3 s on Web here, most of it the device action and the screen settling. These are two short flows on one model, so treat them as a direction, not a benchmark.
|
|
100
|
+
Each local turn costs one model call plus one device action: about 3.7 s per turn on iOS and 5.3 s on Web here, most of it the device action and the screen settling. These are two short flows on one model, so treat them as a direction, not a benchmark. GPT and Gemini have not been measured yet. Claude Sonnet 5.5 passed with `grounding: 'model'` on iOS Settings in 3 turns; that is one run, so treat `grounding: 'model'` as dependent on the model.
|
|
101
101
|
|
|
102
102
|
Start with the default grounding and the cheapest vision model your provider has. Move to a stronger model if steps run out of turns or pick the wrong elements.
|
|
103
103
|
|
package/docs/web.md
CHANGED
|
@@ -53,7 +53,7 @@ Revyl runs one step at a time on a session. A step sent while another is still r
|
|
|
53
53
|
|
|
54
54
|
## Exact actions
|
|
55
55
|
|
|
56
|
-
**Early access.** Since TypeScript (`@revyl/verify`) 0.4.0, Python (`revyl-verify`) 0.2.0 and Go v0.3.0. `doubleTap`, `longPress`, `drag`, `typeFocused`, `goHome`, and Web keys and modifiers in `pressKey`, are new in TypeScript 0.5.0, Python 0.3.0 and Go v0.4.0. The [Python](https://github.com/RevylAI/verify/blob/v0.
|
|
56
|
+
**Early access.** Since TypeScript (`@revyl/verify`) 0.4.0, Python (`revyl-verify`) 0.2.0 and Go v0.3.0. `doubleTap`, `longPress`, `drag`, `typeFocused`, `goHome`, and Web keys and modifiers in `pressKey`, are new in TypeScript 0.5.0, Python 0.3.0 and Go v0.4.0. The [Python](https://github.com/RevylAI/verify/blob/v0.6.1/python/README.md#exact-actions) and [Go](https://github.com/RevylAI/verify/blob/v0.6.1/go/README.md#exact-actions) READMEs show them with each language's names. When you know exactly what to click, skip the agent. Exact actions run in the browser as given, with no model, and mix freely with `instruction()` and `validation()`:
|
|
57
57
|
|
|
58
58
|
```ts
|
|
59
59
|
import { open } from '@revyl/verify';
|
|
@@ -66,6 +66,7 @@ try {
|
|
|
66
66
|
await session.validation('A list of products is shown');
|
|
67
67
|
await session.swipe('up'); // the finger moves up, so the page scrolls down
|
|
68
68
|
await session.instruction('Add the Sauce Labs Onesie to the cart'); // hand the fuzzy part to the agent
|
|
69
|
+
await session.swipe('down'); // back to the top, so the cart icon is on screen
|
|
69
70
|
const cart = await session.locate('Shopping cart icon'); // a point in screenshot pixels
|
|
70
71
|
await session.tap(cart); // a tap by coordinates
|
|
71
72
|
await session.validation('The cart lists the Sauce Labs Onesie');
|
|
@@ -103,23 +104,25 @@ try {
|
|
|
103
104
|
}
|
|
104
105
|
```
|
|
105
106
|
|
|
106
|
-
`typeFocused(text)` types where the focus already is, without clicking, and replaces the field's text unless you pass `{ clear: false }`. Focus the field first, with `tap()` or `pressKey('tab')`: with no field focused, nothing is typed and no error is thrown. `pressKey()` takes `'enter'`, `'backspace'`, `'tab'`, `'escape'`, `'delete'`, `'space'`, `'arrowup'`, `'arrowdown'`, `'arrowleft'`, `'arrowright'`, `'home'`, `'end'`, `'pageup'` and `'pagedown'`, with up to four `modifiers` held (`'ctrl'`, `'shift'`, `'alt'`, `'meta'`); with modifiers a lowercase letter or digit works too. `longPress()` holds for 1500 ms unless you pass `duration`, from 1 to 5000. `drag(from, to)` presses at one location, moves to the other and releases. A failed action throws `ACTION_FAILED`. One whose outcome Revyl can't confirm throws `ACTION_UNCERTAIN` or `TIMEOUT`, so check the session before acting again. Actions are never repeated. If Verify can't reach Revyl, the action fails with `API_ERROR` and isn't resent either, so check the session before trying it again. [api.md](api.md#exact-actions) lists every call.
|
|
107
|
+
`typeFocused(text)` types where the focus already is, without clicking, and replaces the field's text unless you pass `{ clear: false }`. Focus the field first, with `tap()` or `pressKey('tab')`: with no field focused, nothing is typed and no error is thrown. `pressKey()` takes `'enter'`, `'backspace'`, `'tab'`, `'escape'`, `'delete'`, `'space'`, `'arrowup'`, `'arrowdown'`, `'arrowleft'`, `'arrowright'`, `'home'`, `'end'`, `'pageup'` and `'pagedown'`, with up to four `modifiers` held (`'ctrl'`, `'shift'`, `'alt'`, `'meta'`); with modifiers a lowercase letter or digit works too. The shortcut key on Web is `'ctrl'`, so select all with `pressKey('a', { modifiers: ['ctrl'] })`: `'meta'` is not the shortcut key here, though it is Command on a [Mac computer](computers.md#screen). `longPress()` holds for 1500 ms unless you pass `duration`, from 1 to 5000. `drag(from, to)` presses at one location, moves to the other and releases. A failed action throws `ACTION_FAILED`. One whose outcome Revyl can't confirm throws `ACTION_UNCERTAIN` or `TIMEOUT`, so check the session before acting again. Actions are never repeated. If Verify can't reach Revyl, the action fails with `API_ERROR` and isn't resent either, so check the session before trying it again. [api.md](api.md#exact-actions) lists every call.
|
|
107
108
|
|
|
108
109
|
| Call | Web | iOS | Android | Mac computer |
|
|
109
110
|
| --- | --- | --- | --- | --- |
|
|
110
|
-
| `tap
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
111
|
+
| `tap({ x, y })`, `tap({ target })`, `screenSize` | Yes | Yes | Yes | Yes |
|
|
112
|
+
| `locate` | Yes | Yes | Yes | No, use `tap({ target })`, which returns the point it clicked, or read the point off `screenshot()` |
|
|
113
|
+
| `doubleTap`, `longPress`, `type`, `swipe`, `drag` | Yes | Yes | Yes | No |
|
|
114
|
+
| `pressKey(key, { modifiers })` | Yes: `'enter'`, `'backspace'`, `'tab'`, `'escape'`, arrows and more, with modifiers | `'enter'` and `'backspace'` only | `'enter'` and `'backspace'` only | Yes: the Web keys and modifiers, where `'meta'` is Command |
|
|
115
|
+
| `typeFocused(text, { clear })` | Yes, types into the focused field | No, use `type(location, text)` | No, use `type(location, text)` | Yes, with `{ clear: false }` |
|
|
113
116
|
| `back()` | Yes, browser history | No, tap the on-screen back control | Yes, the system back button | No |
|
|
114
117
|
| `goHome()` | No, use `openUrl` or `navigate` | Yes, the home screen | Yes, the home screen | No |
|
|
115
118
|
| `openUrl(url)` | Yes, loads an http(s) URL in the tab | Yes, a URL or deep link | Yes, a URL or deep link | No |
|
|
116
119
|
| `launchApp(bundleId)` | No, use `openUrl` or `navigate` | Yes | Yes | No |
|
|
117
120
|
| `navigate(url)` | Yes | No, use `openUrl` | No, use `openUrl` | No |
|
|
118
|
-
| `instruction`, `validation`, your own model | Yes | Yes | Yes | No, use `exec()` |
|
|
121
|
+
| `instruction`, `validation`, your own model | Yes | Yes | Yes | No, use `exec()` and the screen calls above |
|
|
119
122
|
| `screenshot()` | Yes | Yes | Yes | Yes |
|
|
120
123
|
| `exec`, files, processes | No, open a Mac computer | No, open a Mac computer | No, open a Mac computer | Yes |
|
|
121
124
|
|
|
122
|
-
**Coordinates.** Prefer a description, such as `tap({ target: 'Sign in button' })`: Revyl finds the element on the current screen, and you never handle coordinates. Otherwise a point is `{ x, y }` in screenshot pixels on every platform: whole numbers from the top-left corner of the image `screenshot()` returns. So a position you read off a screenshot goes to `tap()`, `type()`, `swipe()` and every other exact action unchanged. `screenSize()` returns the screenshot's width and height, and `locate()` and the points `tap()`, `doubleTap()`, `longPress()` and `type()` return use the same pixels. A point outside the screenshot is refused with `INVALID_ARGUMENT` before any action is sent, and the message names the valid range. On iOS, Verify converts the pixels to the screen's points for you. On Web they equal CSS pixels at the default device scale factor of 1, and you still pass screenshot pixels at any other. This holds from TypeScript 0.4.1, Python 0.2.1 and Go v0.3.1; earlier versions took iOS points.
|
|
125
|
+
**Coordinates.** Prefer a description, such as `tap({ target: 'Sign in button' })`: Revyl finds the element on the current screen, and you never handle coordinates. Otherwise a point is `{ x, y }` in screenshot pixels on every platform: whole numbers from the top-left corner of the image `screenshot()` returns. So a position you read off a screenshot goes to `tap()`, `type()`, `swipe()` and every other exact action unchanged. `screenSize()` returns the screenshot's width and height, and `locate()` and the points `tap()`, `doubleTap()`, `longPress()` and `type()` return use the same pixels. A point outside the screenshot is refused with `INVALID_ARGUMENT` before any action is sent, and the message names the valid range. On iOS, Verify converts the pixels to the screen's points for you. On Web they equal CSS pixels at the default device scale factor of 1, and you still pass screenshot pixels at any other. This holds from TypeScript 0.4.1, Python 0.2.1 and Go v0.3.1; earlier versions took iOS points. On a Mac computer a point is in screenshot pixels too, and `tap({ target })` returns the point it clicked in the same pixels. Verify refuses a negative or fractional point before sending it, and Revyl refuses one past the screen's edge without clicking; both throw `INVALID_ARGUMENT` naming the valid range.
|
|
123
126
|
|
|
124
127
|
A call a platform doesn't support throws `NOT_AVAILABLE_ON_PLATFORM`, and its message names the alternative.
|
|
125
128
|
|