@verirun/cli 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,6 +4,8 @@ Record a browser workflow, replay it, and capture the Supabase, Vercel + Railway
4
4
  your backend produced during the replay. Plus `verirun map`: a static
5
5
  interaction graph of your React/TypeScript app — every page, what you can click
6
6
  on it, and which backend calls those clicks make — without running anything.
7
+ And `verirun scan <url>`: a read-only scan of any live site for JS errors,
8
+ failed requests, dead links and missing anchors, with no source or logs needed.
7
9
 
8
10
  ## Install
9
11
 
@@ -30,17 +32,34 @@ your `MANPATH` (`man verirun`) on macOS, Linux, and WSL / Git Bash. Native
30
32
  Windows (cmd, PowerShell) has no `man` — use `--help`, or `man -l` the file
31
33
  under a POSIX shell. From a `npm link` checkout: `man -l man/verirun.1`.
32
34
 
35
+ ### Output and exit codes
36
+
37
+ Output is compact by default: a verdict on the first line (`PASS` / `FAIL`),
38
+ then only the evidence for it. Commands that report something take `--json`
39
+ (one object on one line, with a `schemaVersion`) and `--verbose` for the full
40
+ detail. With `--json`, progress and warnings go to stderr, so stdout is the
41
+ result and nothing else.
42
+
43
+ | Exit code | Meaning |
44
+ | --- | --- |
45
+ | `0` | The command did what it was asked and the check, if any, passed. |
46
+ | `1` | It ran, and what it checks failed: a replay step (`run`), findings (`scan`), errors in a workflow file (`workflow check`), no usable license (`whoami`), a recording with no actions (`record`). |
47
+ | `2` | It could not run: bad usage, missing setup, no license, an unreachable target. With `--json` the message is also printed on stdout as `{"status":"error","error":{"message":"…"}}`. |
48
+
49
+ Before 0.3.0 every failure exited `1`. A script that only tests for non-zero is
50
+ unaffected.
51
+
33
52
  ---
34
53
 
35
54
  ## Account
36
55
 
37
- `record` and `run` need a connected Verirun account. First run on a machine:
56
+ `record`, `run`, `map` and `scan` need a connected Verirun account. First run on a machine:
38
57
 
39
58
  ```bash
40
59
  verirun login
41
60
  ```
42
61
 
43
- This prints a short code and a link. Open the link, sign in (a 7 day free trial
62
+ This prints a short code and a link. Open the link, sign in (a 3-day free trial
44
63
  starts automatically if you are new), and enter the code. The CLI then holds a
45
64
  device token for that machine.
46
65
 
@@ -49,6 +68,11 @@ verirun whoami # account, plan, subscription status
49
68
  verirun logout # disconnect this machine
50
69
  ```
51
70
 
71
+ `verirun whoami` exits `0` when the gated commands would run and `1` when they
72
+ would not, and `verirun whoami --json` prints the same status as JSON
73
+ (`schemaVersion` 1, with a `licensed` boolean), so a script can check the
74
+ license without parsing text.
75
+
52
76
  | | |
53
77
  | --- | --- |
54
78
  | `~/.verirun/config.json` | Holds the device token. Per user, not per project. Restricted to your account (0600, or an `icacls` ACL on Windows). |
@@ -114,6 +138,8 @@ Opens a headed browser. Perform the workflow by hand — click, type, navigate.
114
138
  | `--user-data-dir <dir>` | Record in a persistent profile directory, so the session is already logged in and stays logged in for later runs. The real browser using that profile must be closed first. |
115
139
  | `--test-id-attribute <attr>` | Attribute preferred for test-id selectors. Defaults to `data-testid`; also settable as `testIdAttribute` in config. |
116
140
  | `-f, --force` | Overwrite an existing workflow of the same name. Without it, recording onto an existing name errors. |
141
+ | `--json` | Print the result as JSON (`schemaVersion` 1: `name`, `saved`, `steps`, `path`). Everything else goes to stderr. |
142
+ | `--verbose` | List every recorded step after saving. By default `record` prints one line: how many steps it saved and where. |
117
143
 
118
144
  **Name rules.** `<name>` becomes a filename: letters, digits, dot, dash,
119
145
  underscore; must start with a letter or digit; 64 chars max. Two Windows rules
@@ -222,6 +248,26 @@ Replays the recorded steps — headless, in the same browser kind the workflow w
222
248
  recorded in — then captures logs for the wall-clock window the replay occupied
223
249
  and writes `.verirun/runs/<name>-<timestamp>/logs.json`.
224
250
 
251
+ What it prints is a summary, verdict first:
252
+
253
+ ```text
254
+ FAIL checkout step 2 failed: click button "Pay now" (2/4 steps replayed, 2.3s)
255
+ locator.click: Timeout 2000ms exceeded.
256
+ waiting for locator('role=button[name="Pay now"i]')
257
+ selector: role=button[name="Pay now"i]
258
+ logs 90 entries, 34 errors, 6 warnings (Vercel 90)
259
+ x30 vercel/lambda error 500 Error: connect ECONNREFUSED 10.0.3.7:5432
260
+ x3 vercel/lambda error 500 TypeError: Cannot read properties of undefined (reading 'total')
261
+ x1 vercel/lambda error 500 Unhandled rejection: payment provider returned 402 for order ord_99812
262
+ full log: .verirun/runs/checkout-2026-11-13T13-10-02-468Z/logs.json
263
+ ```
264
+
265
+ The `logs` lines are the error-level entries (and any carrying an HTTP 5xx
266
+ status) from the run window. Lines that differ only by ids or numbers are
267
+ folded into one with a count, most frequent first, 8 groups at most. A `PASS`
268
+ with error lines under `logs` is the case this tool exists for: the screen said
269
+ done and the backend threw. Everything else is in `logs.json`.
270
+
225
271
  | Flag | Meaning |
226
272
  | --- | --- |
227
273
  | `--slow-mo <ms>` | Pause before each action. Defaults to **500 ms when `--headed`** so you can follow the run by eye, and to 0 otherwise. `--slow-mo 0` watches at full speed; a bigger number follows a fast form more easily. |
@@ -232,6 +278,24 @@ and writes `.verirun/runs/<name>-<timestamp>/logs.json`.
232
278
  | `--cdp <url>` | Attach to a browser already running with remote debugging and drive it. See below. |
233
279
  | `--user <id>` | Keep only log entries whose message or raw payload contains this string, case-insensitive — e.g. a user's email. Overrides `config.userFilter`. Recorded in `logs.json` as `"filter": { "user": … }`; `entries` and the per-source counts are post-filter. If everything is filtered out, `run` warns. |
234
280
  | `--dialog <answer>` | How to answer a browser dialog opened by a step with no saved `dialog` answer: `accept` (OK, the default) or `dismiss` (Cancel). A step's own `dialog` always wins. |
281
+ | `--json` | Print the summary as one JSON object instead of text (below). `logs.json` is written either way. |
282
+ | `--verbose` | Also print each step as it replays, every distinct error line, and the full error text. |
283
+
284
+ `run --json` (`schemaVersion` 1) is the summary, not the entries:
285
+
286
+ ```jsonc
287
+ { "schemaVersion": 1, "tool": "verirun-run", "workflow": "checkout", "status": "ok" | "failed",
288
+ "durationMs": 2300, "window": { "startedAt": "<ISO>", "endedAt": "<ISO>" },
289
+ "steps": { "total": 4, "replayed": 2, "failedAt": 2 },
290
+ "failedStep": { "index": 2, "action": "click", "selector": "…", "description": "…" }, // on failure
291
+ "error": "<full message>", // on failure
292
+ "dialogs": 0,
293
+ "sources": [ { "id": "vercel", "name": "Vercel", "entries": 90 } ],
294
+ "logs": { "entries": 90, "problems": 34, "warnings": 6, "groupsOmitted": 0,
295
+ "groups": [ { "source": "vercel", "stream": "lambda", "level": "error", "status": 500,
296
+ "message": "<first line>", "count": 30, "firstAt": "<ISO>", "lastAt": "<ISO>" } ] },
297
+ "logsPath": ".verirun/runs/checkout-<timestamp>/logs.json" }
298
+ ```
235
299
 
236
300
  **Where the user filter runs.** For **Supabase** it goes into the log query's
237
301
  `WHERE` (matched against the rendered message and the JSON of `metadata`), so
@@ -331,8 +395,92 @@ warned about and contributes no entries.
331
395
  sub-stream within a source (`lambda`, `edge`, `postgres_logs`, …). `raw` is
332
396
  the provider's untouched payload (for Supabase that now includes the row's
333
397
  `metadata`, so a `--user` filter can match a nested email field).
334
- - With no log sources configured, `run` still replays and warns; `entries` is
335
- empty.
398
+ - With no log sources configured, `run` still replays and says so in its
399
+ summary; `entries` is empty.
400
+ - The file's schema is unchanged by the compact summary: `run` prints less, it
401
+ does not store less.
402
+
403
+ ---
404
+
405
+ ## `verirun scan <url>`
406
+
407
+ ```bash
408
+ verirun scan https://example.com
409
+ verirun scan https://example.com --md # paste-ready Markdown
410
+ verirun scan https://example.com --json # the full report
411
+ ```
412
+
413
+ An outside-in scan of a live site. It needs no source code, no recording and
414
+ no log source: point it at a public URL. It crawls same-origin links breadth
415
+ first (20 pages, 2 hops from the start URL by default) in headless Chromium,
416
+ loading up to 4 pages at a time with a 500 ms pause before each request on
417
+ each connection. Pages listed in the site's sitemap (the `Sitemap:` lines in
418
+ `robots.txt`, else `/sitemap.xml`) join the crawl after the start page's own
419
+ links, so a site whose navigation is JavaScript rather than `<a href>` is still
420
+ covered. A request that gets no response at all (a reset connection, not a
421
+ timeout) is asked once more before it is reported. A `429` means the site is
422
+ rate-limiting the scanner, not that the URL is broken, so it is counted rather
423
+ than reported; a link served as a download is treated as a file. It reports,
424
+ per page:
425
+
426
+ - JavaScript exceptions and console errors
427
+ - failed requests and HTTP 4xx / 5xx responses
428
+ - dead internal links
429
+ - in-page `#anchor` and skip-link targets that do not exist
430
+ - a missing main landmark
431
+
432
+ Only the site's own problems are kept: its own origin, or an API it calls.
433
+ Analytics, ad, chat-widget and other third-party noise is dropped. The same
434
+ finding on several pages is reported once, with the pages and a count.
435
+
436
+ ```text
437
+ FAIL https://example.com/ 5 findings on 6 pages (browser, 9.4s)
438
+ JS exception TypeError: Cannot read properties of undefined (reading 'call')
439
+ on /
440
+ HTTP error 500 GET /api/data
441
+ on /
442
+ dead link /broken-page returned 404
443
+ on /
444
+ …
445
+ 14 internal links checked
446
+ never requested: 2 destructive-looking links, 1 URL disallowed by robots.txt
447
+ ```
448
+
449
+ **It is strictly read-only.**
450
+
451
+ - It only navigates. It sends `GET` and `HEAD`, never clicks a button, never
452
+ submits a form.
453
+ - It never requests a link that looks destructive: `logout`, `signout`,
454
+ `delete`, `remove`, `unsubscribe`, `cancel`, `deactivate`, `revoke` and
455
+ similar, in the URL or the link text.
456
+ - It identifies itself honestly:
457
+ `Mozilla/5.0 (compatible; VerirunScan/1.0; +https://get-verirun.duely.in/scanner)`.
458
+ That page explains the scanner and how to block it.
459
+ - It obeys `robots.txt` (the `VerirunScan` group, else `*`), including
460
+ `Crawl-delay`; a site that sets one gets one request at a time. If the start
461
+ URL is disallowed, the scan does not run.
462
+
463
+ Scan sites you own or have permission to scan.
464
+
465
+ | Flag | Meaning |
466
+ | --- | --- |
467
+ | `--max-pages <n>` | Stop after this many pages. Default 20. |
468
+ | `--depth <n>` | Follow same-origin links this many hops from the start URL. Default 2. |
469
+ | `--delay <ms>` | Pause before each page load, on each of the 4 parallel connections. Default 500; a larger `Crawl-delay` in robots.txt wins and also drops the scan to one connection. |
470
+ | `--timeout <ms>` | Per-page load timeout. Default 20000. |
471
+ | `--no-browser` | Plain HTTP instead of headless Chromium. Nothing is executed, so JS exceptions and console errors are not checked, and a page that renders entirely client-side shows little. The report says so. |
472
+ | `--json` | The report as one JSON object, `schemaVersion` 1: `status` (`clean` / `findings` / `error`), `summary`, `pages`, and `findings` (`kind`, `message`, `evidence`, `pages`, `count`). |
473
+ | `--md` | The report as Markdown, ready to paste into a reply or an issue. |
474
+ | `--verbose` | Every finding with its evidence, and every page scanned. |
475
+
476
+ Exit code `0` when nothing was found, `1` when there are findings, `2` when the
477
+ scan could not run (unreachable, disallowed by robots.txt, not HTML).
478
+
479
+ **What an outside scan cannot see.** It sees what a visitor's browser sees. It
480
+ does not know which backend call a button makes (`verirun map` reads that from
481
+ your source) and it cannot see your backend logs (`verirun run` captures those
482
+ for a recorded flow). It does not log in, so anything behind a login is out of
483
+ reach.
336
484
 
337
485
  ---
338
486
 
@@ -419,7 +567,7 @@ browser, no server, no recording.
419
567
 
420
568
  ```bash
421
569
  verirun map # per-page summary
422
- verirun map --json # the whole graph
570
+ verirun map --json # the whole graph, on one line (--verbose indents it)
423
571
  verirun map --report # graph + coverage.md into .verirun/map
424
572
  verirun map --bfs /account --depth 2 # what is reachable from one page
425
573
  verirun map --reach # how much of each page was captured
@@ -474,6 +622,9 @@ as `unresolved`, an endpoint passed through a variable as `?`, and Supabase
474
622
  tells you what fraction of each page it actually attached, so you can see when
475
623
  it is missing something rather than guessing.
476
624
 
625
+ `--bfs` and `--reach` also take `--json`, for the same result as one JSON
626
+ object (`schemaVersion` 1).
627
+
477
628
  ---
478
629
 
479
630
  ## `verirun workflow check <name>`
@@ -538,18 +689,30 @@ confirms it. Don't overwrite a recording with a generated file — use a new nam
538
689
 
539
690
  ## `verirun agent`
540
691
 
541
- Prints a single self-contained reference for an AI coding agent: every command
542
- and flag (read from the CLI itself, so it can't drift), the `.verirun/` layout,
543
- the `logs.json` schema, the recommended record-then-run loop, and a snapshot of
544
- **this** checkout — resolved repo root, configured log sources, recorded
545
- workflows, recent runs.
692
+ Prints a compact reference for an AI coding agent: what the tool does, the loop
693
+ to follow, how to read a result, the exit codes, the commands an agent drives
694
+ with their flags (read from the CLI itself, so they can't drift), and a
695
+ snapshot of **this** checkout — resolved repo root, configured log sources,
696
+ recorded workflows, recent runs.
697
+
698
+ The default is deliberately short, because an agent reads it on every task.
699
+ Three longer sections are printed only when asked for:
700
+
701
+ | `--topic` | What it covers |
702
+ | --- | --- |
703
+ | `authoring` | Writing a workflow file yourself from `map --json`: format, actions, selectors, dialogs. |
704
+ | `signed-in` | Testing a flow behind a login: the browser profile, Windows paths. |
705
+ | `schemas` | The full shapes of `run --json`, `logs.json` and the scan report. |
546
706
 
547
707
  ```bash
548
- verirun agent > .verirun/AGENT.md # save it as context for your agent
708
+ verirun agent # the short reference
709
+ verirun agent --topic authoring # one section
710
+ verirun agent --full > .verirun/AGENT.md # everything, saved as context for your agent
549
711
  verirun agent --json # machine-readable form
550
712
  verirun agent --skill --install # .claude/skills/verirun/SKILL.md
551
713
  verirun agent --skill --install cursor # .cursor/rules/verirun.mdc
552
- verirun agent --skill --install all # both
714
+ verirun agent --skill --install codex # .agents/skills/verirun/SKILL.md + an AGENTS.md section
715
+ verirun agent --skill --install all # all three
553
716
  ```
554
717
 
555
718
  `--skill` emits the same knowledge as an agent skill — frontmatter with a
@@ -561,14 +724,23 @@ looks:
561
724
  |---|---|---|
562
725
  | `claude` (default) | `.claude/skills/verirun/SKILL.md` | automatically, when a task looks like app testing |
563
726
  | `cursor` | `.cursor/rules/verirun.mdc` | when the task matches its description (an agent-requested rule, not always-on) |
564
- | `all` | both | |
727
+ | `codex` | `.agents/skills/verirun/SKILL.md`, plus a short section in `AGENTS.md` | the skill when the task matches its description; the `AGENTS.md` section (a pointer to the skill) on every request |
728
+ | `all` | all three | |
729
+
730
+ Codex reads skills from `.agents/skills/` between the working directory and the
731
+ repo root. `AGENTS.md` is shared with other tools and with people, so verirun only
732
+ writes between `<!-- verirun:start -->` and `<!-- verirun:end -->`: it creates the
733
+ file if there is none, appends the section if there is no marked one, and
734
+ replaces just that section on a re-run. Everything outside the markers is left as
735
+ it was.
565
736
 
566
737
  Re-run it after upgrading verirun; the file is generated, not hand-maintained.
567
738
 
568
739
  For any other agent, paste the output into `CLAUDE.md`, an `AGENTS.md`, or pipe it
569
740
  straight into a prompt. No account needed. The agent then knows to run
570
- `verirun run <name>`, read exit code + `.verirun/runs/<name>-<newest>/logs.json`,
571
- and act on `status` / `error` / `entries` without you explaining the tool.
741
+ `verirun run <name>`, branch on the exit code, and act on the `PASS` / `FAIL`
742
+ summary without you explaining the tool, opening `logs.json` only when it needs
743
+ an entry the summary does not show.
572
744
 
573
745
  ---
574
746
 
@@ -715,6 +887,43 @@ run this often.
715
887
 
716
888
  ## Changelog
717
889
 
890
+ ### 0.3.0
891
+
892
+ - **`verirun scan <url>`.** A read-only, outside-in scan of any live site: JS
893
+ exceptions, console errors, failed requests, HTTP 4xx / 5xx, dead internal
894
+ links, missing `#anchor` and skip-link targets, a missing main landmark. No
895
+ source and no logs needed. `--json`, `--md`, `--no-browser`. It never clicks
896
+ or submits, skips logout / delete style links, sends an honest user agent and
897
+ obeys `robots.txt`. Needs a connected account, like `record` and `run`.
898
+ It also crawls the pages a site's sitemap lists, so JavaScript-only
899
+ navigation is covered, and asks once more before reporting a request that
900
+ got no response, so one dropped connection does not fail the scan.
901
+ A rendered page bloated with inline base64 images no longer produces
902
+ missing-anchor findings past the read limit (data URIs are compacted, and a
903
+ page still too large skips its absence checks); `429`s and download links
904
+ are no longer reported as failures.
905
+ - **Compact output.** `run` now prints a verdict first, then the failed step
906
+ and the error-level log lines from the run window with repeats folded into a
907
+ count, instead of a step list and a pointer to a file. On the same run, what
908
+ an agent has to read to learn why it failed drops from the whole `logs.json`
909
+ to a few lines. `workflow check`, `whoami` and `record` lead with their
910
+ result too. `--verbose` brings the detail back.
911
+ - **`--json` everywhere it was missing**: `run`, `record`, `whoami`, `map --bfs`
912
+ and `map --reach`. Each is one object on one line with a `schemaVersion`.
913
+ `map --json` and `workflow check --json` are now one line too (`map
914
+ --verbose` indents); `workflow check --json` gained `schemaVersion`.
915
+ - **Exit codes mean something.** `0` ok, `1` the check failed, `2` could not
916
+ run (usage, setup, license, unreachable). Before, every failure was `1`.
917
+ `whoami` now exits `1` when the gated commands would not run.
918
+ - **`verirun agent --skill --install codex`.** Writes the skill to
919
+ `.agents/skills/verirun/SKILL.md`, where Codex looks for skills, and a short
920
+ marked section in `AGENTS.md` pointing at it, without touching the rest of
921
+ that file. `--install all` now covers Claude Code, Cursor and Codex.
922
+ - **`verirun agent` is shorter.** The default reference carries what an agent
923
+ needs on every task; the workflow-authoring, signed-in and schema sections
924
+ moved behind `--topic`, and `--full` prints everything.
925
+ - `logs.json` and workflow files are unchanged (both still `schemaVersion` 1).
926
+
718
927
  ### 0.2.5
719
928
 
720
929
  - **`verirun run` answers browser dialogs instead of cancelling them.** With no