From ebe4b4795f9c5ec683ecfc9467d47e7b0279aaf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kj=C3=A6r?= Date: Fri, 24 Oct 2025 17:40:11 +0200 Subject: [PATCH 1/5] Wrap transcriptions in posthog --- packages/ai/src/openai/index.ts | 222 ++++++++++++++++++++++++++++++++ packages/ai/src/utils.ts | 3 + 2 files changed, 225 insertions(+) diff --git a/packages/ai/src/openai/index.ts b/packages/ai/src/openai/index.ts index 35425d7935..26d601db42 100644 --- a/packages/ai/src/openai/index.ts +++ b/packages/ai/src/openai/index.ts @@ -21,6 +21,8 @@ const Chat = OpenAIOrignal.Chat const Completions = Chat.Completions const Responses = OpenAIOrignal.Responses const Embeddings = OpenAIOrignal.Embeddings +const Audio = OpenAIOrignal.Audio +const Transcriptions = OpenAIOrignal.Audio.Transcriptions type ChatCompletion = OpenAIOrignal.ChatCompletion type ChatCompletionChunk = OpenAIOrignal.ChatCompletionChunk @@ -46,6 +48,7 @@ export class PostHogOpenAI extends OpenAIOrignal { public chat: WrappedChat public responses: WrappedResponses public embeddings: WrappedEmbeddings + public audio: WrappedAudio constructor(config: MonitoringOpenAIConfig) { const { posthog, ...openAIConfig } = config @@ -54,6 +57,7 @@ export class PostHogOpenAI extends OpenAIOrignal { this.chat = new WrappedChat(this, this.phClient) this.responses = new WrappedResponses(this, this.phClient) this.embeddings = new WrappedEmbeddings(this, this.phClient) + this.audio = new WrappedAudio(this, this.phClient) } } @@ -651,6 +655,224 @@ export class WrappedEmbeddings extends Embeddings { } } +export class WrappedAudio extends Audio { + constructor(parentClient: PostHogOpenAI, phClient: PostHog) { + super(parentClient) + this.transcriptions = new WrappedTranscriptions(parentClient, phClient) + } + + public transcriptions: WrappedTranscriptions +} + +export class WrappedTranscriptions extends Transcriptions { + private readonly phClient: PostHog + private readonly baseURL: string + + constructor(client: OpenAIOrignal, phClient: PostHog) { + super(client) + this.phClient = phClient + this.baseURL = client.baseURL + } + + // --- Overload #1: Non-streaming + public create( + body: OpenAIOrignal.Audio.Transcriptions.TranscriptionCreateParamsNonStreaming<'json' | undefined> & + MonitoringParams, + options?: RequestOptions + ): APIPromise + + // --- Overload #2: Non-streaming + public create( + body: OpenAIOrignal.Audio.Transcriptions.TranscriptionCreateParamsNonStreaming<'verbose_json'> & MonitoringParams, + options?: RequestOptions + ): APIPromise + + // --- Overload #3: Non-streaming + public create( + body: OpenAIOrignal.Audio.Transcriptions.TranscriptionCreateParamsNonStreaming<'srt' | 'vtt' | 'text'> & + MonitoringParams, + options?: RequestOptions + ): APIPromise + + // --- Overload #4: Non-streaming + public create( + body: OpenAIOrignal.Audio.Transcriptions.TranscriptionCreateParamsNonStreaming, + options?: RequestOptions + ): APIPromise + + // --- Overload #5: Streaming + public create( + body: OpenAIOrignal.Audio.Transcriptions.TranscriptionCreateParamsStreaming & MonitoringParams, + options?: RequestOptions + ): APIPromise> + + // --- Overload #6: Streaming + public create( + body: OpenAIOrignal.Audio.Transcriptions.TranscriptionCreateParamsStreaming & MonitoringParams, + options?: RequestOptions + ): APIPromise< + | OpenAIOrignal.Audio.Transcriptions.TranscriptionCreateResponse + | string + | Stream + > + + // --- Overload #7: Generic base + public create( + body: OpenAIOrignal.Audio.Transcriptions.TranscriptionCreateParams & MonitoringParams, + options?: RequestOptions + ): APIPromise< + | OpenAIOrignal.Audio.Transcriptions.TranscriptionCreateResponse + | string + | Stream + > + + // --- Implementation Signature + public create( + body: OpenAIOrignal.Audio.Transcriptions.TranscriptionCreateParams & MonitoringParams, + options?: RequestOptions + ): APIPromise< + | OpenAIOrignal.Audio.Transcriptions.TranscriptionCreateResponse + | string + | Stream + > { + const { providerParams: openAIParams, posthogParams } = + extractPosthogParams(body) + const startTime = Date.now() + + const parentPromise = openAIParams.stream + ? super.create(openAIParams, options) + : super.create(openAIParams, options) + + if (openAIParams.stream) { + return parentPromise.then((value) => { + if ('tee' in value && typeof (value as any).tee === 'function') { + const [stream1, stream2] = (value as any).tee() + ;(async () => { + try { + let finalContent: any[] = [] + let usage: { + inputTokens?: number + outputTokens?: number + } = { + inputTokens: 0, + outputTokens: 0, + } + + const doneEvent: OpenAIOrignal.Audio.Transcriptions.TranscriptionTextDoneEvent['type'] = + 'transcript.text.done' + for await (const chunk of stream1) { + if (chunk.type === doneEvent && 'text' in chunk && chunk.text && chunk.text.length > 0) { + finalContent = chunk.text + } + if ('usage' in chunk && chunk.usage) { + usage = { + inputTokens: chunk.usage?.type === 'tokens' ? (chunk.usage.input_tokens ?? 0) : 0, + outputTokens: chunk.usage?.type === 'tokens' ? (chunk.usage.output_tokens ?? 0) : 0, + } + } + } + + const latency = (Date.now() - startTime) / 1000 + const availableTools = extractAvailableToolCalls('openai', openAIParams) + await sendEventToPosthog({ + client: this.phClient, + ...posthogParams, + model: openAIParams.model, + provider: 'openai', + input: openAIParams.prompt, + output: finalContent, + latency, + baseURL: this.baseURL, + params: body, + httpStatus: 200, + usage, + tools: availableTools, + }) + } catch (error: unknown) { + const httpStatus = + error && typeof error === 'object' && 'status' in error + ? ((error as { status?: number }).status ?? 500) + : 500 + + await sendEventToPosthog({ + client: this.phClient, + ...posthogParams, + model: openAIParams.model, + provider: 'openai', + input: openAIParams.prompt, + output: [], + latency: 0, + baseURL: this.baseURL, + params: body, + httpStatus, + usage: { inputTokens: 0, outputTokens: 0 }, + isError: true, + error: JSON.stringify(error), + }) + } + })() + + return stream2 + } + return value + }) as APIPromise> + } else { + const wrappedPromise = parentPromise.then( + async (result) => { + if ('text' in result) { + const latency = (Date.now() - startTime) / 1000 + await sendEventToPosthog({ + client: this.phClient, + ...posthogParams, + model: String(openAIParams.model ?? ''), + provider: 'openai', + input: openAIParams.prompt, + output: result.text, + latency, + baseURL: this.baseURL, + params: body, + httpStatus: 200, + usage: { + inputTokens: result.usage?.type === 'tokens' ? (result.usage.input_tokens ?? 0) : 0, + outputTokens: result.usage?.type === 'tokens' ? (result.usage.output_tokens ?? 0) : 0, + }, + }) + return result + } + }, + async (error: unknown) => { + const httpStatus = + error && typeof error === 'object' && 'status' in error + ? ((error as { status?: number }).status ?? 500) + : 500 + + await sendEventToPosthog({ + client: this.phClient, + ...posthogParams, + model: String(openAIParams.model ?? ''), + provider: 'openai', + input: openAIParams.prompt, + output: [], + latency: 0, + baseURL: this.baseURL, + params: body, + httpStatus, + usage: { + inputTokens: 0, + outputTokens: 0, + }, + isError: true, + error: JSON.stringify(error), + }) + throw error + } + ) as APIPromise + + return wrappedPromise + } + } +} + export default PostHogOpenAI export { PostHogOpenAI as OpenAI } diff --git a/packages/ai/src/utils.ts b/packages/ai/src/utils.ts index a0cb2071ca..406be38cc3 100644 --- a/packages/ai/src/utils.ts +++ b/packages/ai/src/utils.ts @@ -14,6 +14,7 @@ type ChatCompletionCreateParamsBase = OpenAIOrignal.Chat.Completions.ChatComplet type MessageCreateParams = AnthropicOriginal.Messages.MessageCreateParams type ResponseCreateParams = OpenAIOrignal.Responses.ResponseCreateParams type EmbeddingCreateParams = OpenAIOrignal.EmbeddingCreateParams +type TranscriptionCreateParams = OpenAIOrignal.Audio.Transcriptions.TranscriptionCreateParams type AnthropicTool = AnthropicOriginal.Tool // limit large outputs by truncating to 200kb (approx 200k bytes) @@ -77,6 +78,7 @@ export const getModelParams = ( | ResponseCreateParams | ResponseCreateParamsWithTools | EmbeddingCreateParams + | TranscriptionCreateParams ) & MonitoringParams) | null @@ -402,6 +404,7 @@ export type SendEventToPosthogParams = { | ResponseCreateParams | ResponseCreateParamsWithTools | EmbeddingCreateParams + | TranscriptionCreateParams ) & MonitoringParams isError?: boolean From 42b02ad3db99566e31df532be5208e9c95942707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kj=C3=A6r?= Date: Tue, 28 Oct 2025 11:54:43 +0100 Subject: [PATCH 2/5] Use string instead of any --- packages/ai/src/openai/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ai/src/openai/index.ts b/packages/ai/src/openai/index.ts index 26d601db42..195d9f8fe3 100644 --- a/packages/ai/src/openai/index.ts +++ b/packages/ai/src/openai/index.ts @@ -749,7 +749,7 @@ export class WrappedTranscriptions extends Transcriptions { const [stream1, stream2] = (value as any).tee() ;(async () => { try { - let finalContent: any[] = [] + let finalContent: string = '' let usage: { inputTokens?: number outputTokens?: number From 8677a6fd25780c59d6c9850fab7d8923cba650b4 Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Wed, 12 Nov 2025 13:53:16 +0000 Subject: [PATCH 3/5] add tests for openai audio transcription --- packages/ai/tests/openai.test.ts | 281 +++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) diff --git a/packages/ai/tests/openai.test.ts b/packages/ai/tests/openai.test.ts index 94b14588ed..921fd1d143 100644 --- a/packages/ai/tests/openai.test.ts +++ b/packages/ai/tests/openai.test.ts @@ -67,11 +67,26 @@ jest.mock('openai', () => { } } + // Mock Transcriptions class + class MockTranscriptions { + constructor() {} + create() { + return Promise.resolve({}) + } + } + + // Mock Audio class + class MockAudio { + constructor() {} + static Transcriptions = MockTranscriptions + } + // Mock OpenAI class class MockOpenAI { chat: any embeddings: any responses: any + audio: any constructor() { this.chat = { completions: { @@ -84,10 +99,16 @@ jest.mock('openai', () => { this.responses = { create: jest.fn(), } + this.audio = { + transcriptions: { + create: jest.fn(), + }, + } } static Chat = MockChat static Responses = MockResponses static Embeddings = MockEmbeddings + static Audio = MockAudio } return { @@ -97,6 +118,7 @@ jest.mock('openai', () => { Chat: MockChat, Responses: MockResponses, Embeddings: MockEmbeddings, + Audio: MockAudio, } }) @@ -1210,6 +1232,265 @@ describe('PostHogOpenAI - Jest test suite', () => { }) }) + describe('Transcriptions', () => { + let mockTranscriptionResponse: any + + beforeEach(() => { + // Default transcription response + mockTranscriptionResponse = { + text: 'Hello, this is a test transcription.', + } + + // Mock the Audio.Transcriptions.prototype.create method + const AudioMock: any = openaiModule.Audio + const TranscriptionsMock = AudioMock.Transcriptions + TranscriptionsMock.prototype.create = jest.fn().mockResolvedValue(mockTranscriptionResponse) + }) + + conditionalTest('basic transcription', async () => { + const mockFile = new Blob(['mock audio data'], { type: 'audio/mpeg' }) as any + mockFile.name = 'test.mp3' + + const response = await client.audio.transcriptions.create({ + file: mockFile, + model: 'whisper-1', + posthogDistinctId: 'test-transcription-user', + posthogProperties: { test: 'transcription' }, + }) + + expect(response).toEqual(mockTranscriptionResponse) + expect(mockPostHogClient.capture).toHaveBeenCalledTimes(1) + + const [captureArgs] = (mockPostHogClient.capture as jest.Mock).mock.calls + const { distinctId, event, properties } = captureArgs[0] + + expect(distinctId).toBe('test-transcription-user') + expect(event).toBe('$ai_generation') + expect(properties['$ai_lib']).toBe('posthog-ai') + expect(properties['$ai_lib_version']).toBe(version) + expect(properties['$ai_provider']).toBe('openai') + expect(properties['$ai_model']).toBe('whisper-1') + expect(properties['$ai_output_choices']).toBe('Hello, this is a test transcription.') + expect(properties['$ai_http_status']).toBe(200) + expect(properties['test']).toBe('transcription') + expect(typeof properties['$ai_latency']).toBe('number') + expect(properties['$ai_input_tokens']).toBe(0) + expect(properties['$ai_output_tokens']).toBe(0) + }) + + conditionalTest('transcription with prompt', async () => { + const mockFile = new Blob(['mock audio data'], { type: 'audio/mpeg' }) as any + mockFile.name = 'test.mp3' + + await client.audio.transcriptions.create({ + file: mockFile, + model: 'whisper-1', + prompt: 'This is a test prompt to guide transcription.', + posthogDistinctId: 'test-user', + }) + + expect(mockPostHogClient.capture).toHaveBeenCalledTimes(1) + const [captureArgs] = (mockPostHogClient.capture as jest.Mock).mock.calls + const { properties } = captureArgs[0] + + expect(properties['$ai_input']).toBe('This is a test prompt to guide transcription.') + }) + + conditionalTest('transcription with language parameter', async () => { + const mockFile = new Blob(['mock audio data'], { type: 'audio/mpeg' }) as any + mockFile.name = 'test.mp3' + + await client.audio.transcriptions.create({ + file: mockFile, + model: 'whisper-1', + language: 'en', + posthogDistinctId: 'test-user', + }) + + expect(mockPostHogClient.capture).toHaveBeenCalledTimes(1) + const [captureArgs] = (mockPostHogClient.capture as jest.Mock).mock.calls + const { properties } = captureArgs[0] + + expect(properties['$ai_model_parameters']).toMatchObject({ + language: 'en', + }) + }) + + conditionalTest('transcription verbose json format', async () => { + const mockVerboseResponse = { + language: 'english', + duration: 2.5, + text: 'Hello, this is a test transcription.', + words: [ + { word: 'Hello', start: 0.0, end: 0.5 }, + { word: 'this', start: 0.5, end: 0.8 }, + ], + } + + const AudioMock: any = openaiModule.Audio + const TranscriptionsMock = AudioMock.Transcriptions + TranscriptionsMock.prototype.create = jest.fn().mockResolvedValue(mockVerboseResponse) + + const mockFile = new Blob(['mock audio data'], { type: 'audio/mpeg' }) as any + mockFile.name = 'test.mp3' + + const response = await client.audio.transcriptions.create({ + file: mockFile, + model: 'whisper-1', + response_format: 'verbose_json', + posthogDistinctId: 'test-verbose-user', + }) + + expect(response).toEqual(mockVerboseResponse) + expect(mockPostHogClient.capture).toHaveBeenCalledTimes(1) + + const [captureArgs] = (mockPostHogClient.capture as jest.Mock).mock.calls + const { properties } = captureArgs[0] + + expect(properties['$ai_output_choices']).toBe('Hello, this is a test transcription.') + expect(properties['$ai_model_parameters']).toMatchObject({ + response_format: 'verbose_json', + }) + }) + + conditionalTest('transcription privacy mode', async () => { + const mockFile = new Blob(['mock audio data'], { type: 'audio/mpeg' }) as any + mockFile.name = 'test.mp3' + + await client.audio.transcriptions.create({ + file: mockFile, + model: 'whisper-1', + prompt: 'Sensitive prompt', + posthogDistinctId: 'test-user', + posthogPrivacyMode: true, + }) + + expect(mockPostHogClient.capture).toHaveBeenCalledTimes(1) + const [captureArgs] = (mockPostHogClient.capture as jest.Mock).mock.calls + const { properties } = captureArgs[0] + + expect(properties['$ai_input']).toBeNull() + expect(properties['$ai_output_choices']).toBeNull() + }) + + conditionalTest('transcription with usage tokens', async () => { + const responseWithUsage = { + text: 'Hello, this is a test transcription.', + usage: { + type: 'tokens' as const, + input_tokens: 150, + output_tokens: 50, + }, + } + + const AudioMock: any = openaiModule.Audio + const TranscriptionsMock = AudioMock.Transcriptions + TranscriptionsMock.prototype.create = jest.fn().mockResolvedValue(responseWithUsage) + + const mockFile = new Blob(['mock audio data'], { type: 'audio/mpeg' }) as any + mockFile.name = 'test.mp3' + + await client.audio.transcriptions.create({ + file: mockFile, + model: 'whisper-1', + posthogDistinctId: 'test-user', + }) + + expect(mockPostHogClient.capture).toHaveBeenCalledTimes(1) + const [captureArgs] = (mockPostHogClient.capture as jest.Mock).mock.calls + const { properties } = captureArgs[0] + + expect(properties['$ai_input_tokens']).toBe(150) + expect(properties['$ai_output_tokens']).toBe(50) + }) + + conditionalTest('transcription error handling', async () => { + const AudioMock: any = openaiModule.Audio + const TranscriptionsMock = AudioMock.Transcriptions + const testError = new Error('API Error') as Error & { status: number } + testError.status = 400 + TranscriptionsMock.prototype.create = jest.fn().mockRejectedValue(testError) + + const mockFile = new Blob(['mock audio data'], { type: 'audio/mpeg' }) as any + mockFile.name = 'test.mp3' + + await expect( + client.audio.transcriptions.create({ + file: mockFile, + model: 'whisper-1', + posthogDistinctId: 'error-user', + }) + ).rejects.toThrow('API Error') + + expect(mockPostHogClient.capture).toHaveBeenCalledTimes(1) + const [captureArgs] = (mockPostHogClient.capture as jest.Mock).mock.calls + const { properties } = captureArgs[0] + + expect(properties['$ai_http_status']).toBe(400) + expect(properties['$ai_is_error']).toBe(true) + expect(properties['$ai_error']).toContain('400') + }) + + conditionalTest('transcription captureImmediate flag', async () => { + const mockFile = new Blob(['mock audio data'], { type: 'audio/mpeg' }) as any + mockFile.name = 'test.mp3' + + await client.audio.transcriptions.create({ + file: mockFile, + model: 'whisper-1', + posthogDistinctId: 'test-user', + posthogCaptureImmediate: true, + }) + + expect(mockPostHogClient.captureImmediate).toHaveBeenCalledTimes(1) + expect(mockPostHogClient.capture).toHaveBeenCalledTimes(0) + }) + + conditionalTest('transcription groups', async () => { + const mockFile = new Blob(['mock audio data'], { type: 'audio/mpeg' }) as any + mockFile.name = 'test.mp3' + + await client.audio.transcriptions.create({ + file: mockFile, + model: 'whisper-1', + posthogDistinctId: 'test-user', + posthogGroups: { company: 'test_company' }, + }) + + expect(mockPostHogClient.capture).toHaveBeenCalledTimes(1) + const [captureArgs] = (mockPostHogClient.capture as jest.Mock).mock.calls + const { groups } = captureArgs[0] + expect(groups).toEqual({ company: 'test_company' }) + }) + + conditionalTest('posthogProperties are not sent to OpenAI', async () => { + const AudioMock: any = openaiModule.Audio + const TranscriptionsMock = AudioMock.Transcriptions + const mockCreate = jest.fn().mockResolvedValue(mockTranscriptionResponse) + const originalCreate = TranscriptionsMock.prototype.create + TranscriptionsMock.prototype.create = mockCreate + + const mockFile = new Blob(['mock audio data'], { type: 'audio/mpeg' }) as any + mockFile.name = 'test.mp3' + + await client.audio.transcriptions.create({ + file: mockFile, + model: 'whisper-1', + posthogDistinctId: 'test-user', + posthogProperties: { key: 'value' }, + posthogGroups: { team: 'test' }, + posthogPrivacyMode: true, + posthogCaptureImmediate: true, + posthogTraceId: 'trace-123', + }) + + const [actualParams] = mockCreate.mock.calls[0] + const posthogParams = Object.keys(actualParams).filter((key) => key.startsWith('posthog')) + expect(posthogParams).toEqual([]) + TranscriptionsMock.prototype.create = originalCreate + }) + }) + conditionalTest('posthogProperties are not sent to OpenAI', async () => { const ChatMock: any = openaiModule.Chat const mockCreate = jest.fn().mockResolvedValue({}) From 5de85b2e0d10d3bc7cd51e5f4539dc081360e696 Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Wed, 12 Nov 2025 14:21:33 +0000 Subject: [PATCH 4/5] fix: add transcription parameters to model params tracking - Add language, response_format, and timestamp_granularities to getModelParams - Ensures transcription-specific parameters are captured in $ai_model_parameters - Fixes test failures for transcription language and response_format tests --- packages/ai/src/utils.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/ai/src/utils.ts b/packages/ai/src/utils.ts index 19967b2a68..a5d36fe034 100644 --- a/packages/ai/src/utils.ts +++ b/packages/ai/src/utils.ts @@ -98,6 +98,9 @@ export const getModelParams = ( 'stop', 'stream', 'streaming', + 'language', + 'response_format', + 'timestamp_granularities', ] as const for (const key of paramKeys) { From 30a5968d625d1c04058642dd66bf37d5378cd84f Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Wed, 12 Nov 2025 14:49:49 +0000 Subject: [PATCH 5/5] chore: add @types/cross-spawn for TypeScript declarations --- pnpm-lock.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ca3ce513be..2faa5e7e29 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,8 +7,8 @@ settings: catalogs: default: '@posthog/cli': - specifier: ~0.5.8 - version: 0.5.8 + specifier: ~0.5.9 + version: 0.5.10 '@rslib/core': specifier: ^0.10.5 version: 0.10.6 @@ -417,7 +417,7 @@ importers: dependencies: '@posthog/cli': specifier: 'catalog:' - version: 0.5.8 + version: 0.5.10 '@posthog/core': specifier: workspace:* version: link:../core @@ -476,7 +476,7 @@ importers: version: 4.1.3(magicast@0.3.5) '@posthog/cli': specifier: 'catalog:' - version: 0.5.8 + version: 0.5.10 '@posthog/core': specifier: workspace:* version: link:../core @@ -2026,7 +2026,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {'0': node >=0.10.0} + engines: {node: '>=0.10.0'} '@expo/cli@0.1.7': resolution: {integrity: sha512-F81fPthpT7QtVu1P7QeZMezGn0tCcalCh3ANIzWBaQZNG4vly7mo2dp3PMGzNdmXq6yt93bJ4HbfS+0/NpKl7g==} @@ -3257,8 +3257,8 @@ packages: '@poppinss/exception@1.2.2': resolution: {integrity: sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==} - '@posthog/cli@0.5.8': - resolution: {integrity: sha512-CO+Rl5J29W9jN0bnsr9Ugl/vhMNl17o9i7EC3hSMGf0Dizi2FqNP7hTh9nZTxWs6lOR+XhIXotiakmXF0HE19A==} + '@posthog/cli@0.5.10': + resolution: {integrity: sha512-AJlCebe/oerSeAAzxfAu0upbWFgX7SxXBor2nsH9yyf8rai1tl7hzerRcRNHL+Iz+SPndv7Kvg2g0rO8S0a1cw==} engines: {node: '>=14', npm: '>=6'} hasBin: true @@ -16734,7 +16734,7 @@ snapshots: '@poppinss/exception@1.2.2': {} - '@posthog/cli@0.5.8': + '@posthog/cli@0.5.10': dependencies: axios: 1.12.2 axios-proxy-builder: 0.1.2