From d95eea98b02d316a5b23773f289514645527cd4a Mon Sep 17 00:00:00 2001 From: Josh Carver Date: Mon, 1 Jun 2026 10:35:10 -0700 Subject: [PATCH] chore: Don't prompt user to share report if they opted out --- src/index.ts | 22 +++++++++++++++------- src/interactive/sharePrompt.test.ts | 19 +++++++++++++++++-- src/interactive/sharePrompt.ts | 23 ++++++++++++++++++++++- 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/index.ts b/src/index.ts index 2b5e762..a69058c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,7 +8,7 @@ import { createContext } from './context.ts'; import { getEnvironment, isTelemetryDisabled } from './environment.ts'; import { welcomeBannerBody, welcomeBannerTitle } from './interactive/banner.ts'; import { openReport } from './interactive/openReport.ts'; -import { runSharePrompt } from './interactive/sharePrompt.ts'; +import { runSharePrompt, shouldRequestReportShare, showLocalReportReadyNotice } from './interactive/sharePrompt.ts'; import { formatInteractiveTokenError, interactiveResolveToken } from './interactive/tokenWalkthrough.ts'; import { enforceTty } from './interactive/ttyGate.ts'; import { IoImpl } from './IoImpl.ts'; @@ -90,12 +90,20 @@ const result = await main(ctx, argv); if (result.kind === 'completed') { await openReport({ context: ctx, htmlPath: result.run.paths.html }); - await runSharePrompt({ - context: ctx, - target: result.run.target, - htmlPath: result.run.paths.html, - htmlContent: result.run.html, - }); + if (shouldRequestReportShare(ctx)) { + await runSharePrompt({ + context: ctx, + target: result.run.target, + htmlPath: result.run.paths.html, + htmlContent: result.run.html, + }); + } else { + showLocalReportReadyNotice({ + context: ctx, + target: result.run.target, + htmlPath: result.run.paths.html, + }); + } } await shutdown(); diff --git a/src/interactive/sharePrompt.test.ts b/src/interactive/sharePrompt.test.ts index c4e2e28..c62e4d8 100644 --- a/src/interactive/sharePrompt.test.ts +++ b/src/interactive/sharePrompt.test.ts @@ -1,9 +1,24 @@ import { describe, expect, test } from 'bun:test'; import { fakeContextHandle } from '../testHelpers/testFactories.ts'; -import { runSharePrompt } from './sharePrompt.ts'; +import { runSharePrompt, shouldRequestReportShare, showLocalReportReadyNotice } from './sharePrompt.ts'; import { sharePromptInputsFor } from './testFactories.ts'; -describe('runSharePrompt', () => { +describe('report sharing prompt', () => { + test('does not request report sharing when do not track disables telemetry', () => { + const handle = fakeContextHandle.build(); + const context = { ...handle.ctx, env: { ...handle.ctx.env, DO_NOT_TRACK: true }, telemetryDisabled: true }; + + showLocalReportReadyNotice({ context, target: 'acme', htmlPath: '/tmp/report.html' }); + + expect(shouldRequestReportShare(context)).toBe(false); + expect(handle.prompter.selects).toHaveLength(0); + expect(handle.prompter.texts).toHaveLength(0); + expect(handle.uploader.calls).toHaveLength(0); + expect(handle.prompter.notes[0]).toMatchObject({ + title: 'Report ready', + }); + expect(handle.prompter.notes[0]?.message).toContain('Nothing was uploaded because tracking is disabled.'); + }); test('shows the report path and a clear share question, defaulting to sharing', async () => { const handle = fakeContextHandle.build(); handle.prompter.scriptSelect('declined'); diff --git a/src/interactive/sharePrompt.ts b/src/interactive/sharePrompt.ts index d19e1cd..b42badd 100644 --- a/src/interactive/sharePrompt.ts +++ b/src/interactive/sharePrompt.ts @@ -6,10 +6,13 @@ const SUPPORT_LINE = 'Reach us at founders@contextbridge.ai — or learn more at export type ShareChoice = 'html' | 'declined'; -export interface SharePromptInputs { +export interface ReportReadyNoticeInputs { readonly context: Context; readonly target: string; readonly htmlPath: string; +} + +export interface SharePromptInputs extends ReportReadyNoticeInputs { readonly htmlContent: string; } @@ -19,6 +22,24 @@ export type ShareOutcome = | { kind: 'cancelled' } | { kind: 'upload-failed'; message: string }; +export function shouldRequestReportShare(context: Context): boolean { + return !context.telemetryDisabled; +} + +export function showLocalReportReadyNotice(inputs: ReportReadyNoticeInputs): void { + const { prompter } = inputs.context; + prompter.note( + [ + `Scanned: ${inputs.target}`, + `HTML report: ${inputs.htmlPath}`, + '', + 'Nothing was uploaded because tracking is disabled.', + ].join('\n'), + 'Report ready', + ); + prompter.outro('Done.'); +} + export async function runSharePrompt(inputs: SharePromptInputs): Promise { const { prompter, analytics, uploader } = inputs.context;