verikun 0.23.0 → 0.25.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/.claude/skills/verikun/SKILL.md +44 -5
- package/CHANGELOG.md +47 -0
- package/README.md +4 -3
- package/dist/agent/remote.js +16 -3
- package/dist/args.js +9 -4
- package/dist/cli.js +342 -28
- package/dist/device/claims.js +12 -2
- package/dist/device/failover.js +230 -0
- package/dist/device/prep.js +156 -0
- package/dist/device/settings.js +82 -0
- package/dist/drivers/adb.js +299 -7
- package/dist/drivers/ios.js +20 -0
- package/dist/run.js +3 -1
- package/dist/server.js +341 -15
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -93,6 +93,8 @@ usually don't need a `wait` before an action — `vk tap @next` already polls fo
|
|
|
93
93
|
- `vk swipe up|down|left|right [--on <selector>] [--distance f] [--duration ms]`
|
|
94
94
|
- `vk swipe --from x,y --to x,y [--duration ms]`
|
|
95
95
|
- `vk key <name|code>` · `vk back` · `vk home` · `vk enter`
|
|
96
|
+
Named keys include `sleep` and `wakeup`. `power` is a **toggle**, so use `sleep`/`wakeup`
|
|
97
|
+
when you mean a direction.
|
|
96
98
|
- `vk screenshot [--out path] [--more] [--max px] [--full]` — saves a PNG (default
|
|
97
99
|
`./.verikun/screen.png`) and prints the path; then read that file to *see* the
|
|
98
100
|
screen. It's **downscaled to a 700px longest edge by default** to save tokens
|
|
@@ -118,11 +120,15 @@ banner, the retry path, dark theme, a layout that breaks at accessibility text s
|
|
|
118
120
|
|
|
119
121
|
| key | values | Android | iOS simulator |
|
|
120
122
|
|---|---|---|---|
|
|
123
|
+
| `animations` | `on\|off` | yes | **no** — nothing disables UIKit animation |
|
|
121
124
|
| `airplane` | `on\|off` | yes | **no** — a simulator has no radio |
|
|
122
125
|
| `dark` | `on\|off` | yes | yes |
|
|
123
126
|
| `font-scale` | `0.5`–`3.0`, `default` | yes | yes (nearest Dynamic Type category) |
|
|
124
127
|
| `rotation` | `portrait\|landscape\|portrait-reverse\|landscape-reverse\|auto` | yes | **no** |
|
|
125
128
|
| `stay-awake` | `on\|off` | yes | no-op (simulators don't sleep) |
|
|
129
|
+
| `screen-timeout` | `30s` / `10m` / ms / `max` | yes | no-op (simulators don't sleep) |
|
|
130
|
+
| `dnd` | `on\|off` | yes | **no** — Focus is not scriptable |
|
|
131
|
+
| `doze` | `on\|off` | yes | no-op (no Doze equivalent) |
|
|
126
132
|
|
|
127
133
|
Set several at once: `vk device set dark=on font-scale=1.3`. Each change is **verified by
|
|
128
134
|
reading it back**, so success means it actually landed — these device commands silently
|
|
@@ -134,6 +140,14 @@ banner, the retry path, dark theme, a layout that breaks at accessibility text s
|
|
|
134
140
|
even when the flow *fails* — but a bare `vk device set` from a shell stays applied until
|
|
135
141
|
you reset it, so don't leave someone's phone in airplane mode.
|
|
136
142
|
|
|
143
|
+
- `vk device prep [--dry-run] [--revert]` — set a **test** device up once, stickily:
|
|
144
|
+
`animations=off stay-awake=on screen-timeout=max dnd=on doze=off`.
|
|
145
|
+
|
|
146
|
+
Unlike `device set`, prep **survives the run** and is undone only by `--revert`. A physical
|
|
147
|
+
device must be named (`--device <serial>`) — that requirement is deliberate, so prep can
|
|
148
|
+
never land on a personal phone that happened to be plugged in. Do not run it on a device
|
|
149
|
+
the user did not point you at. `vk doctor` reports whether a device is prepared.
|
|
150
|
+
|
|
137
151
|
Two traps:
|
|
138
152
|
- **`airplane=off` brings the radio back, not the internet.** Follow it with
|
|
139
153
|
`vk assert <selector> --wait 10s`, never an immediate `tap`.
|
|
@@ -378,7 +392,12 @@ vk ai onboarding.md --timeout 5m # tighten the run timeout (default 15m)
|
|
|
378
392
|
failure).
|
|
379
393
|
- **Bounded by default:** the run aborts if the estimated spend crosses **$3**
|
|
380
394
|
(`--max-cost-usd`) or the wall-clock passes **15m** (`--timeout`), so a runaway
|
|
381
|
-
compile/repair loop can't spend or hang without limit.
|
|
395
|
+
compile/repair loop can't spend or hang without limit. The ceiling is **per test, not per
|
|
396
|
+
suite** — `vk suite` gives each `*.md` its own fresh budget, so 20 tests at the default can
|
|
397
|
+
reach $60; there is no suite-wide cap, so lower the per-test figure instead. A model is only
|
|
398
|
+
ever called to **compile** (once, on a cache miss) or to **repair** (≤3 per failing step);
|
|
399
|
+
replay is always $0, and every non-`ai` command is $0 always. Full mechanism, the estimate
|
|
400
|
+
formula and the cache multipliers: <https://ddikman.github.io/verikun/reference/cost/>.
|
|
382
401
|
- Exit `0` pass · `1` a step failed (or the budget/timeout was hit) · `2` usage · `3` environment
|
|
383
402
|
(e.g. the model's API key — `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` — unset, or the `codex` /
|
|
384
403
|
`cursor-agent` CLI missing / not logged in for `--model codex-cli` / `cursor-cli`).
|
|
@@ -433,6 +452,19 @@ device; `503` means the server has no device attached — boot one (below). To
|
|
|
433
452
|
expose a device from THIS machine: `vk server --allow-install`
|
|
434
453
|
(add `--bind <addr>` to leave loopback; auth key auto-generates if unset).
|
|
435
454
|
|
|
455
|
+
**If you see `[verikun] server moved device: A → B` on stderr**, the server left a
|
|
456
|
+
device that failed and is now on another one. What that means depends on the line:
|
|
457
|
+
|
|
458
|
+
- `— retried there` (installs only): the build DID land, on **B**. Anything you go on
|
|
459
|
+
to do with an explicit serial must name B, not A.
|
|
460
|
+
- `— this step failed on the old device; the next runs on the new one`: your step
|
|
461
|
+
failed on **A**. Do NOT re-run it expecting a different answer for the same reason —
|
|
462
|
+
the failure was real on A, and B has none of the state your flow built up. Start the
|
|
463
|
+
flow again from the top if you want it on B.
|
|
464
|
+
|
|
465
|
+
The server rules the bad device out until it is power-cycled;
|
|
466
|
+
`vk devices --server <url>` shows why in its `NOTE` column.
|
|
467
|
+
|
|
436
468
|
## The device is missing or wedged
|
|
437
469
|
|
|
438
470
|
```sh
|
|
@@ -510,8 +542,13 @@ owns the redaction and the review-first flow.
|
|
|
510
542
|
|
|
511
543
|
## Gotchas
|
|
512
544
|
|
|
513
|
-
- **
|
|
514
|
-
animations can make `vk ui` flaky (it already retries 3×).
|
|
545
|
+
- **Prepare the device once** for reliable dumps: `vk device prep` (a physical device
|
|
546
|
+
needs `--device <serial>`). Live animations can make `vk ui` flaky (it already retries 3×).
|
|
547
|
+
- **A slept device returns the LOCK SCREEN, not an error.** The dump succeeds and hands
|
|
548
|
+
back `com.android.systemui` — so selectors miss for a reason unrelated to the app.
|
|
549
|
+
verikun detects this, wakes the device and clears a *swipe* lock automatically; on a
|
|
550
|
+
PIN/pattern/password it exits **3** naming the lock rather than returning that dump.
|
|
551
|
+
Tell the user to remove the lock in Settings > Security — verikun never asks for a PIN.
|
|
515
552
|
- **Ambiguous selector → exit 2**, never a random tap. `vk` prints the candidate
|
|
516
553
|
matches; add `--index N` or use a more specific selector.
|
|
517
554
|
- **Indexes are per-snapshot.** `vk tap 3` taps `[3]` from the *latest* dump;
|
|
@@ -549,13 +586,15 @@ owns the redaction and the review-first flow.
|
|
|
549
586
|
use `xcrun simctl`. Run `vk doctor --ios` to check the toolchain. Caveats: `clear`
|
|
550
587
|
is unsupported (no per-app reset), `current` is `(unknown)`, device logs are
|
|
551
588
|
simulator-only, and `device set` is partial — `dark`/`font-scale` work on a simulator
|
|
552
|
-
while `airplane`/`rotation` do not exist there at all
|
|
589
|
+
while `animations`/`airplane`/`rotation`/`dnd` do not exist there at all, and
|
|
590
|
+
`stay-awake`/`screen-timeout`/`doze` are no-ops (`vk device caps --ios`). That makes
|
|
591
|
+
`vk device prep` effectively Android-only: on iOS it applies nothing and says so. iOS
|
|
553
592
|
accessibility ids are often unset, so prefer `text:`/`desc:` selectors there.
|
|
554
593
|
|
|
555
594
|
## Worked example — verify a login flow
|
|
556
595
|
|
|
557
596
|
```sh
|
|
558
|
-
vk doctor
|
|
597
|
+
vk doctor # is the device set up? (read-only)
|
|
559
598
|
vk launch com.example.app
|
|
560
599
|
vk text @email_input "user@example.com" # field lookup auto-waits up to 5s
|
|
561
600
|
vk text @password_input "hunter2" --enter
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,49 @@ All notable changes to this project are documented here. The format is based on
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.25.0] - 2026-08-21
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **`vk server`**: moves to another attached healthy device when the bound one fails; only a pinned `--device` disables it. ([#99])
|
|
13
|
+
- **`vk server --allow-failover[=serials]`**: re-enable failover on a pinned server, and bound where it may go.
|
|
14
|
+
- **`vk server --no-failover`** / **`VERIKUN_NO_FAILOVER`**: disable failover outright.
|
|
15
|
+
- **`/v1/health`**: adds `failoverEnabled` and `quarantined`, so a client can see which devices the server ruled out.
|
|
16
|
+
- **`vk install --server`**: reports the device the build landed on when it differs from the one bound.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- **`vk install --server`**: a device-attributable install failure retries on another healthy device.
|
|
20
|
+
- **`/v1/exec`**, **`/v1/elements`**: report `deviceChanged` when the server moved device; the failing step is never replayed.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- **`/v1/logs`**: served the startup device's logs after a rebind instead of the bound device's.
|
|
24
|
+
- **`/v1/health`**: reported the startup device's read path after a rebind.
|
|
25
|
+
|
|
26
|
+
[#99]: https://github.com/ddikman/verikun/issues/99
|
|
27
|
+
|
|
28
|
+
## [0.24.0] - 2026-08-20
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- **`vk device prep`**: prepare a test device once — animations off, display awake, DND on, doze off.
|
|
32
|
+
- **`vk device prep --revert`**: restore what prep changed, from a host-global snapshot that outlives the run.
|
|
33
|
+
- **`vk device prep --dry-run`**: show each knob's current and target value without writing.
|
|
34
|
+
- **`vk device set animations|screen-timeout|dnd|doze`**: four new keys, each verified by readback.
|
|
35
|
+
- **`vk key sleep` / `vk key wakeup`**: directional screen keys; `power` remains a toggle.
|
|
36
|
+
- **`vk doctor`**: reports whether the device is prepared and whether it has a screen lock.
|
|
37
|
+
- **Docs site**: a `Cost & budget` reference page — when a model is called, the estimate formula,
|
|
38
|
+
and that `--max-cost-usd` is per test, not per suite. ([#94])
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
- **`vk doctor --fix`**: now an alias for `device prep`, so a physical device must be named with `--device`.
|
|
42
|
+
- **`vk doctor`**: read-only without `--fix`; it no longer writes animation scales directly.
|
|
43
|
+
- **`vk batch|ai|suite`**: put a prepped device to sleep on teardown; `--no-sleep-when-idle` opts out.
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
- **UI reads**: a locked device returned the lock screen as a successful dump; now woken, unlocked if
|
|
47
|
+
the lock is a swipe, else exit `3` naming it.
|
|
48
|
+
- **Docs site**: the `vk ai` cost line was printed in a format the CLI never emitted.
|
|
49
|
+
|
|
50
|
+
[#94]: https://github.com/ddikman/verikun/issues/94
|
|
51
|
+
|
|
9
52
|
## [0.23.0] - 2026-08-17
|
|
10
53
|
|
|
11
54
|
### Added
|
|
@@ -30,6 +73,10 @@ All notable changes to this project are documented here. The format is based on
|
|
|
30
73
|
- **`vk devices start --server`**: releases the device lock when it finishes, instead of `409`ing
|
|
31
74
|
the run it just booted the device for.
|
|
32
75
|
|
|
76
|
+
### Changed
|
|
77
|
+
- **Docs site**: content column widened to 60rem, giving the reference tables more room on
|
|
78
|
+
large screens.
|
|
79
|
+
|
|
33
80
|
## [0.22.2] - 2026-08-17
|
|
34
81
|
|
|
35
82
|
### Changed
|
package/README.md
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
- **Agent CLI** — `vk <command>`: one-shot commands to inspect the screen as a semantic tree (or screenshot) and act on it.
|
|
8
8
|
- **Puppeteer for native mobile** — a thin wrapper over native Android and iOS automation runners with zero runtime dependencies.
|
|
9
|
-
- **Natural-language tests** — `vk ai <file>`: runs plain-English tests, compiled once and replayed model-free (~$0), calling a model only to self-heal a drifted step.
|
|
9
|
+
- **Natural-language tests** — `vk ai <file>`: runs plain-English tests, compiled once and replayed model-free (~$0), calling a model only to self-heal a drifted step. [What that costs](https://ddikman.github.io/verikun/reference/cost/), and how the `--max-cost-usd` ceiling bounds it.
|
|
10
10
|
- **Self-improving** — the agent runner will provide prescriptive improvements to existing scripts to help stabilise flakiness for future runs.
|
|
11
|
-
- **CI-ready** — `vk suite` runs a folder of tests as one gated pass/fail run; `vk server` exposes a real device over an authenticated tunnel so a disposable CI runner (no phone attached) can still drive it.
|
|
11
|
+
- **CI-ready** — `vk suite` runs a folder of tests as one gated pass/fail run; `vk server` exposes a real device over an authenticated tunnel so a disposable CI runner (no phone attached) can still drive it, and moves to another attached device if that one goes bad.
|
|
12
12
|
|
|
13
13
|
```
|
|
14
14
|
$ vk ui
|
|
@@ -37,7 +37,8 @@ The package also carries the agent [`SKILL.md`](.claude/skills/verikun/SKILL.md)
|
|
|
37
37
|
## Quick start
|
|
38
38
|
|
|
39
39
|
```sh
|
|
40
|
-
vk doctor
|
|
40
|
+
vk doctor # check adb/device (read-only — never changes anything)
|
|
41
|
+
vk device prep --device <id> # set a TEST device up once: animations off, stays awake
|
|
41
42
|
vk devices # list attached devices
|
|
42
43
|
vk ui # semantic snapshot of the current screen
|
|
43
44
|
vk tap @login_button # tap by resource-id
|
package/dist/agent/remote.js
CHANGED
|
@@ -89,8 +89,14 @@ class RemoteTransport {
|
|
|
89
89
|
finally {
|
|
90
90
|
clearTimeout(timer);
|
|
91
91
|
}
|
|
92
|
-
if (!res.ok)
|
|
93
|
-
|
|
92
|
+
if (!res.ok) {
|
|
93
|
+
const body = await readBody(res);
|
|
94
|
+
// Before throwing: a failing request may still have moved the device, and that is
|
|
95
|
+
// exactly the case a caller must not miss (an exhausted install, a dead-device read).
|
|
96
|
+
if (body?.deviceChanged)
|
|
97
|
+
this.opts.onDeviceChange?.(body.deviceChanged);
|
|
98
|
+
throw describeStatus(res.status, body, url);
|
|
99
|
+
}
|
|
94
100
|
const parsed = await readBody(res);
|
|
95
101
|
if (parsed === null)
|
|
96
102
|
throw new errors_1.CliError(`verikun server at ${url} returned a non-JSON response`, 3);
|
|
@@ -157,6 +163,9 @@ function createRemoteBackend(opts, health) {
|
|
|
157
163
|
const res = await t.postJson('/v1/exec', req, EXEC_TIMEOUT_MS);
|
|
158
164
|
if (record && res.step)
|
|
159
165
|
opts.onStep?.(res.step, decodeArtifacts(res.artifacts), res.logStart);
|
|
166
|
+
// A failing step is a 200, so this is the ordinary path for a mid-run device death.
|
|
167
|
+
if (res.deviceChanged)
|
|
168
|
+
opts.onDeviceChange?.(res.deviceChanged);
|
|
160
169
|
return { code: res.code, error: res.error ? (0, rpc_1.rebuildError)(res.error) : undefined };
|
|
161
170
|
};
|
|
162
171
|
return {
|
|
@@ -184,11 +193,15 @@ function createRemoteBackend(opts, health) {
|
|
|
184
193
|
throw new errors_1.CliError(`install: cannot read '${appPath}' (${e.message})`, 2);
|
|
185
194
|
}
|
|
186
195
|
const sha256 = (0, node_crypto_1.createHash)('sha256').update(buf).digest('hex');
|
|
187
|
-
await t.request('POST', '/v1/install', buf, INSTALL_TIMEOUT_MS, {
|
|
196
|
+
const res = await t.request('POST', '/v1/install', buf, INSTALL_TIMEOUT_MS, {
|
|
188
197
|
'content-type': 'application/octet-stream',
|
|
189
198
|
'x-verikun-ext': ext,
|
|
190
199
|
'x-verikun-sha256': sha256,
|
|
191
200
|
});
|
|
201
|
+
// Install is the one operation the server replays elsewhere, so a move here means
|
|
202
|
+
// the build DID land — on a different device than the one we started with.
|
|
203
|
+
if (res.deviceChanged)
|
|
204
|
+
opts.onDeviceChange?.(res.deviceChanged);
|
|
192
205
|
},
|
|
193
206
|
async reset(appId) {
|
|
194
207
|
// Between-test housekeeping (vk suite): the step is deliberately NOT spliced
|
package/dist/args.js
CHANGED
|
@@ -44,12 +44,17 @@ const BOOLEAN = new Set([
|
|
|
44
44
|
'no-restart',
|
|
45
45
|
'no-logs',
|
|
46
46
|
'wipe',
|
|
47
|
+
'dry-run',
|
|
48
|
+
'revert',
|
|
49
|
+
'no-sleep-when-idle',
|
|
47
50
|
'allow-install',
|
|
48
51
|
'allow-unsafe-anonymous',
|
|
49
|
-
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
52
|
+
'no-failover',
|
|
53
|
+
// NOT here on purpose: 'allow-device-control', 'allow-failover' and 'ensure-device'
|
|
54
|
+
// are tri-state (absent / on / on-with-a-value), and the inline `--flag=value` branch
|
|
55
|
+
// is checked BEFORE this set — listing them would make `--allow-device-control=Pixel_6`
|
|
56
|
+
// parse fine but `--ensure-device Pixel_6` silently become a boolean plus a positional.
|
|
57
|
+
// Its opposite, 'no-failover', never takes a value, so it DOES belong above.
|
|
53
58
|
// Selector state modifiers (STATE_ATTRS in ui/selector.ts) and their negations.
|
|
54
59
|
// `enabled` was missing here until 0.15.0, and the omission was not cosmetic: a
|
|
55
60
|
// non-BOOLEAN flag swallows the next token, so `vk tap --enabled @submit` bound
|