UNPKG

@mastra/core

Version:
1 lines 15.2 kB
{"version":3,"sources":["../../src/telemetry/posthog.ts","../../src/telemetry/usage-telemetry.ts"],"names":["createHash","os","PostHog","path","readFileSync","mkdirSync","writeFileSync"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createHash } from 'node:crypto';\nimport os from 'node:os';\nimport { PostHog } from 'posthog-node';\n\nconst POSTHOG_API_KEY = 'phc_SBLpZVAB6jmHOct9CABq3PF0Yn5FU3G2FgT4xUr2XrT';\nconst POSTHOG_HOST = 'https://us.posthog.com';\nconst TRUTHY_DISABLED_VALUES = new Set(['1', 'true', 'yes']);\n\nlet client: PostHog | null = null;\n\nexport type EEEventName = 'ee_license_check' | 'ee_feature_used';\n\nexport type TelemetryEventName = EEEventName | 'mastra_model_token_usage';\n\nexport function isEETelemetryEnabled(): boolean {\n const value = process.env['MASTRA_TELEMETRY_DISABLED'];\n if (!value) {\n return true;\n }\n return !TRUTHY_DISABLED_VALUES.has(value.trim().toLowerCase());\n}\n\nexport function hashTelemetryValue(value: string): string {\n return createHash('sha256').update(value).digest('hex');\n}\n\nfunction getHashedHostname(): string {\n return hashTelemetryValue(os.hostname() || 'unknown-host').slice(0, 16);\n}\n\nexport function getEETelemetryFallbackDistinctId(): string {\n return `mastra-${getHashedHostname()}`;\n}\n\nfunction getClient(): PostHog | null {\n if (!isEETelemetryEnabled()) {\n return null;\n }\n\n if (!client) {\n client = new PostHog(POSTHOG_API_KEY, {\n host: POSTHOG_HOST,\n flushAt: 1,\n flushInterval: 0,\n disableGeoip: false,\n });\n }\n\n return client;\n}\n\nfunction getSystemProperties(): Record<string, unknown> {\n return {\n os: process.platform,\n os_version: os.release(),\n node_version: process.version,\n platform: process.arch,\n machine_id: getHashedHostname(),\n mastra_version: process.env['npm_package_version'] || 'unknown',\n };\n}\n\nexport function captureTelemetryEvent(\n event: TelemetryEventName,\n distinctId: string | undefined,\n properties?: Record<string, unknown>,\n): void {\n try {\n const posthog = getClient();\n if (!posthog) {\n return;\n }\n\n posthog.capture({\n distinctId: distinctId || getEETelemetryFallbackDistinctId(),\n event,\n properties: {\n ...getSystemProperties(),\n ...properties,\n },\n });\n } catch {\n // Telemetry must never affect auth or EE feature behavior.\n }\n}\n\nexport function captureEEEvent(\n event: EEEventName,\n distinctId: string | undefined,\n properties?: Record<string, unknown>,\n): void {\n captureTelemetryEvent(event, distinctId, properties);\n}\n\nexport function resetEETelemetryForTests(): void {\n client = null;\n}\n","import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { Mastra } from '../mastra';\nimport type { GetMetricBreakdownResponse, ObservabilityStorage } from '../storage/domains';\nimport { captureTelemetryEvent, hashTelemetryValue, isEETelemetryEnabled } from './posthog';\n\nconst INPUT_TOKENS_METRIC = 'mastra_model_total_input_tokens';\nconst OUTPUT_TOKENS_METRIC = 'mastra_model_total_output_tokens';\nconst BREAKDOWN_LIMIT = 200;\n\nexport const USAGE_TELEMETRY_EVENT = 'mastra_model_token_usage';\n\nexport interface SyncUsageTelemetryOptions {\n /** Override the cursor file location (used by tests). */\n cursorPath?: string;\n /** Override the current time (used by tests). */\n now?: Date;\n}\n\ninterface UsageTelemetryCursors {\n projects: Record<string, string>;\n}\n\ninterface UsageRow {\n provider: string | null;\n model: string | null;\n inputTokens: number;\n outputTokens: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n}\n\nfunction getDefaultCursorPath(): string {\n return path.join(os.homedir(), '.mastra', 'usage-telemetry.json');\n}\n\nfunction readCursors(cursorPath: string): UsageTelemetryCursors {\n try {\n const parsed = JSON.parse(readFileSync(cursorPath, 'utf-8')) as Partial<UsageTelemetryCursors> | null;\n if (parsed && typeof parsed === 'object' && parsed.projects && typeof parsed.projects === 'object') {\n return { projects: parsed.projects };\n }\n } catch {\n // Missing or corrupt cursor file - treat as first sync.\n }\n return { projects: {} };\n}\n\nfunction readCursor(cursorPath: string, projectId: string): Date | undefined {\n const value = readCursors(cursorPath).projects[projectId];\n if (!value) {\n return undefined;\n }\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? undefined : date;\n}\n\nfunction writeCursor(cursorPath: string, projectId: string, syncedAt: Date): void {\n const cursors = readCursors(cursorPath);\n cursors.projects[projectId] = syncedAt.toISOString();\n mkdirSync(path.dirname(cursorPath), { recursive: true });\n writeFileSync(cursorPath, JSON.stringify(cursors));\n}\n\nfunction applyBreakdown(\n rows: Map<string, UsageRow>,\n response: GetMetricBreakdownResponse,\n field: keyof Pick<UsageRow, 'inputTokens' | 'outputTokens' | 'totalInputTokens' | 'totalOutputTokens'>,\n): void {\n for (const group of response.groups) {\n const provider = group.dimensions.provider ?? null;\n const model = group.dimensions.model ?? null;\n const key = `${provider}\\u0000${model}`;\n let row = rows.get(key);\n if (!row) {\n row = { provider, model, inputTokens: 0, outputTokens: 0, totalInputTokens: 0, totalOutputTokens: 0 };\n rows.set(key, row);\n }\n row[field] += group.value ?? 0;\n }\n}\n\n/**\n * Sends aggregated model token usage (input/output tokens per provider+model) to\n * Mastra's anonymous telemetry when the project has observability metrics enabled.\n *\n * Sync strategy: deltas since the last successful sync, tracked per project via a\n * cursor file in `~/.mastra/usage-telemetry.json`. Each event also carries lifetime\n * totals so consumers can read either incremental or cumulative usage. Runs once at\n * server startup; respects `MASTRA_TELEMETRY_DISABLED`. Never throws.\n */\nexport async function syncUsageTelemetry(mastra: Mastra, options: SyncUsageTelemetryOptions = {}): Promise<void> {\n try {\n if (!isEETelemetryEnabled()) {\n return;\n }\n\n const observability = mastra.getStorage()?.stores?.observability as ObservabilityStorage | undefined;\n if (!observability || typeof observability.getMetricBreakdown !== 'function') {\n return;\n }\n\n const projectRoot = process.env.MASTRA_PROJECT_ROOT || process.cwd();\n const projectId = hashTelemetryValue(projectRoot).slice(0, 16);\n const cursorPath = options.cursorPath ?? getDefaultCursorPath();\n const lastSyncedAt = readCursor(cursorPath, projectId);\n const now = options.now ?? new Date();\n if (lastSyncedAt && lastSyncedAt.getTime() >= now.getTime()) {\n return;\n }\n\n const baseArgs = {\n groupBy: ['provider', 'model'],\n aggregation: 'sum' as const,\n limit: BREAKDOWN_LIMIT,\n };\n const deltaFilters = {\n timestamp: { ...(lastSyncedAt ? { start: lastSyncedAt, startExclusive: true } : {}), end: now },\n };\n const totalFilters = { timestamp: { end: now } };\n\n const [inputDelta, outputDelta, inputTotal, outputTotal] = await Promise.all([\n observability.getMetricBreakdown({ ...baseArgs, name: [INPUT_TOKENS_METRIC], filters: deltaFilters }),\n observability.getMetricBreakdown({ ...baseArgs, name: [OUTPUT_TOKENS_METRIC], filters: deltaFilters }),\n observability.getMetricBreakdown({ ...baseArgs, name: [INPUT_TOKENS_METRIC], filters: totalFilters }),\n observability.getMetricBreakdown({ ...baseArgs, name: [OUTPUT_TOKENS_METRIC], filters: totalFilters }),\n ]);\n\n const rows = new Map<string, UsageRow>();\n applyBreakdown(rows, inputDelta, 'inputTokens');\n applyBreakdown(rows, outputDelta, 'outputTokens');\n applyBreakdown(rows, inputTotal, 'totalInputTokens');\n applyBreakdown(rows, outputTotal, 'totalOutputTokens');\n\n const distinctId = process.env.MASTRA_CLI_DISTINCT_ID || undefined;\n const command = process.env.MASTRA_TELEMETRY_COMMAND || 'server';\n const nodeEnv = process.env.NODE_ENV || 'development';\n const isFirstSync = !lastSyncedAt;\n\n for (const row of rows.values()) {\n if (row.inputTokens <= 0 && row.outputTokens <= 0) {\n continue;\n }\n captureTelemetryEvent(USAGE_TELEMETRY_EVENT, distinctId, {\n provider: row.provider,\n model: row.model,\n input_tokens: row.inputTokens,\n output_tokens: row.outputTokens,\n total_input_tokens: row.totalInputTokens,\n total_output_tokens: row.totalOutputTokens,\n command,\n node_env: nodeEnv,\n project_id: projectId,\n is_first_sync: isFirstSync,\n window_start: lastSyncedAt?.toISOString() ?? null,\n window_end: now.toISOString(),\n });\n }\n\n writeCursor(cursorPath, projectId, now);\n } catch {\n // Usage telemetry must never affect server startup or runtime behavior.\n }\n}\n"]}