@beryl-so/cli 0.34.4 → 0.35.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 +3 -0
- package/dist/adapters/cli.js +8 -1
- package/dist/adapters/mcp.js +1 -8
- package/dist/commands/auth.js +3 -0
- package/dist/commands/init.js +2 -2
- package/dist/config.js +11 -0
- package/dist/telemetry.js +28 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,6 +30,9 @@ From the monorepo (development): `cd cli && npm install && npm run build && npm
|
|
|
30
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
|
+
The CLI reports which commands run (never their arguments or output) so onboarding can
|
|
34
|
+
be measured; set `BERYL_TELEMETRY=0` or `DO_NOT_TRACK=1` to send nothing.
|
|
35
|
+
|
|
33
36
|
## Authenticate
|
|
34
37
|
|
|
35
38
|
```bash
|
package/dist/adapters/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { CliError, EXIT_OK, EXIT_USAGE, UsageError } from "../errors.js";
|
|
|
4
4
|
import { ApiClient } from "../http.js";
|
|
5
5
|
import { autoFormat, bold, cyan, dim } from "../output.js";
|
|
6
6
|
import { commandGroups, findCommand, groupSummary } from "../registry/index.js";
|
|
7
|
-
import { withTool } from "../telemetry.js";
|
|
7
|
+
import { captureCliEvent, shutdownTelemetry, withTool, } from "../telemetry.js";
|
|
8
8
|
import { cliVersion } from "../version-check.js";
|
|
9
9
|
import { mcpToolFor } from "./mcp.js";
|
|
10
10
|
export const GLOBAL_FLAGS = [
|
|
@@ -324,6 +324,10 @@ export async function runCli(argv) {
|
|
|
324
324
|
config.token = parsed.token;
|
|
325
325
|
const client = new ApiClient(config.apiUrl, config.token);
|
|
326
326
|
const ctx = createContext({ client, config, json: parsed.json, mcp: false });
|
|
327
|
+
// The MCP server reports its own tool calls; every other invocation is one command.
|
|
328
|
+
if (spec.name !== "mcp") {
|
|
329
|
+
captureCliEvent("cli_command", config.apiUrl, { command: spec.name });
|
|
330
|
+
}
|
|
327
331
|
try {
|
|
328
332
|
const result = (await withTool(spec.name, () => spec.run(ctx, parsed.input))) ?? {};
|
|
329
333
|
if (parsed.json) {
|
|
@@ -345,4 +349,7 @@ export async function runCli(argv) {
|
|
|
345
349
|
}
|
|
346
350
|
throw err;
|
|
347
351
|
}
|
|
352
|
+
finally {
|
|
353
|
+
await shutdownTelemetry();
|
|
354
|
+
}
|
|
348
355
|
}
|
package/dist/adapters/mcp.js
CHANGED
|
@@ -250,14 +250,7 @@ export function identifyUser(baseCtx) {
|
|
|
250
250
|
if (me?.id) {
|
|
251
251
|
identity = {
|
|
252
252
|
distinctId: me.id,
|
|
253
|
-
|
|
254
|
-
// want our own MCP sessions visible when debugging, so dashboards exclude
|
|
255
|
-
// them by cohort instead.
|
|
256
|
-
properties: {
|
|
257
|
-
email: me.email,
|
|
258
|
-
name: me.name,
|
|
259
|
-
is_internal: Boolean(me.is_vibemonitor),
|
|
260
|
-
},
|
|
253
|
+
properties: { email: me.email, name: me.name },
|
|
261
254
|
};
|
|
262
255
|
}
|
|
263
256
|
}
|
package/dist/commands/auth.js
CHANGED
|
@@ -5,6 +5,7 @@ import { deviceLogin, SWITCH_TO_OTP } from "../device-login.js";
|
|
|
5
5
|
import { CliError, UsageError } from "../errors.js";
|
|
6
6
|
import { ApiClient } from "../http.js";
|
|
7
7
|
import { dim, green } from "../output.js";
|
|
8
|
+
import { aliasMachineToAccount, flushTelemetry } from "../telemetry.js";
|
|
8
9
|
import { arg, flagBool, flagStr } from "./util.js";
|
|
9
10
|
function gitEmail() {
|
|
10
11
|
try {
|
|
@@ -172,6 +173,8 @@ export const authCommands = [
|
|
|
172
173
|
token,
|
|
173
174
|
api_url: apiUrl === "https://api.beryl.so" ? undefined : apiUrl,
|
|
174
175
|
});
|
|
176
|
+
aliasMachineToAccount(me.id);
|
|
177
|
+
flushTelemetry();
|
|
175
178
|
const human = `${green("Logged in")} as ${me.name} <${me.email}>` +
|
|
176
179
|
`\n${dim(`Token saved to ${saved}`)}`;
|
|
177
180
|
return {
|
package/dist/commands/init.js
CHANGED
|
@@ -8,7 +8,7 @@ import { AuthError, CliError } from "../errors.js";
|
|
|
8
8
|
import { ApiClient } from "../http.js";
|
|
9
9
|
import { bold, cyan, dim, green, red, yellow } from "../output.js";
|
|
10
10
|
import { anyGap, describeGaps, installCommandsFor, installPlaywright, playwrightGaps, } from "../playwright-install.js";
|
|
11
|
-
import {
|
|
11
|
+
import { aliasMachineToAccount, captureCliEvent, flushTelemetry, shutdownTelemetry, } from "../telemetry.js";
|
|
12
12
|
import { cliVersion, warnIfStale } from "../version-check.js";
|
|
13
13
|
import { authCommands } from "./auth.js";
|
|
14
14
|
import { flagStr } from "./util.js";
|
|
@@ -227,7 +227,7 @@ export const initCommands = [
|
|
|
227
227
|
await signIn();
|
|
228
228
|
}
|
|
229
229
|
if (accountId) {
|
|
230
|
-
|
|
230
|
+
aliasMachineToAccount(accountId);
|
|
231
231
|
flushTelemetry();
|
|
232
232
|
}
|
|
233
233
|
const report = (label, fresh, where) => ctx.err(`${green("✓")} ${label} ${fresh ? "configured" : "already configured"} ${dim(where)}`);
|
package/dist/config.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import fs from "node:fs";
|
|
2
3
|
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
@@ -45,3 +46,13 @@ export function saveGlobalConfig(patch, env = process.env) {
|
|
|
45
46
|
}
|
|
46
47
|
return file;
|
|
47
48
|
}
|
|
49
|
+
// One id per install: every CLI event from this machine lands on it, and a login aliases
|
|
50
|
+
// it onto the account, so an install joins the same PostHog person as the signup.
|
|
51
|
+
export function machineId(env = process.env) {
|
|
52
|
+
const current = readJson(globalConfigPath(env)) ?? {};
|
|
53
|
+
if (current.machine_id)
|
|
54
|
+
return current.machine_id;
|
|
55
|
+
const id = randomUUID();
|
|
56
|
+
saveGlobalConfig({ machine_id: id }, env);
|
|
57
|
+
return id;
|
|
58
|
+
}
|
package/dist/telemetry.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { PostHog } from "posthog-node";
|
|
4
|
+
import { machineId } from "./config.js";
|
|
4
5
|
import { cliVersion } from "./version-check.js";
|
|
5
6
|
// The webapp already ships this public (write-only) project key in its JS bundle, and
|
|
6
7
|
// dev/prod deploys share it — environments are separated by the `environment` property
|
|
@@ -229,22 +230,38 @@ export function baseEventProperties(apiUrl) {
|
|
|
229
230
|
};
|
|
230
231
|
}
|
|
231
232
|
let posthog;
|
|
233
|
+
export function telemetryDisabled(env = process.env) {
|
|
234
|
+
return env.BERYL_TELEMETRY === "0" || env.DO_NOT_TRACK === "1";
|
|
235
|
+
}
|
|
232
236
|
export function createPostHogClient() {
|
|
233
|
-
posthog ??= new PostHog(POSTHOG_KEY, {
|
|
237
|
+
posthog ??= new PostHog(POSTHOG_KEY, {
|
|
238
|
+
host: POSTHOG_HOST,
|
|
239
|
+
disableGeoip: false,
|
|
240
|
+
disabled: telemetryDisabled(),
|
|
241
|
+
});
|
|
234
242
|
return posthog;
|
|
235
243
|
}
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
// so the install joins the same person as the signup and the later MCP work.
|
|
244
|
+
// CLI events land on the install's machine id, which a login aliases onto the account:
|
|
245
|
+
// an install made before there is an account still joins the person who later signs up.
|
|
239
246
|
export function captureCliEvent(event, apiUrl, properties = {}) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
247
|
+
try {
|
|
248
|
+
createPostHogClient().capture({
|
|
249
|
+
distinctId: machineId(),
|
|
250
|
+
event,
|
|
251
|
+
properties: { ...baseEventProperties(apiUrl), ...properties },
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// telemetry must never break a command
|
|
256
|
+
}
|
|
245
257
|
}
|
|
246
|
-
export function
|
|
247
|
-
|
|
258
|
+
export function aliasMachineToAccount(userId) {
|
|
259
|
+
try {
|
|
260
|
+
createPostHogClient().alias({ distinctId: userId, alias: machineId() });
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
// telemetry must never break a command
|
|
264
|
+
}
|
|
248
265
|
}
|
|
249
266
|
export function flushTelemetry() {
|
|
250
267
|
// Fire-and-forget: init keeps running (login, installs) while the batch goes out, and a
|
package/package.json
CHANGED