diff --git a/src/lib/telemetry.ts b/src/lib/telemetry.ts index a2e5c472..e657bef7 100644 --- a/src/lib/telemetry.ts +++ b/src/lib/telemetry.ts @@ -87,6 +87,11 @@ export function trackCommand( try { const interactive = isInteractive() && !opts.json; + + if (!interactive && process.env.RESEND_TELEMETRY_OPT_IN !== '1') { + return; + } + showFirstRunNotice(interactive); const distinctId = getOrCreateAnonymousId(); diff --git a/tests/lib/telemetry.test.ts b/tests/lib/telemetry.test.ts index cf0f8d35..19ecf68e 100644 --- a/tests/lib/telemetry.test.ts +++ b/tests/lib/telemetry.test.ts @@ -107,6 +107,7 @@ describe('trackCommand', () => { process.env.XDG_CONFIG_HOME = testConfigDir; delete process.env.DO_NOT_TRACK; delete process.env.RESEND_TELEMETRY_DISABLED; + process.env.RESEND_TELEMETRY_OPT_IN = '1'; unrefMock = vi.fn(); const cp = await import('node:child_process'); @@ -268,6 +269,70 @@ describe('trackCommand', () => { expect(spawnMock).not.toHaveBeenCalled(); }); + it('skips telemetry in non-interactive mode without opt-in', () => { + Object.defineProperty(process.stdin, 'isTTY', { + value: undefined, + writable: true, + }); + Object.defineProperty(process.stdout, 'isTTY', { + value: undefined, + writable: true, + }); + delete process.env.RESEND_TELEMETRY_OPT_IN; + + trackCommand('emails send', {}); + expect(spawnMock).not.toHaveBeenCalled(); + }); + + it('skips telemetry in CI without opt-in', () => { + Object.defineProperty(process.stdin, 'isTTY', { + value: true, + writable: true, + }); + Object.defineProperty(process.stdout, 'isTTY', { + value: true, + writable: true, + }); + process.env.CI = 'true'; + delete process.env.RESEND_TELEMETRY_OPT_IN; + + trackCommand('emails send', {}); + expect(spawnMock).not.toHaveBeenCalled(); + }); + + it('sends telemetry in non-interactive mode with RESEND_TELEMETRY_OPT_IN=1', () => { + Object.defineProperty(process.stdin, 'isTTY', { + value: undefined, + writable: true, + }); + Object.defineProperty(process.stdout, 'isTTY', { + value: undefined, + writable: true, + }); + process.env.RESEND_TELEMETRY_OPT_IN = '1'; + + trackCommand('emails send', {}); + expect(spawnMock).toHaveBeenCalledOnce(); + }); + + it('sends telemetry in interactive mode without opt-in', () => { + Object.defineProperty(process.stdin, 'isTTY', { + value: true, + writable: true, + }); + Object.defineProperty(process.stdout, 'isTTY', { + value: true, + writable: true, + }); + delete process.env.CI; + delete process.env.GITHUB_ACTIONS; + delete process.env.TERM; + delete process.env.RESEND_TELEMETRY_OPT_IN; + + trackCommand('emails send', {}); + expect(spawnMock).toHaveBeenCalledOnce(); + }); + it('handles spawn failures gracefully', () => { spawnMock.mockImplementation(() => { throw new Error('spawn error');