@beryl-so/cli 0.29.0 → 0.33.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 +70 -55
- package/dist/beryl-test-skill.js +3 -0
- package/dist/commands/accounts.js +6 -6
- package/dist/commands/auth.js +5 -5
- package/dist/commands/config-vars.js +5 -5
- package/dist/commands/environments.js +91 -25
- package/dist/commands/explorations.js +3 -3
- package/dist/commands/groups.js +69 -0
- package/dist/commands/health.js +1 -1
- package/dist/commands/init.js +8 -8
- package/dist/commands/mailboxes.js +6 -6
- package/dist/commands/mcp.js +3 -3
- package/dist/commands/projects.js +5 -5
- package/dist/commands/runs.js +83 -23
- package/dist/commands/tests.js +112 -32
- package/dist/commands/util.js +20 -0
- package/dist/commands/watch.js +2 -1
- package/dist/registry/index.js +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
<!-- GENERATED by `npm run docs
|
|
1
|
+
<!-- GENERATED by `npm run docs`. Edit the command registry (src/commands/), not this file. -->
|
|
2
2
|
|
|
3
3
|
# beryl CLI
|
|
4
4
|
|
|
5
5
|
Beryl on the command line: create projects, watch the agent explore and author tests,
|
|
6
|
-
trigger runs in CI, manage everything the web app can
|
|
6
|
+
trigger runs in CI, manage everything the web app can, plus an MCP server (`beryl mcp`)
|
|
7
7
|
that exposes every command to coding agents.
|
|
8
8
|
|
|
9
|
-
## Quickstart
|
|
9
|
+
## Quickstart: one command
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
npx @beryl-so/cli@latest init
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Signs you in (browser approval by default, emailed one-time code as fallback
|
|
15
|
+
Signs you in (browser approval by default, emailed one-time code as fallback; new
|
|
16
16
|
emails are signed up on the spot), wires the beryl + playwright MCP servers into Claude
|
|
17
17
|
Code or Cursor, installs the beryl-test authoring skill, and installs Playwright for
|
|
18
18
|
local runs. Safe to re-run.
|
|
@@ -27,18 +27,18 @@ npx @beryl-so/cli@latest … # or run one-off without installing
|
|
|
27
27
|
From the monorepo (development): `cd cli && npm install && npm run build && npm link`.
|
|
28
28
|
|
|
29
29
|
`beryl init` and `beryl mcp` warn (stderr, best-effort) when the running CLI is behind the
|
|
30
|
-
latest npm release
|
|
30
|
+
latest npm release; a stale MCP server silently exposes fewer tools. Set
|
|
31
31
|
`BERYL_NO_UPDATE_CHECK=1` to opt out.
|
|
32
32
|
|
|
33
33
|
## Authenticate
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
beryl login # opens the browser to approve
|
|
36
|
+
beryl login # opens the browser to approve; press Enter for an emailed code instead
|
|
37
37
|
beryl login --otp # skip the browser; emailed one-time code (signs up new emails too)
|
|
38
38
|
export BERYL_API_KEY=beryl_pat_… # CI: use a token from Account → API tokens
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
Config stores your token + API URL only
|
|
41
|
+
Config stores your token + API URL only; nothing else persists. Point a command at a
|
|
42
42
|
workspace/project with the `--workspace` / `--project` flags or the `BERYL_WORKSPACE` /
|
|
43
43
|
`BERYL_PROJECT` env vars; with exactly one, the CLI auto-picks it.
|
|
44
44
|
Precedence: flags > `BERYL_*` env > auto-pick-if-one.
|
|
@@ -59,18 +59,18 @@ claude mcp add beryl -- beryl mcp # or, with the CLI installe
|
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
Most non-interactive commands below are exposed as an MCP tool with the same name
|
|
62
|
-
(spaces and dashes become underscores)
|
|
62
|
+
(spaces and dashes become underscores); see the MCP tool column. `none` marks commands
|
|
63
63
|
kept CLI-only: account/billing/team admin, feedback, and cloud exploration.
|
|
64
64
|
|
|
65
65
|
## Commands
|
|
66
66
|
|
|
67
67
|
### init
|
|
68
68
|
|
|
69
|
-
Set up Beryl in this repo
|
|
69
|
+
Set up Beryl in this repo: sign in and wire up your coding agent
|
|
70
70
|
|
|
71
71
|
| Command | Summary | MCP tool |
|
|
72
72
|
| --- | --- | --- |
|
|
73
|
-
| `beryl init` | Set up Beryl in this repo
|
|
73
|
+
| `beryl init` | Set up Beryl in this repo: sign in and wire up your coding agent | none |
|
|
74
74
|
|
|
75
75
|
### guide
|
|
76
76
|
|
|
@@ -86,7 +86,7 @@ Authenticate the CLI with your Beryl account
|
|
|
86
86
|
|
|
87
87
|
| Command | Summary | MCP tool |
|
|
88
88
|
| --- | --- | --- |
|
|
89
|
-
| `beryl login` | Authenticate the CLI with your Beryl account |
|
|
89
|
+
| `beryl login` | Authenticate the CLI with your Beryl account | none |
|
|
90
90
|
|
|
91
91
|
### signup
|
|
92
92
|
|
|
@@ -143,9 +143,9 @@ Manage who belongs to a workspace and their roles.
|
|
|
143
143
|
|
|
144
144
|
| Command | Summary | MCP tool |
|
|
145
145
|
| --- | --- | --- |
|
|
146
|
-
| `beryl members list` | List workspace members |
|
|
147
|
-
| `beryl members set-role <user-id> <role>` | Change a member's role |
|
|
148
|
-
| `beryl members remove <user-id>` | Remove a member from the workspace |
|
|
146
|
+
| `beryl members list` | List workspace members | none |
|
|
147
|
+
| `beryl members set-role <user-id> <role>` | Change a member's role | none |
|
|
148
|
+
| `beryl members remove <user-id>` | Remove a member from the workspace | none |
|
|
149
149
|
|
|
150
150
|
### invites
|
|
151
151
|
|
|
@@ -153,16 +153,16 @@ Send, list, and revoke workspace invitations, and accept ones sent to you.
|
|
|
153
153
|
|
|
154
154
|
| Command | Summary | MCP tool |
|
|
155
155
|
| --- | --- | --- |
|
|
156
|
-
| `beryl invites send <email>` | Invite someone to the workspace by email |
|
|
157
|
-
| `beryl invites list` | List the workspace's outstanding invitations |
|
|
158
|
-
| `beryl invites revoke <invitation-id>` | Revoke a pending invitation |
|
|
159
|
-
| `beryl invites mine` | List invitations sent to you |
|
|
160
|
-
| `beryl invites accept <invitation>` | Accept an invitation (by id, or by the token from the invite email) |
|
|
161
|
-
| `beryl invites decline <invitation-id>` | Decline an invitation |
|
|
156
|
+
| `beryl invites send <email>` | Invite someone to the workspace by email | none |
|
|
157
|
+
| `beryl invites list` | List the workspace's outstanding invitations | none |
|
|
158
|
+
| `beryl invites revoke <invitation-id>` | Revoke a pending invitation | none |
|
|
159
|
+
| `beryl invites mine` | List invitations sent to you | none |
|
|
160
|
+
| `beryl invites accept <invitation>` | Accept an invitation (by id, or by the token from the invite email) | none |
|
|
161
|
+
| `beryl invites decline <invitation-id>` | Decline an invitation | none |
|
|
162
162
|
|
|
163
163
|
### projects
|
|
164
164
|
|
|
165
|
-
Create and manage projects
|
|
165
|
+
Create and manage projects: a site Beryl explores, authors tests for, and runs.
|
|
166
166
|
|
|
167
167
|
`beryl projects` with no subcommand runs `projects list`.
|
|
168
168
|
|
|
@@ -170,16 +170,16 @@ Create and manage projects — a site Beryl explores, authors tests for, and run
|
|
|
170
170
|
| --- | --- | --- |
|
|
171
171
|
| `beryl projects list` | List projects in the workspace | `projects_list` |
|
|
172
172
|
| `beryl projects get` | Show one project, including its current exploration state | `projects_get` |
|
|
173
|
-
| `beryl projects create [url]` | Create a project
|
|
173
|
+
| `beryl projects create [url]` | Create a project. With a URL the agent starts exploring; with just --name an empty one | `projects_create` |
|
|
174
174
|
| `beryl projects rename <name>` | Rename a project | `projects_rename` |
|
|
175
175
|
| `beryl projects delete` | Delete a project and all its tests and runs | `projects_delete` |
|
|
176
|
-
| `beryl projects re-explore` | Send the agent back in
|
|
176
|
+
| `beryl projects re-explore` | Send the agent back in to run/heal existing tests and discover new flows | none |
|
|
177
177
|
| `beryl projects report` | Aggregate quality report across recent runs (pass rates, flaky tests, trend) | `projects_report` |
|
|
178
178
|
| `beryl projects reusable-auth <url>` | Check whether a saved login can be reused for a URL before creating a project | `projects_reusable_auth` |
|
|
179
179
|
|
|
180
180
|
### envs
|
|
181
181
|
|
|
182
|
-
Manage a project's environments
|
|
182
|
+
Manage a project's environments: the URLs and auth Beryl runs tests against.
|
|
183
183
|
|
|
184
184
|
| Command | Summary | MCP tool |
|
|
185
185
|
| --- | --- | --- |
|
|
@@ -191,17 +191,18 @@ Manage a project's environments — the URLs and auth Beryl runs tests against.
|
|
|
191
191
|
|
|
192
192
|
### schedule
|
|
193
193
|
|
|
194
|
-
|
|
194
|
+
Manage the schedules on which Beryl runs a project's tests automatically. A project can hold many independent schedules; each has its own cadence (daily or weekly), local run time, timezone, and target groups (none = every active test).
|
|
195
195
|
|
|
196
196
|
| Command | Summary | MCP tool |
|
|
197
197
|
| --- | --- | --- |
|
|
198
|
-
| `beryl schedule
|
|
199
|
-
| `beryl schedule
|
|
200
|
-
| `beryl schedule
|
|
198
|
+
| `beryl schedule list` | List the project's schedules | `schedule_list` |
|
|
199
|
+
| `beryl schedule add` | Add a schedule (daily, or weekly on a given day) | `schedule_add` |
|
|
200
|
+
| `beryl schedule update <schedule-id>` | Change a schedule's cadence, time, timezone, or groups | `schedule_update` |
|
|
201
|
+
| `beryl schedule remove <schedule-id>` | Remove a schedule (its tests and groups stay) | `schedule_remove` |
|
|
201
202
|
|
|
202
203
|
### tests
|
|
203
204
|
|
|
204
|
-
Author, inspect, version, and heal a project's tests
|
|
205
|
+
Author, inspect, version, and heal a project's tests: the checks Beryl runs on each run.
|
|
205
206
|
|
|
206
207
|
`beryl tests` with no subcommand runs `tests list`.
|
|
207
208
|
|
|
@@ -211,9 +212,10 @@ Author, inspect, version, and heal a project's tests — the checks Beryl runs o
|
|
|
211
212
|
| `beryl tests list` | List the project's tests with their latest result | `tests_list` |
|
|
212
213
|
| `beryl tests get <test-id>` | Show one test | `tests_get` |
|
|
213
214
|
| `beryl tests plan <test-id>` | Print a test's current step plan (JSON) | `tests_plan` |
|
|
214
|
-
| `beryl tests create` | Create a test case from a JSON action plan
|
|
215
|
+
| `beryl tests create` | Create a test case from a JSON action plan (for tests authored locally, e.g. by your coding agent) | `tests_create` |
|
|
215
216
|
| `beryl tests set-plan <test-id>` | Replace a test's step plan from a JSON file (creates a new version) | `tests_set_plan` |
|
|
216
217
|
| `beryl tests rename <test-id> <title>` | Rename a test | `tests_rename` |
|
|
218
|
+
| `beryl tests set-groups <test-id>` | Replace the groups a test belongs to | `tests_set_groups` |
|
|
217
219
|
| `beryl tests quarantine <test-id> <state>` | Mute a flaky test: it keeps running, but its failures stop failing the run | `tests_quarantine` |
|
|
218
220
|
| `beryl tests delete <test-id>` | Delete a test, its version history, and its results | `tests_delete` |
|
|
219
221
|
| `beryl tests recompile <test-id>` | Validate + verify an edited plan against the live site before persisting | `tests_recompile` |
|
|
@@ -227,6 +229,19 @@ Author, inspect, version, and heal a project's tests — the checks Beryl runs o
|
|
|
227
229
|
| `beryl tests script [test-id]` | Print the rendered Playwright spec for a test (or an unbanked plan file) | `tests_script` |
|
|
228
230
|
| `beryl tests export <test-ids...>` | Export tests as Playwright .spec.ts files in a ZIP | `tests_export` |
|
|
229
231
|
|
|
232
|
+
### groups
|
|
233
|
+
|
|
234
|
+
Manage a project's test groups: labels a test can carry any number of, used to filter, run, or schedule a slice of the suite. Groups are created only here (or in Settings → Groups); assigning a test to an unknown name is an error.
|
|
235
|
+
|
|
236
|
+
`beryl groups` with no subcommand runs `groups list`.
|
|
237
|
+
|
|
238
|
+
| Command | Summary | MCP tool |
|
|
239
|
+
| --- | --- | --- |
|
|
240
|
+
| `beryl groups list` | List the project's test groups and how many tests each holds | `groups_list` |
|
|
241
|
+
| `beryl groups create <name>` | Create a group | `groups_create` |
|
|
242
|
+
| `beryl groups rename <group> <name>` | Rename a group (tests keep their membership) | `groups_rename` |
|
|
243
|
+
| `beryl groups delete <group>` | Delete a group: its tests stay; a schedule targeting only this group goes with it | `groups_delete` |
|
|
244
|
+
|
|
230
245
|
### runs
|
|
231
246
|
|
|
232
247
|
Trigger a run of a project's tests (e.g. in CI), then watch, inspect, and download results.
|
|
@@ -247,7 +262,7 @@ Trigger a run of a project's tests (e.g. in CI), then watch, inspect, and downlo
|
|
|
247
262
|
|
|
248
263
|
### health
|
|
249
264
|
|
|
250
|
-
Site Health
|
|
265
|
+
Site Health, or how your site reads to search engines and visitors: content, speed, mobile, links and security, graded from a real check of your live pages.
|
|
251
266
|
|
|
252
267
|
`beryl health` with no subcommand runs `health get`.
|
|
253
268
|
|
|
@@ -258,15 +273,15 @@ Site Health — how your site reads to search engines and visitors: content, spe
|
|
|
258
273
|
|
|
259
274
|
### explorations
|
|
260
275
|
|
|
261
|
-
Inspect the agent's exploration runs
|
|
276
|
+
Inspect the agent's exploration runs: how it crawled a site and authored its tests.
|
|
262
277
|
|
|
263
278
|
| Command | Summary | MCP tool |
|
|
264
279
|
| --- | --- | --- |
|
|
265
|
-
| `beryl explorations list` | List the agent's exploration passes for a project |
|
|
266
|
-
| `beryl explorations get <exploration-id>` | Show one exploration: authored tests, abandoned flows, coverage, frontier |
|
|
267
|
-
| `beryl explorations steps <exploration-id>` | List every step the agent took in an exploration |
|
|
268
|
-
| `beryl explorations cancel <exploration-id>` | Cancel an in-flight exploration |
|
|
269
|
-
| `beryl explorations watch <exploration-id>` | Stream an exploration live
|
|
280
|
+
| `beryl explorations list` | List the agent's exploration passes for a project | none |
|
|
281
|
+
| `beryl explorations get <exploration-id>` | Show one exploration: authored tests, abandoned flows, coverage, frontier | none |
|
|
282
|
+
| `beryl explorations steps <exploration-id>` | List every step the agent took in an exploration | none |
|
|
283
|
+
| `beryl explorations cancel <exploration-id>` | Cancel an in-flight exploration | none |
|
|
284
|
+
| `beryl explorations watch <exploration-id>` | Stream an exploration live: watch the agent explore and author tests | none |
|
|
270
285
|
|
|
271
286
|
### config
|
|
272
287
|
|
|
@@ -290,7 +305,7 @@ Manage the variables, secrets, and files the agent can use while exploring and r
|
|
|
290
305
|
|
|
291
306
|
### mailbox
|
|
292
307
|
|
|
293
|
-
The project's standing email addresses
|
|
308
|
+
The project's standing email addresses, where its tests receive sign-in mail.
|
|
294
309
|
|
|
295
310
|
`beryl mailbox` with no subcommand runs `mailbox get`.
|
|
296
311
|
|
|
@@ -305,14 +320,14 @@ The project's standing email addresses — where its tests receive sign-in mail.
|
|
|
305
320
|
|
|
306
321
|
### accounts
|
|
307
322
|
|
|
308
|
-
Durable identities on the site under test
|
|
323
|
+
Durable identities on the site under test: what an authenticated test signs in as.
|
|
309
324
|
|
|
310
325
|
`beryl accounts` with no subcommand runs `accounts list`.
|
|
311
326
|
|
|
312
327
|
| Command | Summary | MCP tool |
|
|
313
328
|
| --- | --- | --- |
|
|
314
329
|
| `beryl accounts list` | List the test accounts an environment's tests sign in as | `accounts_list` |
|
|
315
|
-
| `beryl accounts create` | Add a test account
|
|
330
|
+
| `beryl accounts create` | Add a test account: the customer's own, or one Beryl signs up | `accounts_create` |
|
|
316
331
|
| `beryl accounts provision <account-id>` | Prove a test account can get in, by replaying a plan that ends logged in | `accounts_provision` |
|
|
317
332
|
| `beryl accounts set-login <account-id>` | Store the sign-in plan a run replays once, plus the probe that proves it | `accounts_set_login` |
|
|
318
333
|
| `beryl accounts get-login <account-id>` | Read the stored sign-in plan, its probe, and the hash a safe write must cite | `accounts_get_login` |
|
|
@@ -326,9 +341,9 @@ View and update your personal account profile.
|
|
|
326
341
|
|
|
327
342
|
| Command | Summary | MCP tool |
|
|
328
343
|
| --- | --- | --- |
|
|
329
|
-
| `beryl account get` | Show your account profile |
|
|
330
|
-
| `beryl account update` | Update your profile |
|
|
331
|
-
| `beryl account deletion-preview` | Preview what deleting your account would remove or leave |
|
|
344
|
+
| `beryl account get` | Show your account profile | none |
|
|
345
|
+
| `beryl account update` | Update your profile | none |
|
|
346
|
+
| `beryl account deletion-preview` | Preview what deleting your account would remove or leave | none |
|
|
332
347
|
|
|
333
348
|
### feedback
|
|
334
349
|
|
|
@@ -336,7 +351,7 @@ Send product feedback to the Beryl team.
|
|
|
336
351
|
|
|
337
352
|
| Command | Summary | MCP tool |
|
|
338
353
|
| --- | --- | --- |
|
|
339
|
-
| `beryl feedback send <message>` | Send product feedback to the Beryl team |
|
|
354
|
+
| `beryl feedback send <message>` | Send product feedback to the Beryl team | none |
|
|
340
355
|
|
|
341
356
|
### billing
|
|
342
357
|
|
|
@@ -344,10 +359,10 @@ Review a workspace's plan usage, subscription, and invoices.
|
|
|
344
359
|
|
|
345
360
|
| Command | Summary | MCP tool |
|
|
346
361
|
| --- | --- | --- |
|
|
347
|
-
| `beryl billing usage` | Show plan usage: monthly cloud run minutes |
|
|
348
|
-
| `beryl billing subscription` | Show the workspace's subscription |
|
|
349
|
-
| `beryl billing invoices` | List recent invoices |
|
|
350
|
-
| `beryl billing portal` | Get a Stripe billing-portal link for the workspace |
|
|
362
|
+
| `beryl billing usage` | Show plan usage: monthly cloud run minutes | none |
|
|
363
|
+
| `beryl billing subscription` | Show the workspace's subscription | none |
|
|
364
|
+
| `beryl billing invoices` | List recent invoices | none |
|
|
365
|
+
| `beryl billing portal` | Get a Stripe billing-portal link for the workspace | none |
|
|
351
366
|
|
|
352
367
|
### version
|
|
353
368
|
|
|
@@ -359,18 +374,18 @@ Show the running CLI version, API URL, and Node version
|
|
|
359
374
|
|
|
360
375
|
### mcp
|
|
361
376
|
|
|
362
|
-
Run the Beryl MCP server (stdio)
|
|
377
|
+
Run the Beryl MCP server (stdio): every CLI command as an agent tool
|
|
363
378
|
|
|
364
379
|
| Command | Summary | MCP tool |
|
|
365
380
|
| --- | --- | --- |
|
|
366
|
-
| `beryl mcp` | Run the Beryl MCP server (stdio)
|
|
381
|
+
| `beryl mcp` | Run the Beryl MCP server (stdio): every CLI command as an agent tool | none |
|
|
367
382
|
|
|
368
383
|
Run `beryl <command> --help` for flags and examples.
|
|
369
384
|
|
|
370
385
|
## Global flags
|
|
371
386
|
|
|
372
|
-
- `-V, --version
|
|
373
|
-
- `--json
|
|
374
|
-
- `--api-url
|
|
375
|
-
- `--token
|
|
376
|
-
- `-h, --help
|
|
387
|
+
- `-V, --version`: Print the CLI version and exit
|
|
388
|
+
- `--json`: Print machine-readable JSON (NDJSON for streams)
|
|
389
|
+
- `--api-url`: API base URL (default https://api.beryl.so)
|
|
390
|
+
- `--token`: Personal access token (overrides config/BERYL_API_KEY)
|
|
391
|
+
- `-h, --help`: Show help
|
package/dist/beryl-test-skill.js
CHANGED
|
@@ -289,6 +289,9 @@ ${JSON.stringify(BERYL_TEST_SKILL_EXAMPLE_PLAN, null, 2)
|
|
|
289
289
|
description. Inspect the exact spec with \`beryl tests script --file plan.json\`.
|
|
290
290
|
Full ActionPlan JSON Schema:
|
|
291
291
|
https://api.beryl.so/api/v1/schemas/action-plan.schema.json.
|
|
292
|
+
Groups are optional and never block: after the test is banked, if \`beryl groups list\`
|
|
293
|
+
shows any, offer them once (\`tests set-groups <id> --group <name>\`); no answer = leave
|
|
294
|
+
it ungrouped and move on. Never create a group yourself — that's the user's call.
|
|
292
295
|
|
|
293
296
|
### The outcome assertion is the whole game
|
|
294
297
|
|
|
@@ -37,7 +37,7 @@ export const testAccountCommands = [
|
|
|
37
37
|
name: "accounts list",
|
|
38
38
|
summary: "List the test accounts an environment's tests sign in as",
|
|
39
39
|
groupDefault: true,
|
|
40
|
-
groupSummary: "Durable identities on the site under test
|
|
40
|
+
groupSummary: "Durable identities on the site under test: what an authenticated test signs in as.",
|
|
41
41
|
description: "Every test account on the project, with the identity label a plan's `auth_label` " +
|
|
42
42
|
"names. The row marked * is the default: what a plan gets when it sets " +
|
|
43
43
|
"requires_auth without an auth_label.",
|
|
@@ -59,10 +59,10 @@ export const testAccountCommands = [
|
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
61
|
name: "accounts create",
|
|
62
|
-
summary: "Add a test account
|
|
62
|
+
summary: "Add a test account: the customer's own, or one Beryl signs up",
|
|
63
63
|
description: "Two kinds. `--type user_provided` records a dedicated account you already have on " +
|
|
64
64
|
"the site: pass --email and --password, and it is usable immediately. `--type beryl` " +
|
|
65
|
-
"reserves one Beryl will sign up itself, addressed at the project mailbox
|
|
65
|
+
"reserves one Beryl will sign up itself, addressed at the project mailbox. It starts " +
|
|
66
66
|
"`pending` and becomes usable after `beryl accounts provision`. " +
|
|
67
67
|
"The first account an environment gets is its default whatever you pass.",
|
|
68
68
|
scope: "project",
|
|
@@ -124,11 +124,11 @@ export const testAccountCommands = [
|
|
|
124
124
|
name: "accounts provision",
|
|
125
125
|
summary: "Prove a test account can get in, by replaying a plan that ends logged in",
|
|
126
126
|
description: "Runs the plan once in a real browser and, if it passes, marks the account ready. " +
|
|
127
|
-
"The plan is an ordinary ActionPlan that ends logged in
|
|
127
|
+
"The plan is an ordinary ActionPlan that ends logged in: a SIGNUP when the account " +
|
|
128
128
|
"does not exist yet, or a SIGN-IN when it already does (you created it by hand, or " +
|
|
129
129
|
"the site already had it). Either proves the same thing, and a sign-in is what every " +
|
|
130
130
|
"later test will do anyway. Type {{mailbox_address}} into the email field and " +
|
|
131
|
-
"{{login_password}} into the password field
|
|
131
|
+
"{{login_password}} into the password field. Both resolve at replay time, and an " +
|
|
132
132
|
"`await_email` step reads the project mailbox, so a verification code works.",
|
|
133
133
|
scope: "project",
|
|
134
134
|
args: [{ name: "account-id", description: "Account id from `beryl accounts list`", required: true }],
|
|
@@ -170,7 +170,7 @@ export const testAccountCommands = [
|
|
|
170
170
|
"field; an emailed code or magic link arrives at the account's own address, so an " +
|
|
171
171
|
"`await_email` step reads it with no human involved.\n\n" +
|
|
172
172
|
"--probe is the liveness check: a two-step plan (goto a gated page, then a POSITIVE " +
|
|
173
|
-
"assertion that only holds when signed in
|
|
173
|
+
"assertion that only holds when signed in: the account menu, a 'Sign out' control). " +
|
|
174
174
|
"It is replayed in a fresh browser carrying only the captured session. It is " +
|
|
175
175
|
"REQUIRED, and not a formality: assertions like `hidden`, `count 0`, and a URL " +
|
|
176
176
|
"match on a redirect all pass against a logged-out page, so without a positive " +
|
package/dist/commands/auth.js
CHANGED
|
@@ -97,7 +97,7 @@ export const authCommands = [
|
|
|
97
97
|
summary: "Authenticate the CLI with your Beryl account",
|
|
98
98
|
description: "Signs in via your browser (a code you confirm at beryl.so) and stores a personal " +
|
|
99
99
|
"access token in the CLI config. Pass --otp (or --email) to skip the browser and " +
|
|
100
|
-
"sign in with an emailed one-time code instead
|
|
100
|
+
"sign in with an emailed one-time code instead. A new email gets an account " +
|
|
101
101
|
"created automatically. Pass --token to use an existing token from Account → API " +
|
|
102
102
|
"tokens. Pass --email plus --code (the 6 digits from the email, e.g. read from a " +
|
|
103
103
|
"`beryl inbox`) to complete the OTP flow without a prompt. In CI, prefer the " +
|
|
@@ -108,18 +108,18 @@ export const authCommands = [
|
|
|
108
108
|
{
|
|
109
109
|
name: "otp",
|
|
110
110
|
type: "boolean",
|
|
111
|
-
description: "Skip the browser
|
|
111
|
+
description: "Skip the browser and sign in with an emailed code",
|
|
112
112
|
},
|
|
113
113
|
{
|
|
114
114
|
name: "email",
|
|
115
115
|
type: "string",
|
|
116
116
|
description: "Email for the one-time code sign-in (default: your last sign-in, or your git " +
|
|
117
|
-
"user.email
|
|
117
|
+
"user.email, offered as the prompt default)",
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
120
|
name: "code",
|
|
121
121
|
type: "string",
|
|
122
|
-
description: "The emailed 6-digit code
|
|
122
|
+
description: "The emailed 6-digit code. Skips the prompt for non-interactive use " +
|
|
123
123
|
"(requires --email; also skips sending a fresh code, so pair it with a " +
|
|
124
124
|
"code already requested via `beryl login`, `beryl signup`, or the API)",
|
|
125
125
|
},
|
|
@@ -184,7 +184,7 @@ export const authCommands = [
|
|
|
184
184
|
name: "signup",
|
|
185
185
|
summary: "Register a new Beryl account (emails a 6-digit verification code)",
|
|
186
186
|
description: "Creates a passwordless account for the email and sends it a 6-digit code. " +
|
|
187
|
-
"Finish with `beryl login --email <addr> --code <the 6 digits
|
|
187
|
+
"Finish with `beryl login --email <addr> --code <the 6 digits>`, which verifies " +
|
|
188
188
|
"the account, creates its workspace, and signs the CLI in. With an inbox from " +
|
|
189
189
|
"`beryl inbox create` as the address, an agent can provision a fresh account " +
|
|
190
190
|
"end-to-end with no human at a prompt.",
|
|
@@ -57,8 +57,8 @@ export const configCommands = [
|
|
|
57
57
|
{
|
|
58
58
|
name: "config vars get",
|
|
59
59
|
summary: "Show one config variable",
|
|
60
|
-
description: "Returns the variable row with its value in plaintext (variables are not secret)
|
|
61
|
-
"
|
|
60
|
+
description: "Returns the variable row with its value in plaintext (variables are not secret). " +
|
|
61
|
+
"A sensitive value lives in `config secrets`, readable only via `config secrets " +
|
|
62
62
|
"get --reveal`.",
|
|
63
63
|
scope: "project",
|
|
64
64
|
args: [{ name: "key", description: "Variable key or id", required: true }],
|
|
@@ -116,15 +116,15 @@ export const configCommands = [
|
|
|
116
116
|
{
|
|
117
117
|
name: "config secrets get",
|
|
118
118
|
summary: "Show one secret's metadata, or reveal its value with --reveal",
|
|
119
|
-
description: "Returns metadata only by default; --reveal is a logged, member-gated decrypt
|
|
120
|
-
"unlike `config vars get`, which returns its value in plaintext.",
|
|
119
|
+
description: "Returns metadata only by default; --reveal is a logged, member-gated decrypt " +
|
|
120
|
+
"(unlike `config vars get`, which returns its value in plaintext).",
|
|
121
121
|
scope: "project",
|
|
122
122
|
args: [{ name: "key", description: "Secret key or id", required: true }],
|
|
123
123
|
flags: [
|
|
124
124
|
{
|
|
125
125
|
name: "reveal",
|
|
126
126
|
type: "boolean",
|
|
127
|
-
description: "Return the decrypted value
|
|
127
|
+
description: "Return the decrypted value: the explicit read that lets the agent drive a " +
|
|
128
128
|
"real login while authoring. Member-gated; every reveal is logged",
|
|
129
129
|
},
|
|
130
130
|
{
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
import { UsageError } from "../errors.js";
|
|
2
|
-
import { arg, flagBool, flagNum, flagStr, projectPath } from "./util.js";
|
|
2
|
+
import { arg, findGroup, flagBool, flagNum, flagStr, projectPath } from "./util.js";
|
|
3
|
+
const SCHEDULE_GROUPS_FLAG = "Comma-separated group names or ids the schedule targets (the run covers the union of their active tests at fire time); omit to run every active test";
|
|
4
|
+
const SCHEDULE_CADENCE_FLAGS = [
|
|
5
|
+
{
|
|
6
|
+
name: "frequency",
|
|
7
|
+
type: "string",
|
|
8
|
+
enum: ["daily", "weekly"],
|
|
9
|
+
description: "How often (default daily)",
|
|
10
|
+
},
|
|
11
|
+
{ name: "day", type: "number", description: "Weekly only: day of week, 0=Monday … 6=Sunday" },
|
|
12
|
+
{ name: "hour", type: "number", description: "Hour of day 0-23" },
|
|
13
|
+
{ name: "minute", type: "number", description: "Minute 0-59" },
|
|
14
|
+
{ name: "tz", type: "string", description: "IANA timezone (e.g. America/Los_Angeles)" },
|
|
15
|
+
];
|
|
3
16
|
export const environmentCommands = [
|
|
4
17
|
{
|
|
5
18
|
name: "envs list",
|
|
6
19
|
summary: "List a project's environments",
|
|
7
20
|
scope: "project",
|
|
8
|
-
groupSummary: "Manage a project's environments
|
|
21
|
+
groupSummary: "Manage a project's environments: the URLs and auth Beryl runs tests against.",
|
|
9
22
|
async run(ctx, input) {
|
|
10
23
|
const { workspaceId, projectId } = await ctx.requireProject(input);
|
|
11
24
|
return { data: await ctx.client.get(`${projectPath(workspaceId, projectId)}/environments`) };
|
|
@@ -91,57 +104,110 @@ export const environmentCommands = [
|
|
|
91
104
|
},
|
|
92
105
|
},
|
|
93
106
|
{
|
|
94
|
-
name: "schedule
|
|
95
|
-
summary: "
|
|
107
|
+
name: "schedule list",
|
|
108
|
+
summary: "List the project's schedules",
|
|
96
109
|
scope: "project",
|
|
97
|
-
groupSummary: "
|
|
110
|
+
groupSummary: "Manage the schedules on which Beryl runs a project's tests automatically. A project can hold many independent schedules; each has its own cadence (daily or weekly), local run time, timezone, and target groups (none = every active test).",
|
|
98
111
|
async run(ctx, input) {
|
|
99
112
|
const { workspaceId, projectId } = await ctx.requireProject(input);
|
|
100
|
-
return { data: await ctx.client.get(`${projectPath(workspaceId, projectId)}/
|
|
113
|
+
return { data: await ctx.client.get(`${projectPath(workspaceId, projectId)}/schedules`) };
|
|
101
114
|
},
|
|
102
115
|
},
|
|
103
116
|
{
|
|
104
|
-
name: "schedule
|
|
105
|
-
summary: "
|
|
117
|
+
name: "schedule add",
|
|
118
|
+
summary: "Add a schedule (daily, or weekly on a given day)",
|
|
106
119
|
scope: "project",
|
|
107
120
|
flags: [
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
{ name: "day", type: "number", description: "Weekly only: day of week, 0=Monday … 6=Sunday" },
|
|
115
|
-
{ name: "hour", type: "number", description: "Hour of day 0-23" },
|
|
116
|
-
{ name: "minute", type: "number", description: "Minute 0-59" },
|
|
117
|
-
{ name: "tz", type: "string", description: "IANA timezone (e.g. America/Los_Angeles)" },
|
|
121
|
+
...SCHEDULE_CADENCE_FLAGS,
|
|
122
|
+
{ name: "groups", type: "string", description: SCHEDULE_GROUPS_FLAG },
|
|
123
|
+
],
|
|
124
|
+
examples: [
|
|
125
|
+
"beryl schedule add --frequency daily --hour 6 --tz UTC",
|
|
126
|
+
'beryl schedule add --groups "Smoke,Checkout" --frequency weekly --day 0 --hour 9 --minute 15 --tz UTC',
|
|
118
127
|
],
|
|
119
|
-
examples: ["beryl schedule set --frequency daily --hour 6 --tz UTC"],
|
|
120
128
|
async run(ctx, input) {
|
|
121
129
|
const { workspaceId, projectId } = await ctx.requireProject(input);
|
|
122
130
|
return {
|
|
123
|
-
data: await ctx.client.
|
|
124
|
-
enabled: true,
|
|
131
|
+
data: await ctx.client.post(`${projectPath(workspaceId, projectId)}/schedules`, {
|
|
125
132
|
frequency: flagStr(input, "frequency") ?? "daily",
|
|
126
133
|
day_of_week: flagNum(input, "day") ?? null,
|
|
127
134
|
run_hour: flagNum(input, "hour"),
|
|
128
135
|
run_minute: flagNum(input, "minute"),
|
|
129
136
|
timezone: flagStr(input, "tz"),
|
|
137
|
+
group_ids: await resolveGroupIds(ctx, input, workspaceId, projectId),
|
|
130
138
|
}),
|
|
131
139
|
};
|
|
132
140
|
},
|
|
133
141
|
},
|
|
134
142
|
{
|
|
135
|
-
name: "schedule
|
|
136
|
-
summary: "
|
|
143
|
+
name: "schedule update",
|
|
144
|
+
summary: "Change a schedule's cadence, time, timezone, or groups",
|
|
137
145
|
scope: "project",
|
|
146
|
+
args: [{ name: "schedule-id", description: "Schedule id (see schedule list)", required: true }],
|
|
147
|
+
flags: [
|
|
148
|
+
...SCHEDULE_CADENCE_FLAGS,
|
|
149
|
+
{ name: "groups", type: "string", description: SCHEDULE_GROUPS_FLAG },
|
|
150
|
+
{
|
|
151
|
+
name: "all-tests",
|
|
152
|
+
type: "boolean",
|
|
153
|
+
description: "Target every active test (clears the schedule's groups)",
|
|
154
|
+
},
|
|
155
|
+
],
|
|
138
156
|
async run(ctx, input) {
|
|
139
157
|
const { workspaceId, projectId } = await ctx.requireProject(input);
|
|
158
|
+
const scheduleId = arg(input, "schedule-id");
|
|
159
|
+
const schedules = (await ctx.client.get(`${projectPath(workspaceId, projectId)}/schedules`));
|
|
160
|
+
const existing = schedules.find((sch) => sch.id === scheduleId);
|
|
161
|
+
if (!existing)
|
|
162
|
+
throw new UsageError(`No schedule '${scheduleId}' in this project.`);
|
|
163
|
+
if (flagBool(input, "all-tests") && flagStr(input, "groups"))
|
|
164
|
+
throw new UsageError("--all-tests and --groups are mutually exclusive");
|
|
165
|
+
const groupIds = flagBool(input, "all-tests")
|
|
166
|
+
? []
|
|
167
|
+
: flagStr(input, "groups")
|
|
168
|
+
? await resolveGroupIds(ctx, input, workspaceId, projectId)
|
|
169
|
+
: existing.group_ids;
|
|
140
170
|
return {
|
|
141
|
-
data: await ctx.client.put(`${projectPath(workspaceId, projectId)}/
|
|
142
|
-
|
|
171
|
+
data: await ctx.client.put(`${projectPath(workspaceId, projectId)}/schedules/${scheduleId}`, {
|
|
172
|
+
frequency: flagStr(input, "frequency") ?? existing.frequency,
|
|
173
|
+
day_of_week: flagNum(input, "day") ?? existing.day_of_week,
|
|
174
|
+
run_hour: flagNum(input, "hour") ?? existing.run_hour,
|
|
175
|
+
run_minute: flagNum(input, "minute") ?? existing.run_minute,
|
|
176
|
+
timezone: flagStr(input, "tz") ?? existing.timezone,
|
|
177
|
+
group_ids: groupIds,
|
|
143
178
|
}),
|
|
144
179
|
};
|
|
145
180
|
},
|
|
146
181
|
},
|
|
182
|
+
{
|
|
183
|
+
name: "schedule remove",
|
|
184
|
+
summary: "Remove a schedule (its tests and groups stay)",
|
|
185
|
+
scope: "project",
|
|
186
|
+
args: [{ name: "schedule-id", description: "Schedule id (see schedule list)", required: true }],
|
|
187
|
+
async run(ctx, input) {
|
|
188
|
+
const { workspaceId, projectId } = await ctx.requireProject(input);
|
|
189
|
+
await ctx.client.del(`${projectPath(workspaceId, projectId)}/schedules/${arg(input, "schedule-id")}`);
|
|
190
|
+
return { human: "Removed." };
|
|
191
|
+
},
|
|
192
|
+
},
|
|
147
193
|
];
|
|
194
|
+
async function resolveGroupIds(ctx, input, workspaceId, projectId) {
|
|
195
|
+
const raw = flagStr(input, "groups");
|
|
196
|
+
if (!raw)
|
|
197
|
+
return [];
|
|
198
|
+
const refs = raw
|
|
199
|
+
.split(",")
|
|
200
|
+
.map((r) => r.trim())
|
|
201
|
+
.filter(Boolean);
|
|
202
|
+
if (refs.length === 0)
|
|
203
|
+
return [];
|
|
204
|
+
const groups = (await ctx.client.get(`${projectPath(workspaceId, projectId)}/groups`));
|
|
205
|
+
return refs.map((ref) => {
|
|
206
|
+
const group = findGroup(groups, ref);
|
|
207
|
+
if (!group) {
|
|
208
|
+
const names = groups.map((g) => g.name).join(", ") || "none yet";
|
|
209
|
+
throw new UsageError(`No group '${ref}' in this project (existing: ${names}).`);
|
|
210
|
+
}
|
|
211
|
+
return group.id;
|
|
212
|
+
});
|
|
213
|
+
}
|
|
@@ -3,7 +3,7 @@ import { watchExploration } from "./watch.js";
|
|
|
3
3
|
export const explorationCommands = [
|
|
4
4
|
{
|
|
5
5
|
name: "explorations list",
|
|
6
|
-
groupSummary: "Inspect the agent's exploration runs
|
|
6
|
+
groupSummary: "Inspect the agent's exploration runs: how it crawled a site and authored its tests.",
|
|
7
7
|
summary: "List the agent's exploration passes for a project",
|
|
8
8
|
scope: "project",
|
|
9
9
|
async run(ctx, input) {
|
|
@@ -38,7 +38,7 @@ export const explorationCommands = [
|
|
|
38
38
|
{
|
|
39
39
|
name: "explorations cancel",
|
|
40
40
|
summary: "Cancel an in-flight exploration",
|
|
41
|
-
description: "Stops a running exploration without touching the project or its tests
|
|
41
|
+
description: "Stops a running exploration without touching the project or its tests: the " +
|
|
42
42
|
"lever for a runaway/non-convergent pass burning agent budget.",
|
|
43
43
|
scope: "project",
|
|
44
44
|
args: [{ name: "exploration-id", description: "Exploration id", required: true }],
|
|
@@ -51,7 +51,7 @@ export const explorationCommands = [
|
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
53
|
name: "explorations watch",
|
|
54
|
-
summary: "Stream an exploration live
|
|
54
|
+
summary: "Stream an exploration live: watch the agent explore and author tests",
|
|
55
55
|
description: "Replays every recorded step on connect, then follows live until the exploration " +
|
|
56
56
|
"completes or fails.",
|
|
57
57
|
scope: "project",
|