@@ -7,6 +7,7 @@ import { MACHINE_METADATA } from "./constants.js";
77import { EventCache } from "./eventCache.js" ;
88import nodeMachineId from "node-machine-id" ;
99import { getDeviceId } from "@mongodb-js/device-id" ;
10+ import fs from "fs/promises" ;
1011
1112type EventResult = {
1213 success : boolean ;
@@ -15,38 +16,36 @@ type EventResult = {
1516
1617export const DEVICE_ID_TIMEOUT = 3000 ;
1718
19+ export type TelemetryOptions = {
20+ session : Session ;
21+ userConfig : UserConfig ;
22+ commonProperties ?: CommonProperties ;
23+ eventCache ?: EventCache ;
24+ getRawMachineId ?: ( ) => Promise < string > ;
25+ } ;
26+
1827export class Telemetry {
1928 private isBufferingEvents : boolean = true ;
2029 /** Resolves when the device ID is retrieved or timeout occurs */
2130 public deviceIdPromise : Promise < string > | undefined ;
2231 private deviceIdAbortController = new AbortController ( ) ;
23- private eventCache : EventCache ;
24- private getRawMachineId : ( ) => Promise < string > ;
2532
2633 private constructor (
2734 private readonly session : Session ,
2835 private readonly userConfig : UserConfig ,
2936 private readonly commonProperties : CommonProperties ,
30- { eventCache, getRawMachineId } : { eventCache : EventCache ; getRawMachineId : ( ) => Promise < string > }
31- ) {
32- this . eventCache = eventCache ;
33- this . getRawMachineId = getRawMachineId ;
34- }
35-
36- static create (
37- session : Session ,
38- userConfig : UserConfig ,
39- {
40- commonProperties = { ...MACHINE_METADATA } ,
41- eventCache = EventCache . getInstance ( ) ,
42- getRawMachineId = ( ) => nodeMachineId . machineId ( true ) ,
43- } : {
44- eventCache ?: EventCache ;
45- getRawMachineId ?: ( ) => Promise < string > ;
46- commonProperties ?: CommonProperties ;
47- } = { }
48- ) : Telemetry {
49- const instance = new Telemetry ( session , userConfig , commonProperties , { eventCache, getRawMachineId } ) ;
37+ private readonly eventCache : EventCache ,
38+ private readonly getRawMachineId : ( ) => Promise < string >
39+ ) { }
40+
41+ static create ( { session, userConfig, commonProperties, eventCache, getRawMachineId } : TelemetryOptions ) : Telemetry {
42+ const instance = new Telemetry (
43+ session ,
44+ userConfig ,
45+ commonProperties || { ...MACHINE_METADATA } ,
46+ eventCache || EventCache . getInstance ( ) ,
47+ getRawMachineId || ( ( ) => nodeMachineId . machineId ( true ) )
48+ ) ;
5049
5150 void instance . start ( ) ;
5251 return instance ;
@@ -106,17 +105,47 @@ export class Telemetry {
106105 * Gets the common properties for events
107106 * @returns Object containing common properties for all events
108107 */
109- public getCommonProperties ( ) : CommonProperties {
108+ public async getCommonProperties ( ) : Promise < CommonProperties > {
110109 return {
111110 ...this . commonProperties ,
112111 mcp_client_version : this . session . agentRunner ?. version ,
113112 mcp_client_name : this . session . agentRunner ?. name ,
114113 session_id : this . session . sessionId ,
115114 config_atlas_auth : this . session . apiClient . hasCredentials ( ) ? "true" : "false" ,
116115 config_connection_string : this . userConfig . connectionString ? "true" : "false" ,
116+ is_container_env : ( await this . isContainerized ( ) ) ? "true" : "false" ,
117117 } ;
118118 }
119119
120+ private async fileExists ( filePath : string ) : Promise < boolean > {
121+ try {
122+ await fs . stat ( filePath ) ;
123+ return true ; // File exists
124+ } catch ( e : unknown ) {
125+ if (
126+ e instanceof Error &&
127+ (
128+ e as Error & {
129+ code : string ;
130+ }
131+ ) . code === "ENOENT"
132+ ) {
133+ return false ; // File does not exist
134+ }
135+ throw e ; // Re-throw unexpected errors
136+ }
137+ }
138+
139+ private async isContainerized ( ) : Promise < boolean > {
140+ for ( const file of [ "/.dockerenv" , "/run/.containerenv" , "/var/run/.containerenv" ] ) {
141+ const fileExists = await this . fileExists ( file ) ;
142+ if ( fileExists ) {
143+ return true ;
144+ }
145+ }
146+ return ! ! process . env . container ;
147+ }
148+
120149 /**
121150 * Checks if telemetry is currently enabled
122151 * This is a method rather than a constant to capture runtime config changes
@@ -177,10 +206,11 @@ export class Telemetry {
177206 */
178207 private async sendEvents ( client : ApiClient , events : BaseEvent [ ] ) : Promise < EventResult > {
179208 try {
209+ const commonProperties = await this . getCommonProperties ( ) ;
180210 await client . sendEvents (
181211 events . map ( ( event ) => ( {
182212 ...event ,
183- properties : { ...this . getCommonProperties ( ) , ...event . properties } ,
213+ properties : { ...commonProperties , ...event . properties } ,
184214 } ) )
185215 ) ;
186216 return { success : true } ;
0 commit comments