feat: telemetry opt-out command + first-run notice#169
Conversation
Greptile SummaryThis PR adds first-class, reversible telemetry opt-out to the WorkOS CLI. It introduces a
Confidence Score: 5/5Safe to merge — all changed paths are additive, non-breaking, and backed by comprehensive unit and integration tests. The preferences store, notice gating, box renderer, and telemetry resolver are all correctly implemented with no observable defects. The most subtle invariants — render-then-persist ordering in the notice, truthy-empty-object sentinel after clearPreferences, and the tri-state env resolver — are each verified by dedicated tests. The shift from an import-time constant to a call-time isTelemetryEnabled() is backward-compatible and correctly prewarmed before any events fire. No files require special attention. Important Files Changed
|
Add `workos telemetry opt-out / opt-in / status` backed by a plain ~/.workos/preferences.json store. The opt-out flag folds into the single Analytics.isEnabled() gate, suppressing all telemetry events when set. WORKOS_TELEMETRY now resolves at call time as a tri-state override: only 'true'/'false' override the saved preference (in both directions); any other value falls through to the preference. Replaces the import-time WORKOS_TELEMETRY_ENABLED const with isTelemetryEnabled(). - New preferences store (sync accessor + async prewarm, never-throws read path, write path surfaces failures so opt-out/opt-in report non-persist) - telemetry command registered in bin.ts + help-json registry; added to SKIP_TELEMETRY_COMMANDS so managing the preference emits no telemetry - Prewarm loadPreferences() before analytics.initForNonInstaller() - Unit specs (store + handlers) and integration coverage proving opt-out emits zero events and env precedence holds in both directions
Add a one-time, stderr-only notice that tells users the WorkOS CLI collects anonymous usage telemetry and how to opt out. Shown at most once ever, only in interactive human mode, and never on the machine-readable path. - preferences.ts: add isNoticeShown()/markNoticeShown() (persists telemetry.noticeShownAt, read-modify-write preserves optedOut) - telemetry-notice.ts: maybeShowTelemetryNotice() with per-session guard, json-mode/opted-out/already-shown gates, never-throws; marks shown ONLY after actually rendering so non-human first runs never consume the one-time display - bin.ts: wire into middleware, skipping the telemetry command and the empty/root command (bare --help/--version) - unit specs for every gate and the mark-only-on-display rule
renderStderrBox drew a single-line border sized to the message's visible length, so any message wider than the terminal soft-wrapped and broke the border. The first-run telemetry notice (~90 chars) hit this on normal and narrow terminals. Make the box width-aware: an ANSI-aware word wrap (wrapAnsiAware) tokenizes into atomic self-closed color spans plus plain words and packs them by visible width, so color never bleeds onto the border. A backward-compatible single-line fast path keeps wide-terminal output byte-for-byte identical; only overflow triggers the multi-line box, snugged to the longest wrapped line. Fixes all three renderStderrBox callers (telemetry notice, unclaimed-env warning/provision). Adds box.spec.ts covering wrapping, atomic color spans, visible-width measurement, and border alignment at narrow widths.
…on reset debug state now reports telemetry enabled/optedOut/source/noticeShown and the preferences file path, so users and support can see the effective state and where it lives. debug reset clears ~/.workos/preferences.json alongside config (non-secret local CLI state rides with the config target), returning telemetry to its fresh-install state. Adds clearPreferences() to the preferences store.
- telemetry-notice: render the opt-out command via formatWorkOSCommand so npx users see the working invocation (e.g. 'npx workos@latest telemetry opt-out') instead of a hardcoded 'workos ...'; add a regression test. - telemetry-notice: set the per-session guard and persist 'shown' only AFTER a successful render, so a render failure lets a later command retry instead of silently suppressing the notice for the rest of the session. - preferences: getTelemetrySource now returns 'default' (not 'preference') when optedOut is explicitly false, matching isTelemetryEnabled()'s precedence — an opted-in state is indistinguishable from a fresh install. - preferences: drop the unused 'mkdir' import. - box: document the single-level-SGR assumption and the long atomic-token overflow caveat (e.g. the npx command form on very narrow terminals).
|
Addressed review feedback in f969135:
|
f969135 to
9cf0ce2
Compare
Summary
Gives users a first-class, reversible way to opt out of CLI telemetry (collection stays on by default) plus a one-time notice that it's happening. Closes the trust/norms gap left by the always-on telemetry added in #122 — until now the only control was the undocumented
WORKOS_TELEMETRY=falseenv var.What's included
workos telemetry opt-out/opt-in/status— reversible commands.statusreports the effective state and its source (env var / saved preference / default), with human and JSON output.~/.workos/preferences.json(mirrors the non-secretdevice-idfile pattern — no keyring, no insecure-fallback warning). Folded into the singleAnalytics.isEnabled()gate so opt-out suppresses all event types (session.start,command,crash).WORKOS_TELEMETRYoverrides the saved preference in both directions for that invocation. Replaced the import-timeWORKOS_TELEMETRY_ENABLEDconst (which conflated "unset" with "true") with a call-time tri-stateisTelemetryEnabled()resolver.--json/non-TTY/CI and when already opted out), backed by a persistednoticeShownAttimestamp. Marked shown only when actually displayed, so a human eventually sees it even if early runs are scripted.telemetryis skip-listed so managing your preference never itself emits telemetry; registered in thehelp-jsoncommand tree.debugintegration —debug statesurfaces telemetry enabled/optedOut/source/noticeShown + the preferences path;debug resetclears preferences alongside config.renderStderrBoxwas single-line-only and broke its border when a message exceeded the terminal width. Now wraps (ANSI-aware), fixing the notice on narrow terminals and benefiting the unclaimed-env warning/provision boxes too.Testing
pnpm test— 2028 tests pass (new specs for the preferences store, thetelemetrycommand, the first-run notice gates, the box wrapping, anddebugintegration).pnpm typecheckandpnpm buildpass; changed files passoxfmt --check.Manual
Notes
scripts/test-local-telemetry.sh(contains a hardcoded local-dev API key) andscripts/datadog-cli-dashboard.json. Flag if either should be tracked (the script needs the key removed first).🤖 Generated with Claude Code