Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
dd8fc2c
chore: remove LogSnag
WcaleNieWolny Aug 18, 2026
9afcfb5
refactor: remove LogSnag integration
WcaleNieWolny Aug 18, 2026
9b336c9
Merge remote-tracking branch 'origin/main' into wolny/remove-logsnag
WcaleNieWolny Aug 18, 2026
0524ce7
refactor(cli): remove legacy tracking names
WcaleNieWolny Aug 18, 2026
258eb83
Merge remote-tracking branch 'origin/main' into wolny/remove-logsnag
WcaleNieWolny Aug 18, 2026
b4ce491
chore: merge main into remove-logsnag
WcaleNieWolny Aug 18, 2026
f316a55
fix: address tracking review feedback
WcaleNieWolny Aug 18, 2026
58ac295
fix: preserve tracking compatibility metadata
WcaleNieWolny Aug 18, 2026
8f73065
Merge remote-tracking branch 'origin/main' into wolny/remove-logsnag
WcaleNieWolny Aug 18, 2026
d4de4d0
chore: remove generated graph churn
WcaleNieWolny Aug 18, 2026
5454b71
fix: preserve tracking rollout compatibility
WcaleNieWolny Aug 18, 2026
cd07a75
Merge remote-tracking branch 'origin/main' into wolny/remove-logsnag
WcaleNieWolny Aug 18, 2026
845b4ca
Merge remote-tracking branch 'origin/main' into wolny/remove-logsnag
WcaleNieWolny Aug 18, 2026
38516b2
refactor(tracking): remove legacy presentation fields
WcaleNieWolny Aug 18, 2026
efc14e8
fix(cli): preserve console event delivery
WcaleNieWolny Aug 18, 2026
c8d3ef4
Merge remote-tracking branch 'origin/main' into wolny/remove-logsnag
WcaleNieWolny Aug 18, 2026
9fdc3e8
Merge remote-tracking branch 'origin/main' into wolny/remove-logsnag
WcaleNieWolny Aug 18, 2026
db2ccc5
fix(db): format legacy tracking migration
WcaleNieWolny Aug 19, 2026
eee7c58
fix(db): refresh migration timestamp
WcaleNieWolny Aug 19, 2026
d57c691
fix(db): simplify legacy marker cleanup
WcaleNieWolny Aug 19, 2026
d02c113
chore: merge main into remove-logsnag
WcaleNieWolny Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions cli/src/ai/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ export async function trackAiAnalysisChoice(input: TrackAiAnalysisChoiceInput):
await sendEvent(input.apikey, {
event: 'CLI AI Build Analysis Choice',
channel: 'build-lifecycle',
icon: '🤖',
notify: false,
org_id: input.orgId,
tracking_version: 2,
tags: {
Expand Down Expand Up @@ -92,8 +90,6 @@ export async function trackAiAnalysisResult(input: TrackAiAnalysisResultInput):
await sendEvent(input.apikey, {
event: 'CLI AI Build Analysis Result',
channel: 'build-lifecycle',
icon: '🤖',
notify: false,
org_id: input.orgId,
tracking_version: 2,
tags,
Expand Down
10 changes: 1 addition & 9 deletions cli/src/analytics/track.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ export function resolveTrackingContext(apikey: string, signal?: AbortSignal): Pr
export interface TrackEventInput {
channel: string
event: string
icon?: string
/** Org id for actor-scoped attribution. Omitted => resolved best-effort. */
orgId?: string
/** App id (also lets the backend verify org ownership). */
Expand Down Expand Up @@ -122,8 +121,6 @@ export function trackEvent(input: TrackEventInput): Promise<void> {
await sendEvent(apikey, {
channel: input.channel,
event: input.event,
icon: input.icon ?? '📊',
notify: false,
tracking_version: 2,
...(orgId ? { org_id: orgId } : {}),
tags,
Expand Down Expand Up @@ -191,7 +188,6 @@ function emitCommandInvoked(commandPath: string, ctx: CommandContext, apikey?: s
apikey,
channel: CLI_USAGE_CHANNEL,
event: 'CLI Command Invoked',
icon: '⚡',
tags: {
command_path: commandPath,
flags: ctx.flags.join(','),
Expand Down Expand Up @@ -226,7 +222,6 @@ export function trackCommandSucceeded(commandPath: string): void {
void trackEvent({
channel: CLI_USAGE_CHANNEL,
event: 'CLI Command Succeeded',
icon: '✅',
tags: {
command_path: commandPath,
...(commandStartedAt ? { duration_ms: Date.now() - commandStartedAt } : {}),
Expand All @@ -238,7 +233,6 @@ export function trackCommandFailed(commandPath: string, opts: { errorCategory: s
void trackEvent({
channel: CLI_USAGE_CHANNEL,
event: 'CLI Command Failed',
icon: '❌',
tags: {
command_path: commandPath,
error_category: opts.errorCategory,
Expand Down Expand Up @@ -275,7 +269,6 @@ export function withMcpToolTracking<H extends AnyAsyncFn>(toolName: string, hand
void trackEvent({
channel: MCP_CHANNEL,
event: 'MCP Tool Invoked',
icon: '🤖',
tags: {
tool_name: toolName,
success,
Expand All @@ -291,7 +284,6 @@ export function trackMcpServerStarted(hasApikey: boolean): void {
void trackEvent({
channel: MCP_CHANNEL,
event: 'MCP Server Started',
icon: '🤖',
tags: {
has_apikey: hasApikey,
mcp_sdk_version: pack.version,
Expand Down Expand Up @@ -337,7 +329,7 @@ function recordSupabaseCall(info: SupabaseCallInfo): void {
}
// Use the key from the Supabase request itself so events fire even when the
// key came from --apikey (not env / a saved file); trackEvent still falls back.
void trackEvent({ apikey: info.apikey, channel: CLI_PERF_CHANNEL, event: 'Supabase Call', icon: '⏱️', tags })
void trackEvent({ apikey: info.apikey, channel: CLI_PERF_CHANNEL, event: 'Supabase Call', tags })
}

setSupabaseCallRecorder(recordSupabaseCall)
Expand Down
1 change: 0 additions & 1 deletion cli/src/app/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ export async function addAppInternal(
org_id: organizationUid,
tracking_version: 2,
tags: { 'app-id': appId, 'source': appCreateSource },
notify: false,
notifyConsole: true,
}).catch(() => {})

Expand Down
20 changes: 9 additions & 11 deletions cli/src/app/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,23 +69,21 @@ function describeFetchFailure(error: unknown, endpoint: string) {
}


export async function markSnag(channel: string, orgId: string, apikey: string, event: string, appId?: string, icon = '✅', tags?: Record<string, string | number | boolean>) {
export async function sendCliEvent(channel: string, orgId: string, apikey: string, event: string, appId?: string, tags?: Record<string, string | number | boolean>) {
await sendEvent(apikey, {
channel,
event,
icon,
org_id: orgId,
tracking_version: 2,
...((appId || tags) ? { tags: { ...(appId ? { 'app-id': appId } : {}), ...tags } } : {}),
notify: false,
})
}

export async function cancelCommand(channel: string, command: boolean | symbol, orgId: string, apikey: string) {
if (!isCancel(command))
return

await markSnag(channel, orgId, apikey, 'canceled', undefined, '🤷')
await sendCliEvent(channel, orgId, apikey, 'canceled')
log.warn('Command cancelled')
throw new CliUserError('Command cancelled')
}
Expand Down Expand Up @@ -145,13 +143,13 @@ export async function getStats(apikey: string, query: QueryStats, after: string
}

type Level = 'info' | 'warn' | 'error'
interface LogSpec { summary: (ctx: { data: LogData, baseAppUrl: string, baseUrl: string }) => string, level: Level, snag?: string, stop?: boolean }
interface LogSpec { summary: (ctx: { data: LogData, baseAppUrl: string, baseUrl: string }) => string, level: Level, trackingEvent?: string, stop?: boolean }

function summarizeAction(data: LogData): LogSpec | null {
const map: Record<string, LogSpec> = {
get: { summary: () => 'Update request by device. Waiting for download…', level: 'info', snag: 'done' },
get: { summary: () => 'Update request by device. Waiting for download…', level: 'info', trackingEvent: 'done' },
delete: { summary: () => 'Bundle deleted on device', level: 'info' },
set: { summary: () => 'Bundle set on device ❤️', level: 'info', snag: 'set', stop: true },
set: { summary: () => 'Bundle set on device ❤️', level: 'info', trackingEvent: 'set', stop: true },
NoChannelOrOverride: { summary: () => 'No default channel/override; create it in channel settings', level: 'error' },
needPlanUpgrade: { summary: ({ baseUrl }) => `Out of quota. Upgrade plan: ${baseUrl}/settings/organization/plans`, level: 'error' },
missingBundle: { summary: () => 'Requested bundle not found on server', level: 'error' },
Expand Down Expand Up @@ -191,7 +189,7 @@ function summarizeAction(data: LogData): LogSpec | null {
if (data.action.startsWith('download_')) {
const part = data.action.split('_')[1]
if (part === 'complete')
return { summary: () => 'Download complete; relaunch app to apply', level: 'info', snag: 'downloaded' }
return { summary: () => 'Download complete; relaunch app to apply', level: 'info', trackingEvent: 'downloaded' }
if (part === 'fail')
return { summary: () => 'Download failed on device', level: 'error' }
return { summary: () => `Downloading ${part}%`, level: 'info' }
Expand All @@ -203,8 +201,8 @@ async function toTableRow(data: LogData, channel: string, orgId: string, apikey:
const spec = summarizeAction(data)
if (!spec)
return {}
if (spec.snag)
await markSnag(channel, orgId, apikey, spec.snag)
if (spec.trackingEvent)
await sendCliEvent(channel, orgId, apikey, spec.trackingEvent)
const time = formatTimeOnly(data.created_at)
const key = data.action
const versionId = data.version_id ? `(version #${data.version_id})` : ''
Expand Down Expand Up @@ -263,7 +261,7 @@ function listenForWaitLogContinue(signal: AbortSignal): Promise<void> {
export async function waitLog(channel: string, apikey: string, appId: string, orgId: string, options: WaitLogOptions = {}): Promise<{ skipped: boolean }> {
const config = await getLocalConfig()
const baseAppUrl = `${config.hostWeb}/app/${appId}`
await markSnag(channel, orgId, apikey, 'Use waitlog', appId)
await sendCliEvent(channel, orgId, apikey, 'Use waitlog', appId)
const query = buildWaitLogQuery(appId, options.deviceId, options.now, options.lookbackMs)
let after: string | null = null
// Track displayed log items to avoid duplicates across rounds
Expand Down
2 changes: 0 additions & 2 deletions cli/src/app/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,9 @@ export async function deleteAppInternal(
await sendEvent(options.apikey, {
channel: 'app',
event: 'App Deleted',
icon: '🗑️',
org_id: orgId,
tracking_version: 2,
tags: { 'app-id': appId },
notify: false,
}).catch(() => {})
}

Expand Down
1 change: 0 additions & 1 deletion cli/src/app/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ export async function getInfoInternal(options: DoctorInfoOptions, silent = false
void trackEvent({
channel: 'cli-usage',
event: 'Doctor Ran',
icon: '👨‍⚕️',
tags: computeDoctorAnalyticsTags(installedDependencies, latestDependencies),
})

Expand Down
2 changes: 1 addition & 1 deletion cli/src/app/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export async function listAppInternal(options: OptionsBase, silent = false) {
supaAnon: options.supaAnon,
})

void trackEvent({ channel: 'app', event: 'Apps Listed', icon: '📋', tags: { app_count: allApps.length } })
void trackEvent({ channel: 'app', event: 'Apps Listed', tags: { app_count: allApps.length } })

if (!allApps.length) {
if (!silent)
Expand Down
1 change: 0 additions & 1 deletion cli/src/app/set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,6 @@ export async function setAppInternal(appId: string, options: Options, silent = f
org_id: organizationUid,
tracking_version: 2,
tags: { 'app-id': appId },
notify: false,
notifyConsole: true,
}).catch(() => {})

Expand Down
2 changes: 0 additions & 2 deletions cli/src/auth/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,7 @@ export async function validateAndSaveKey(apikey: string, options: SaveKeyOptions
await sendEvent(apikey, {
channel: 'user-login',
event: 'User CLI login',
icon: '✅',
tracking_version: 2,
notify: false,
}).catch(() => {})

return { userId }
Expand Down
10 changes: 4 additions & 6 deletions cli/src/build/credentials-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -521,15 +521,13 @@ export async function saveCredentialsCommand(options: SaveCredentialsOptions): P
await sendEvent(apikey, {
channel: 'credentials',
event: 'Credentials saved',
icon: '🔐',
org_id: orgId,
tracking_version: 2,
tags: {
'app-id': appId,
'platform': platform,
'storage': options.local ? 'local' : 'global',
},
notify: false,
}).catch()
}
}
Expand Down Expand Up @@ -646,7 +644,7 @@ export async function listCredentialsCommand(options?: { appId?: string, local?:
log.info('\n🔒 These credentials are stored locally on your machine only.')
log.info(' When building, they are sent to Capgo but NEVER stored there.\n')

void trackEvent({ channel: 'credentials', event: 'Credentials Listed', icon: '📋', tags: { credentials_count: appsToShow.length } })
void trackEvent({ channel: 'credentials', event: 'Credentials Listed', tags: { credentials_count: appsToShow.length } })
}
catch (error) {
log.error(`Failed to list credentials: ${error instanceof Error ? error.message : String(error)}`)
Expand Down Expand Up @@ -700,7 +698,7 @@ export async function clearCredentialsCommand(options: { appId?: string, platfor

log.info(` Location: ${credentialsPath}\n`)

void trackEvent({ channel: 'credentials', event: 'Credentials Cleared', icon: '🧹', tags: {} })
void trackEvent({ channel: 'credentials', event: 'Credentials Cleared', tags: {} })
}
catch (error) {
log.error(`Failed to clear credentials: ${error instanceof Error ? error.message : String(error)}`)
Expand Down Expand Up @@ -955,7 +953,7 @@ export async function updateCredentialsCommand(options: SaveCredentialsOptions):
log.success(`\n✅ ${platform.toUpperCase()} credentials updated for ${appId}!`)
log.info(` Location: ${credentialsPath}\n`)

void trackEvent({ channel: 'credentials', event: 'Credentials Updated', icon: '✏️', tags: {} })
void trackEvent({ channel: 'credentials', event: 'Credentials Updated', tags: {} })
}
catch (error) {
log.error(`Failed to update credentials: ${error instanceof Error ? error.message : String(error)}`)
Expand Down Expand Up @@ -1097,7 +1095,7 @@ export async function migrateCredentialsCommand(options: { appId?: string, platf

log.info('')

void trackEvent({ channel: 'credentials', event: 'Credentials Migrated', icon: '🔀', tags: {} })
void trackEvent({ channel: 'credentials', event: 'Credentials Migrated', tags: {} })
}
catch (error) {
log.error(`Failed to migrate credentials: ${error instanceof Error ? error.message : String(error)}`)
Expand Down
2 changes: 1 addition & 1 deletion cli/src/build/credentials-manage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ export async function manageCredentialsCommand(options: ManageCredentialsOptions

if (!handedOffToOnboarding) {
pOutro('Done.')
void trackEvent({ channel: 'credentials', event: 'Credentials Managed', icon: '🗂️', tags: {} })
void trackEvent({ channel: 'credentials', event: 'Credentials Managed', tags: {} })
}
}
catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion cli/src/build/last-output-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export async function lastOutputCommand(options: LastOutputOptions): Promise<voi
exit(1)
}

void trackEvent({ channel: 'cli-usage', event: 'Build Last Output Viewed', icon: '📄', tags: {} })
void trackEvent({ channel: 'cli-usage', event: 'Build Last Output Viewed', tags: {} })

if (options.qr) {
stdout.write(`${record.qrCodeAscii ?? ''}\n`)
Expand Down
2 changes: 1 addition & 1 deletion cli/src/build/needed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ export async function checkBuildNeeded(

stdout.write(`${output}\n`)
process.exitCode = getBuildNeededExitCode(result.required)
void trackEvent({ channel: 'cli-usage', event: 'Build Needed Checked', icon: '🧭', tags: { build_needed: result.required } })
void trackEvent({ channel: 'cli-usage', event: 'Build Needed Checked', tags: { build_needed: result.required } })
}
catch (error) {
log.error(`Error checking build requirement ${formatError(error)}`)
Expand Down
2 changes: 0 additions & 2 deletions cli/src/build/onboarding/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,8 @@ async function trackBuildOnboardingWorkflowEventAsync(options: TrackBuildOnboard
await sendEvent(apikey, {
channel: 'native-builder',
event: WORKFLOW_EVENT_NAMES[options.event],
icon: '🧭',
org_id: orgId,
tracking_version: 2,
tags,
notify: false,
})
}
4 changes: 2 additions & 2 deletions cli/src/build/onboarding/asc-key/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ export async function createAppleKeyCommand(options: CreateAppleKeyOptions = {})
log.error('This guided flow needs the macOS helper app and only runs on macOS.')
log.info('On other platforms, create the key manually at https://appstoreconnect.apple.com/access/integrations/api '
+ 'then save it with `npx @capgo/cli build credentials save --platform ios --apple-key <AuthKey.p8> --apple-key-id <id> --apple-issuer-id <id>`.')
void trackEvent({ channel: ASC_KEY_CHANNEL, event: 'ASC Key: Unsupported Platform', icon: '🔑', apikey: options.apikey, tags: { os_platform: platform } })
void trackEvent({ channel: ASC_KEY_CHANNEL, event: 'ASC Key: Unsupported Platform', apikey: options.apikey, tags: { os_platform: platform } })
await flushAnalytics()
exit(1)
}

if (!resolveHelperBinary()) {
log.error('Could not find the App Store Connect key helper binary.')
log.info('Update @capgo/cli (and reinstall its optional dependencies) so the signed helper is installed, then try again.')
void trackEvent({ channel: ASC_KEY_CHANNEL, event: 'ASC Key: Helper Missing', icon: '🔑', apikey: options.apikey })
void trackEvent({ channel: ASC_KEY_CHANNEL, event: 'ASC Key: Helper Missing', apikey: options.apikey })
await flushAnalytics()
exit(1)
}
Expand Down
1 change: 0 additions & 1 deletion cli/src/build/onboarding/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,6 @@ export async function onboardingBuilderCommand(options: OnboardingBuilderOptions
void trackEvent({
channel: ASC_KEY_CHANNEL,
event: 'ASC Key: Helper Untrusted',
icon: '🔑',
apikey: options.apikey,
tags: { reason: guidedProbe.reason },
})
Expand Down
6 changes: 0 additions & 6 deletions cli/src/build/onboarding/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ export async function trackBuilderOnboardingStep(input: TrackBuilderOnboardingSt
await sendEvent(input.apikey, {
event: 'Builder Onboarding Step',
channel: 'builder-onboarding',
icon: '🧭',
notify: false,
org_id: input.orgId,
tracking_version: 2,
tags,
Expand Down Expand Up @@ -109,8 +107,6 @@ export async function trackBuilderOnboardingAction(input: TrackBuilderOnboarding
await sendEvent(input.apikey, {
event: 'Builder Onboarding Action',
channel: 'builder-onboarding',
icon: '🧭',
notify: false,
org_id: input.orgId,
tracking_version: 2,
tags,
Expand Down Expand Up @@ -173,8 +169,6 @@ export async function trackBuilderOnboardingCancelled(input: TrackBuilderOnboard
await sendEvent(input.apikey, {
event: 'Builder Onboarding Quit',
channel: 'builder-onboarding',
icon: '🚪',
notify: false,
org_id: input.orgId,
tracking_version: 2,
tags,
Expand Down
Loading
Loading