fix(cli): make telemetry opt-out durable (#2852)

* fix(cli): make telemetry opt-out durable

* fix(cli): make telemetry status trustworthy
This commit is contained in:
Miguel Ángel
2026-07-28 20:35:54 +02:00
committed by GitHub
parent c691869e22
commit 880021411d
13 changed files with 491 additions and 52 deletions
+48 -20
View File
@@ -1,39 +1,67 @@
import { failCommand } from "../utils/commandResult.js";
import { defineCommand } from "citty";
import type { Example } from "./_examples.js";
import { writeConfigWithResult, readConfigFresh, CONFIG_PATH } from "../telemetry/config.js";
import { effectiveTelemetryStatus, type TelemetryStatusSource } from "../telemetry/policy.js";
import { c } from "../ui/colors.js";
import { failCommand } from "../utils/commandResult.js";
import type { Example } from "./_examples.js";
export const examples: Example[] = [
["Check current telemetry status", "hyperframes telemetry status"],
["Disable telemetry", "hyperframes telemetry disable"],
["Enable telemetry", "hyperframes telemetry enable"],
];
import { readConfig, writeConfig, CONFIG_PATH } from "../telemetry/config.js";
function runEnable(): void {
const config = readConfig();
config.telemetryEnabled = true;
writeConfig(config);
console.log(`\n ${c.success("\u2713")} Telemetry ${c.success("enabled")}\n`);
function describeOverride(source: Exclude<TelemetryStatusSource, "config">): string {
switch (source) {
case "HYPERFRAMES_NO_TELEMETRY":
case "DO_NOT_TRACK":
return `${source} is set`;
case "dev_mode":
return "this is a development build";
case "telemetry_disabled_build":
return "this build has no telemetry key";
}
}
function runDisable(): void {
const config = readConfig();
config.telemetryEnabled = false;
writeConfig(config);
console.log(`\n ${c.success("\u2713")} Telemetry ${c.bold("disabled")}\n`);
function setTelemetryEnabled(enabled: boolean): void {
// Bypass the module cache so this privacy preference starts from the latest
// on-disk state rather than a snapshot held by another config consumer.
const config = readConfigFresh();
config.telemetryEnabled = enabled;
const result = writeConfigWithResult(config);
if (!result.ok) {
console.error(
`\n ${c.error("\u2717")} Could not persist telemetry preference to ${c.accent(CONFIG_PATH)}\n` +
` ${c.dim("Reason:")} ${result.error}\n`,
);
failCommand();
}
const effective = effectiveTelemetryStatus(enabled);
const preference = enabled ? c.success("enabled") : c.bold("disabled");
const noun = enabled && !effective.enabled ? "Telemetry preference" : "Telemetry";
console.log(`\n ${c.success("\u2713")} ${noun} ${preference}`);
if (effective.source !== "config") {
console.log(
` ${c.dim("Note:")} Telemetry remains disabled because ${describeOverride(effective.source)}.`,
);
}
console.log();
}
function runStatus(): void {
const config = readConfig();
const status = config.telemetryEnabled ? c.success("enabled") : c.dim("disabled");
const config = readConfigFresh();
const effective = effectiveTelemetryStatus(config.telemetryEnabled);
const status = effective.enabled ? c.success("enabled") : c.dim("disabled");
console.log();
console.log(` ${c.dim("Status:")} ${status}`);
console.log(` ${c.dim("Source:")} ${effective.source}`);
console.log(` ${c.dim("Config:")} ${c.accent(CONFIG_PATH)}`);
console.log(` ${c.dim("Commands:")} ${c.bold(String(config.commandCount))}`);
console.log(` ${c.dim("Tracked commands:")} ${c.bold(String(config.commandCount))}`);
console.log();
console.log(` ${c.dim("Disable:")} ${c.accent("hyperframes telemetry disable")}`);
console.log(` ${c.dim("Env var:")} ${c.accent("HYPERFRAMES_NO_TELEMETRY=1")}`);
console.log(
` ${c.dim("Env var:")} ${c.accent("HYPERFRAMES_NO_TELEMETRY=1")} ${c.dim("or")} ${c.accent("DO_NOT_TRACK=1")}`,
);
console.log();
}
@@ -71,16 +99,16 @@ ${c.bold("WHAT WE DON'T COLLECT:")}
${c.dim("\u2022")} IP addresses (discarded by our analytics provider)
${c.dim("\u2022")} Any personally identifiable information
${c.dim("You can also set")} ${c.accent("HYPERFRAMES_NO_TELEMETRY=1")} ${c.dim("to disable.")}
${c.dim("You can also set")} ${c.accent("HYPERFRAMES_NO_TELEMETRY=1")} ${c.dim("or")} ${c.accent("DO_NOT_TRACK=1")} ${c.dim("to disable.")}
`);
return;
}
switch (subcommand) {
case "enable":
return runEnable();
return setTelemetryEnabled(true);
case "disable":
return runDisable();
return setTelemetryEnabled(false);
case "status":
return runStatus();
default: