mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-07 10:06:21 +00:00
The DE parallel-router breaker's tripped state lived in the same config file as the install id, so the most common identity reset — deleting ~/.hyperframes — also re-enrolled the machine into an experimental path that had already failed on it. Mirror exactly two facts into a machine-local state file (~/.local/state/hyperframes/install-state.json) that a config wipe does not touch: - markerAt: written unconditionally on every install, so the fraction of fresh mints that find it directly measures recoverable id churn (config wiped, machine persisted) vs unrecoverable (fresh machine/container/new user). Emitted as install_predecessor_found on telemetry events; absent (not false) on configs predating the field. - deParallelRouterTrialFired: a breaker tripped by a previous install stays tripped for the new one. Config corruption takes the same mint path, so it survives that too. The file deliberately holds NO identity — no anonymousId, no counters. A wiped config still gets a fresh id unconditionally; only the safety fact about the machine survives. Sync happens inside writeConfig so no breaker write site can forget it; failures are swallowed (telemetry must never break the CLI) but leave the memo unset so a later write retries. `hyperframes telemetry` lists the state path for transparency. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
130 lines
4.7 KiB
TypeScript
130 lines
4.7 KiB
TypeScript
import { defineCommand } from "citty";
|
|
import {
|
|
writeConfigWithResult,
|
|
readConfigFresh,
|
|
CONFIG_PATH,
|
|
STATE_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"],
|
|
];
|
|
|
|
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 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 = 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)}`);
|
|
// Machine-local safety state (no identity): survives a config wipe so a
|
|
// tripped experiment circuit breaker stays tripped. Listed for transparency.
|
|
console.log(` ${c.dim("State:")} ${c.accent(STATE_PATH)}`);
|
|
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")} ${c.dim("or")} ${c.accent("DO_NOT_TRACK=1")}`,
|
|
);
|
|
console.log();
|
|
}
|
|
|
|
export default defineCommand({
|
|
meta: { name: "telemetry", description: "Manage anonymous usage telemetry" },
|
|
args: {
|
|
subcommand: {
|
|
type: "positional",
|
|
description: "Subcommand: enable, disable, status",
|
|
required: false,
|
|
},
|
|
},
|
|
async run({ args }) {
|
|
const subcommand = args.subcommand;
|
|
|
|
if (!subcommand || subcommand === "") {
|
|
console.log(`
|
|
${c.bold("hyperframes telemetry")} ${c.dim("<subcommand>")}
|
|
|
|
Manage anonymous usage data collection.
|
|
|
|
${c.bold("SUBCOMMANDS:")}
|
|
${c.accent("status")} ${c.dim("Show current telemetry status")}
|
|
${c.accent("enable")} ${c.dim("Enable anonymous telemetry")}
|
|
${c.accent("disable")} ${c.dim("Disable anonymous telemetry")}
|
|
|
|
${c.bold("WHAT WE COLLECT:")}
|
|
${c.dim("\u2022")} Command names (init, render, preview, etc.)
|
|
${c.dim("\u2022")} Render performance (duration, fps, quality)
|
|
${c.dim("\u2022")} Template choices
|
|
${c.dim("\u2022")} OS, architecture, Node.js version, CLI version
|
|
|
|
${c.bold("WHAT WE DON'T COLLECT:")}
|
|
${c.dim("\u2022")} File paths, project names, or video content
|
|
${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("or")} ${c.accent("DO_NOT_TRACK=1")} ${c.dim("to disable.")}
|
|
`);
|
|
return;
|
|
}
|
|
|
|
switch (subcommand) {
|
|
case "enable":
|
|
return setTelemetryEnabled(true);
|
|
case "disable":
|
|
return setTelemetryEnabled(false);
|
|
case "status":
|
|
return runStatus();
|
|
default:
|
|
console.error(
|
|
`${c.error("Unknown subcommand:")} ${subcommand}\n\nRun ${c.accent("hyperframes telemetry --help")} for usage.`,
|
|
);
|
|
failCommand();
|
|
}
|
|
},
|
|
});
|