@beryl-so/cli 0.17.0 → 0.22.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 +35 -7
- package/dist/adapters/cli.js +15 -1
- package/dist/adapters/mcp.js +44 -5
- package/dist/beryl-test-skill.js +275 -90
- package/dist/commands/accounts.js +331 -0
- package/dist/commands/environments.js +0 -2
- package/dist/commands/init.js +43 -27
- package/dist/commands/mailboxes.js +159 -0
- package/dist/commands/mcp.js +25 -0
- package/dist/commands/projects.js +0 -6
- package/dist/commands/runs.js +159 -46
- package/dist/commands/tests.js +29 -6
- package/dist/email-pump.js +5 -2
- package/dist/http.js +4 -1
- package/dist/local-exec.js +86 -27
- package/dist/local-run.js +21 -3
- package/dist/playwright-install.js +118 -7
- package/dist/registry/index.js +4 -2
- package/dist/schema.generated.js +32 -0
- package/package.json +2 -2
- package/dist/commands/inboxes.js +0 -145
package/README.md
CHANGED
|
@@ -288,17 +288,37 @@ Send run outcomes to a Slack channel via a per-project incoming webhook.
|
|
|
288
288
|
| `beryl slack clear` | Remove the project's Slack webhook (stops all alerts) | — |
|
|
289
289
|
| `beryl slack test` | Post a sample alert to the configured webhook | — |
|
|
290
290
|
|
|
291
|
-
###
|
|
291
|
+
### mailbox
|
|
292
292
|
|
|
293
|
-
|
|
293
|
+
The project's standing email addresses — where its tests receive sign-in mail.
|
|
294
|
+
|
|
295
|
+
`beryl mailbox` with no subcommand runs `mailbox get`.
|
|
296
|
+
|
|
297
|
+
| Command | Summary | MCP tool |
|
|
298
|
+
| --- | --- | --- |
|
|
299
|
+
| `beryl mailbox get` | The project's mailbox address | `mailbox_get` |
|
|
300
|
+
| `beryl mailbox list` | List the project's mailboxes | `mailbox_list` |
|
|
301
|
+
| `beryl mailbox create` | Add a second mailbox to the project | `mailbox_create` |
|
|
302
|
+
| `beryl mailbox delete <mailbox-id>` | Delete a mailbox and every email it has received | `mailbox_delete` |
|
|
303
|
+
| `beryl mailbox read <mailbox-id>` | Read the latest email in a mailbox (waits for one to arrive) | `mailbox_read` |
|
|
304
|
+
| `beryl mailbox emails <mailbox-id>` | List the emails a mailbox has received | `mailbox_emails` |
|
|
305
|
+
|
|
306
|
+
### accounts
|
|
307
|
+
|
|
308
|
+
Durable identities on the site under test — what an authenticated test signs in as.
|
|
309
|
+
|
|
310
|
+
`beryl accounts` with no subcommand runs `accounts list`.
|
|
294
311
|
|
|
295
312
|
| Command | Summary | MCP tool |
|
|
296
313
|
| --- | --- | --- |
|
|
297
|
-
| `beryl
|
|
298
|
-
| `beryl
|
|
299
|
-
| `beryl
|
|
300
|
-
| `beryl
|
|
301
|
-
| `beryl
|
|
314
|
+
| `beryl accounts list` | List the test accounts an environment's tests sign in as | `accounts_list` |
|
|
315
|
+
| `beryl accounts create` | Add a test account — the customer's own, or one Beryl signs up | `accounts_create` |
|
|
316
|
+
| `beryl accounts provision <account-id>` | Prove a test account can get in, by replaying a plan that ends logged in | `accounts_provision` |
|
|
317
|
+
| `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
|
+
| `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` |
|
|
319
|
+
| `beryl accounts check <account-id>` | Sign in now and prove the session survives into a fresh browser | `accounts_check` |
|
|
320
|
+
| `beryl accounts update <account-id>` | Change a test account's password, login method, or default flag | `accounts_update` |
|
|
321
|
+
| `beryl accounts delete <account-id>` | Delete a test account | `accounts_delete` |
|
|
302
322
|
|
|
303
323
|
### account
|
|
304
324
|
|
|
@@ -329,6 +349,14 @@ Review a workspace's plan usage, subscription, and invoices.
|
|
|
329
349
|
| `beryl billing invoices` | List recent invoices | — |
|
|
330
350
|
| `beryl billing portal` | Get a Stripe billing-portal link for the workspace | — |
|
|
331
351
|
|
|
352
|
+
### version
|
|
353
|
+
|
|
354
|
+
Show the running CLI version, API URL, and Node version
|
|
355
|
+
|
|
356
|
+
| Command | Summary | MCP tool |
|
|
357
|
+
| --- | --- | --- |
|
|
358
|
+
| `beryl version` | Show the running CLI version, API URL, and Node version | `version` |
|
|
359
|
+
|
|
332
360
|
### mcp
|
|
333
361
|
|
|
334
362
|
Run the Beryl MCP server (stdio) — every CLI command as an agent tool
|
package/dist/adapters/cli.js
CHANGED
|
@@ -230,6 +230,8 @@ export async function runCli(argv) {
|
|
|
230
230
|
// flag we don't recognise as global — that one belongs to the command.
|
|
231
231
|
const words = [];
|
|
232
232
|
const wordIndices = [];
|
|
233
|
+
let sawGlobalFlag = false;
|
|
234
|
+
let sawVersionFlag = false;
|
|
233
235
|
for (let i = 0; i < argv.length; i++) {
|
|
234
236
|
const tok = argv[i];
|
|
235
237
|
if (tok === "--")
|
|
@@ -238,6 +240,9 @@ export async function runCli(argv) {
|
|
|
238
240
|
const span = globalFlagSpan(argv, i);
|
|
239
241
|
if (span === 0)
|
|
240
242
|
break;
|
|
243
|
+
sawGlobalFlag = true;
|
|
244
|
+
if (tok === "--version" || tok === "-V")
|
|
245
|
+
sawVersionFlag = true;
|
|
241
246
|
i += span - 1;
|
|
242
247
|
continue;
|
|
243
248
|
}
|
|
@@ -246,7 +251,16 @@ export async function runCli(argv) {
|
|
|
246
251
|
}
|
|
247
252
|
let rest = argv;
|
|
248
253
|
let restWordIndices = wordIndices;
|
|
249
|
-
|
|
254
|
+
// `-V`, `--version`, and a plain `beryl version` keep printing just the number: that
|
|
255
|
+
// one-line output is a contract CI users parse, so it stays intercepted here rather than
|
|
256
|
+
// going through the registry (which would render the full key/value block instead).
|
|
257
|
+
// Any global flag alongside the `version` word (`--json`, `--api-url`, `--help`, …) means
|
|
258
|
+
// the caller wants more than the number, so it falls through to the `version` spec — the
|
|
259
|
+
// same build picture the `version` MCP tool returns (api_url, Node, staleness). Only flags
|
|
260
|
+
// the scanner above actually recognised count, so it honours `--` the same way (tokens
|
|
261
|
+
// after it are inert positionals, not a request for the spec).
|
|
262
|
+
const versionWord = words[0] === "version" && words.length === 1;
|
|
263
|
+
if (sawVersionFlag || (versionWord && !sawGlobalFlag)) {
|
|
250
264
|
process.stdout.write(cliVersion() + "\n");
|
|
251
265
|
return EXIT_OK;
|
|
252
266
|
}
|
package/dist/adapters/mcp.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import { loadConfig } from "../config.js";
|
|
4
6
|
import { createContext } from "../context.js";
|
|
5
7
|
import { CliError } from "../errors.js";
|
|
8
|
+
import { ApiClient } from "../http.js";
|
|
6
9
|
import { commands } from "../registry/index.js";
|
|
7
10
|
import { cliVersion, warnIfStale } from "../version-check.js";
|
|
8
11
|
export function toolName(spec) {
|
|
@@ -71,15 +74,50 @@ function toInput(spec, params) {
|
|
|
71
74
|
}
|
|
72
75
|
return { args, flags };
|
|
73
76
|
}
|
|
77
|
+
let authCache;
|
|
78
|
+
/** The credentials as they are on disk RIGHT NOW, not as they were at spawn.
|
|
79
|
+
*
|
|
80
|
+
* This server is long-lived and never re-execs, so reading the token once would leave a
|
|
81
|
+
* `beryl login` performed afterwards invisible to it forever — and the 401 it then
|
|
82
|
+
* returns tells you to run the login you just ran, which is unescapable without knowing
|
|
83
|
+
* to reconnect. Keyed on mtime so the common path is a stat, not a rebuilt client. */
|
|
84
|
+
export function currentAuth(fallback) {
|
|
85
|
+
let mtimeMs = 0;
|
|
86
|
+
try {
|
|
87
|
+
mtimeMs = fs.statSync(fallback.config.globalConfigPath).mtimeMs;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// No config file (env-var auth, or never logged in) — nothing to watch.
|
|
91
|
+
}
|
|
92
|
+
if (authCache?.mtimeMs === mtimeMs)
|
|
93
|
+
return authCache.auth;
|
|
94
|
+
const config = loadConfig();
|
|
95
|
+
const auth = {
|
|
96
|
+
client: new ApiClient(config.apiUrl, config.token),
|
|
97
|
+
config,
|
|
98
|
+
};
|
|
99
|
+
authCache = { mtimeMs, auth };
|
|
100
|
+
return auth;
|
|
101
|
+
}
|
|
102
|
+
export function __resetAuthCacheForTests() {
|
|
103
|
+
authCache = undefined;
|
|
104
|
+
}
|
|
74
105
|
export async function serveMcp(baseCtx) {
|
|
75
106
|
// Fire-and-forget staleness warning: a stale MCP server silently exposes fewer
|
|
76
107
|
// tools, and stderr is the one channel a stdio MCP server can safely log to.
|
|
77
108
|
void warnIfStale(cliVersion(), (msg) => console.error(msg));
|
|
78
109
|
const server = new Server({ name: "beryl", version: cliVersion() }, {
|
|
79
110
|
capabilities: { tools: {} },
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
111
|
+
// The running version is stated up front because this server is long-lived and never
|
|
112
|
+
// hot-reloads: a session can sit on a days-old build while `@latest` has moved, and
|
|
113
|
+
// "that tool doesn't exist for me" is indistinguishable from a bug without it. Saying
|
|
114
|
+
// it here means the model knows without spending a `version` tool call.
|
|
115
|
+
instructions: `Beryl CLI v${cliVersion()} (call the \`version\` tool for the API URL, Node ` +
|
|
116
|
+
"version, and whether this build is behind npm's latest). " +
|
|
117
|
+
"Beryl authors, runs, and heals end-to-end tests for any web app: tests are JSON " +
|
|
118
|
+
"action plans replayed in real cloud browsers, signing in as a durable test " +
|
|
119
|
+
"account whose mail arrives at the project's own mailbox — so signup/OTP/" +
|
|
120
|
+
"magic-link flows are self-contained, with no human login needed. Before " +
|
|
83
121
|
"authoring your first test plan, call the `guide` tool — it returns the full " +
|
|
84
122
|
"authoring guide (plan shape, outcome assertions, email/OTP wiring, run-fix loop).",
|
|
85
123
|
});
|
|
@@ -105,9 +143,10 @@ export async function serveMcp(baseCtx) {
|
|
|
105
143
|
if (lines.length > 400)
|
|
106
144
|
lines.splice(0, lines.length - 400);
|
|
107
145
|
};
|
|
146
|
+
const auth = currentAuth(baseCtx);
|
|
108
147
|
const ctx = createContext({
|
|
109
|
-
client:
|
|
110
|
-
config:
|
|
148
|
+
client: auth.client,
|
|
149
|
+
config: auth.config,
|
|
111
150
|
json: true,
|
|
112
151
|
interactive: false,
|
|
113
152
|
mcp: true,
|