@antsplatform/otel
Advanced tools
+133
-4
@@ -29,2 +29,4 @@ "use strict"; | ||
| var import_core2 = require("@antsplatform/core"); | ||
| var import_blake2 = require("@noble/hashes/blake2.js"); | ||
| var import_utils = require("@noble/hashes/utils.js"); | ||
| var import_core3 = require("@opentelemetry/core"); | ||
@@ -267,2 +269,46 @@ var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http"); | ||
| // src/span-processor.ts | ||
| var MAX_AGENT_NAME_LENGTH = 255; | ||
| function generateAgentId(agentName, projectId) { | ||
| const logger = (0, import_core2.getGlobalLogger)(); | ||
| if (!agentName || typeof agentName !== "string") { | ||
| throw new Error("agentName must be a non-empty string"); | ||
| } | ||
| if (!projectId || typeof projectId !== "string") { | ||
| throw new Error("projectId must be a non-empty string"); | ||
| } | ||
| let name = agentName.trim(); | ||
| if (name.length > MAX_AGENT_NAME_LENGTH) { | ||
| logger.warn( | ||
| `agentName too long (${name.length} chars), truncated to ${MAX_AGENT_NAME_LENGTH} characters` | ||
| ); | ||
| name = name.slice(0, MAX_AGENT_NAME_LENGTH); | ||
| } | ||
| const agentId = blake2b64(name, projectId.trim()); | ||
| logger.debug(`[AGENT_ID] Generated: ${agentId} from agent_name: ${name}`); | ||
| return agentId; | ||
| } | ||
| function blake2b64(agentName, projectId) { | ||
| const combined = new TextEncoder().encode(agentName + projectId); | ||
| const hash = (0, import_blake2.blake2b)(combined, { dkLen: 8 }); | ||
| return (0, import_utils.bytesToHex)(hash); | ||
| } | ||
| function resolveAgentConfigWithProjectId(config, projectId) { | ||
| var _a; | ||
| if (!projectId || !projectId.trim()) { | ||
| throw new Error("projectId is required"); | ||
| } | ||
| if (!config.agentName || !config.agentName.trim()) { | ||
| throw new Error("agentName is required"); | ||
| } | ||
| const agentId = generateAgentId( | ||
| config.agentName.trim(), | ||
| projectId.trim() | ||
| ); | ||
| return { | ||
| agentId, | ||
| agentName: config.agentName.trim(), | ||
| agentDisplayName: (_a = config.agentDisplayName) == null ? void 0 : _a.trim(), | ||
| projectId: projectId.trim() | ||
| }; | ||
| } | ||
| var AntsPlatformSpanProcessor = class { | ||
@@ -352,2 +398,7 @@ /** | ||
| this.mediaService = new MediaService({ apiClient: this.apiClient }); | ||
| this.testProjectId = params == null ? void 0 : params._testProjectId; | ||
| if (params == null ? void 0 : params.agent) { | ||
| this.pendingAgentConfig = params.agent; | ||
| this.agentConfigPromise = this.initializeAgentConfig(params.agent); | ||
| } | ||
| logger.debug("Initialized AntsPlatformSpanProcessor with params:", { | ||
@@ -360,5 +411,69 @@ publicKey, | ||
| flushAt, | ||
| flushIntervalSeconds | ||
| flushIntervalSeconds, | ||
| hasAgentConfig: !!(params == null ? void 0 : params.agent) | ||
| }); | ||
| } | ||
| /** | ||
| * Fetches project_id from the API and resolves agent configuration. | ||
| * This matches the Java/Python SDK behavior where project_id is always fetched from the API. | ||
| * | ||
| * @param config - Agent configuration with agentName and optional agentDisplayName | ||
| * @internal | ||
| */ | ||
| async initializeAgentConfig(config) { | ||
| const logger = (0, import_core2.getGlobalLogger)(); | ||
| try { | ||
| let projectId = null; | ||
| if (this.testProjectId) { | ||
| logger.debug(`[AGENT_CONFIG] Using test projectId: ${this.testProjectId}`); | ||
| projectId = this.testProjectId; | ||
| } else { | ||
| projectId = await this.fetchProjectId(); | ||
| } | ||
| if (!projectId) { | ||
| logger.error( | ||
| "[AGENT_CONFIG] Failed to fetch project_id from API. Agent attributes will not be added to spans." | ||
| ); | ||
| return; | ||
| } | ||
| logger.debug(`[AGENT_CONFIG] Using projectId: ${projectId}`); | ||
| this.resolvedAgentConfig = resolveAgentConfigWithProjectId(config, projectId); | ||
| this.cachedProjectId = projectId; | ||
| logger.info("[AGENT_CONFIG] Successfully initialized agent configuration:", { | ||
| agentId: this.resolvedAgentConfig.agentId, | ||
| agentName: this.resolvedAgentConfig.agentName, | ||
| agentDisplayName: this.resolvedAgentConfig.agentDisplayName, | ||
| projectId: this.resolvedAgentConfig.projectId | ||
| }); | ||
| } catch (error) { | ||
| logger.error( | ||
| `[AGENT_CONFIG] Failed to initialize agent configuration: ${error instanceof Error ? error.message : String(error)}` | ||
| ); | ||
| } | ||
| } | ||
| /** | ||
| * Fetches the project_id from the Ants Platform API. | ||
| * This matches the Python SDK behavior: GET /api/public/projects -> data[0].id | ||
| * | ||
| * @returns Promise resolving to the project ID, or null if unavailable | ||
| * @internal | ||
| */ | ||
| async fetchProjectId() { | ||
| const logger = (0, import_core2.getGlobalLogger)(); | ||
| try { | ||
| const response = await this.apiClient.projects.get(); | ||
| if (response.data && response.data.length > 0) { | ||
| const projectId = response.data[0].id; | ||
| logger.debug(`[PROJECT_ID] Fetched from API: ${projectId}`); | ||
| return projectId; | ||
| } | ||
| logger.warn("[PROJECT_ID] No projects found in API response"); | ||
| return null; | ||
| } catch (error) { | ||
| logger.warn( | ||
| `[PROJECT_ID] Failed to fetch from API: ${error instanceof Error ? error.message : String(error)}` | ||
| ); | ||
| return null; | ||
| } | ||
| } | ||
| get logger() { | ||
@@ -368,3 +483,3 @@ return (0, import_core2.getGlobalLogger)(); | ||
| /** | ||
| * Called when a span is started. Adds environment and release attributes to the span. | ||
| * Called when a span is started. Adds environment, release, and agent attributes to the span. | ||
| * | ||
@@ -377,6 +492,17 @@ * @param span - The span that was started | ||
| onStart(span, parentContext) { | ||
| span.setAttributes({ | ||
| const attributes = { | ||
| [import_core2.AntsPlatformOtelSpanAttributes.ENVIRONMENT]: this.environment, | ||
| [import_core2.AntsPlatformOtelSpanAttributes.RELEASE]: this.release | ||
| }); | ||
| }; | ||
| if (this.resolvedAgentConfig) { | ||
| attributes[import_core2.AntsPlatformOtelSpanAttributes.AGENT_ID] = this.resolvedAgentConfig.agentId; | ||
| attributes[import_core2.AntsPlatformOtelSpanAttributes.AGENT_NAME] = this.resolvedAgentConfig.agentName; | ||
| attributes[import_core2.AntsPlatformOtelSpanAttributes.AGENT_DISPLAY_NAME] = this.resolvedAgentConfig.agentDisplayName; | ||
| attributes[import_core2.AntsPlatformOtelSpanAttributes.PROJECT_ID] = this.resolvedAgentConfig.projectId; | ||
| } else if (this.pendingAgentConfig) { | ||
| this.logger.debug( | ||
| "[AGENT_CONFIG] Agent config not yet resolved, span will not have agent attributes. This may happen for spans created before projectId fetch completes." | ||
| ); | ||
| } | ||
| span.setAttributes(attributes); | ||
| return this.processor.onStart(span, parentContext); | ||
@@ -408,2 +534,5 @@ } | ||
| async flush() { | ||
| if (this.agentConfigPromise) { | ||
| await this.agentConfigPromise; | ||
| } | ||
| await Promise.all(Array.from(this.pendingEndedSpans)); | ||
@@ -410,0 +539,0 @@ await this.mediaService.flush(); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/span-processor.ts","../src/MediaService.ts"],"sourcesContent":["export * from \"./span-processor.js\";\n","import {\n Logger,\n getGlobalLogger,\n AntsPlatformAPIClient,\n ANTS_PLATFORM_SDK_VERSION,\n AntsPlatformOtelSpanAttributes,\n getEnv,\n base64Encode,\n} from \"@antsplatform/core\";\nimport { hrTimeToMilliseconds } from \"@opentelemetry/core\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport {\n Span,\n BatchSpanProcessor,\n SimpleSpanProcessor,\n SpanExporter,\n ReadableSpan,\n SpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\n\nimport { MediaService } from \"./MediaService.js\";\n\n/**\n * Function type for masking sensitive data in spans before export.\n *\n * @param params - Object containing the data to be masked\n * @param params.data - The data that should be masked\n * @returns The masked data (can be of any type)\n *\n * @example\n * ```typescript\n * const maskFunction: MaskFunction = ({ data }) => {\n * if (typeof data === 'string') {\n * return data.replace(/password=\\w+/g, 'password=***');\n * }\n * return data;\n * };\n * ```\n *\n * @public\n */\nexport type MaskFunction = (params: { data: any }) => any;\n\n/**\n * Function type for determining whether a span should be exported to AntsPlatform.\n *\n * @param params - Object containing the span to evaluate\n * @param params.otelSpan - The OpenTelemetry span to evaluate\n * @returns `true` if the span should be exported, `false` otherwise\n *\n * @example\n * ```typescript\n * const shouldExportSpan: ShouldExportSpan = ({ otelSpan }) => {\n * // Only export spans that took longer than 100ms\n * return otelSpan.duration[0] * 1000 + otelSpan.duration[1] / 1000000 > 100;\n * };\n * ```\n *\n * @public\n */\nexport type ShouldExportSpan = (params: { otelSpan: ReadableSpan }) => boolean;\n\n/**\n * Configuration parameters for the AntsPlatformSpanProcessor.\n *\n * @public\n */\nexport interface AntsPlatformSpanProcessorParams {\n /**\n * Custom OpenTelemetry span exporter. If not provided, a default OTLP exporter will be used.\n */\n exporter?: SpanExporter;\n\n /**\n * Ants Platform public API key. Can also be set via ANTS_PLATFORM_PUBLIC_KEY environment variable.\n */\n publicKey?: string;\n\n /**\n * Ants Platform secret API key. Can also be set via ANTS_PLATFORM_SECRET_KEY environment variable.\n */\n secretKey?: string;\n\n /**\n * AntsPlatform instance base URL. Can also be set via ANTS_PLATFORM_BASE_URL environment variable.\n * @defaultValue \"https://api.ants-platform.com\"\n */\n baseUrl?: string;\n\n /**\n * Number of spans to batch before flushing. Can also be set via ANTS_PLATFORM_FLUSH_AT environment variable.\n */\n flushAt?: number;\n\n /**\n * Flush interval in seconds. Can also be set via ANTS_PLATFORM_FLUSH_INTERVAL environment variable.\n */\n flushInterval?: number;\n\n /**\n * Function to mask sensitive data in spans before export.\n */\n mask?: MaskFunction;\n\n /**\n * Function to determine whether a span should be exported to AntsPlatform.\n */\n shouldExportSpan?: ShouldExportSpan;\n\n /**\n * Environment identifier for the traces. Can also be set via ANTS_PLATFORM_TRACING_ENVIRONMENT environment variable.\n */\n environment?: string;\n\n /**\n * Release identifier for the traces. Can also be set via ANTS_PLATFORM_RELEASE environment variable.\n */\n release?: string;\n\n /**\n * Request timeout in seconds. Can also be set via ANTS_PLATFORM_TIMEOUT environment variable.\n * @defaultValue 5\n */\n timeout?: number;\n\n /**\n * Additional HTTP headers to include with requests.\n */\n additionalHeaders?: Record<string, string>;\n /**\n * Span export mode to use.\n *\n * - **batched**: Recommended for production environments with long-running processes.\n * Spans are batched and exported in groups for optimal performance.\n * - **immediate**: Recommended for short-lived environments such as serverless functions.\n * Spans are exported immediately to prevent data loss when the process terminates / is frozen.\n *\n * @defaultValue \"batched\"\n */\n exportMode?: \"immediate\" | \"batched\";\n}\n\n/**\n * OpenTelemetry span processor for sending spans to AntsPlatform.\n *\n * This processor extends the standard BatchSpanProcessor to provide:\n * - Automatic batching and flushing of spans to AntsPlatform\n * - Media content extraction and upload from base64 data URIs\n * - Data masking capabilities for sensitive information\n * - Conditional span export based on custom logic\n * - Environment and release tagging\n *\n * @example\n * ```typescript\n * import { NodeSDK } from '@opentelemetry/sdk-node';\n * import { AntsPlatformSpanProcessor } from '@antsplatform/otel';\n *\n * const sdk = new NodeSDK({\n * spanProcessors: [\n * new AntsPlatformSpanProcessor({\n * publicKey: 'pk_...',\n * secretKey: 'sk_...',\n * baseUrl: 'https://api.ants-platform.com',\n * environment: 'production',\n * mask: ({ data }) => {\n * // Mask sensitive data\n * return data.replace(/api_key=\\w+/g, 'api_key=***');\n * }\n * })\n * ]\n * });\n *\n * sdk.start();\n * ```\n *\n * @public\n */\nexport class AntsPlatformSpanProcessor implements SpanProcessor {\n private pendingEndedSpans: Set<Promise<void>> = new Set();\n\n private publicKey?: string;\n private baseUrl?: string;\n private environment?: string;\n private release?: string;\n private mask?: MaskFunction;\n private shouldExportSpan?: ShouldExportSpan;\n private apiClient: AntsPlatformAPIClient;\n private processor: SpanProcessor;\n private mediaService: MediaService;\n\n /**\n * Creates a new AntsPlatformSpanProcessor instance.\n *\n * @param params - Configuration parameters for the processor\n *\n * @example\n * ```typescript\n * const processor = new AntsPlatformSpanProcessor({\n * publicKey: 'pk_...',\n * secretKey: 'sk_...',\n * environment: 'staging',\n * flushAt: 10,\n * flushInterval: 2,\n * mask: ({ data }) => {\n * // Custom masking logic\n * return typeof data === 'string'\n * ? data.replace(/secret_\\w+/g, 'secret_***')\n * : data;\n * },\n * shouldExportSpan: ({ otelSpan }) => {\n * // Only export spans from specific services\n * return otelSpan.name.startsWith('my-service');\n * }\n * });\n * ```\n */\n constructor(params?: AntsPlatformSpanProcessorParams) {\n const logger = getGlobalLogger();\n\n const publicKey = params?.publicKey ?? getEnv(\"ANTS_PLATFORM_PUBLIC_KEY\");\n const secretKey = params?.secretKey ?? getEnv(\"ANTS_PLATFORM_SECRET_KEY\");\n const baseUrl =\n params?.baseUrl ??\n getEnv(\"ANTS_PLATFORM_BASE_URL\") ??\n getEnv(\"ANTS_PLATFORM_BASEURL\") ?? // legacy v2\n \"https://api.ants-platform.com\";\n\n if (!params?.exporter && !publicKey) {\n logger.warn(\n \"No exporter configured and no public key provided in constructor or as ANTS_PLATFORM_PUBLIC_KEY env var. Span exports will fail.\",\n );\n }\n if (!params?.exporter && !secretKey) {\n logger.warn(\n \"No exporter configured and no secret key provided in constructor or as ANTS_PLATFORM_SECRET_KEY env var. Span exports will fail.\",\n );\n }\n const flushAt = params?.flushAt ?? getEnv(\"ANTS_PLATFORM_FLUSH_AT\");\n const flushIntervalSeconds =\n params?.flushInterval ?? getEnv(\"ANTS_PLATFORM_FLUSH_INTERVAL\");\n\n const authHeaderValue = base64Encode(`${publicKey}:${secretKey}`);\n const timeoutSeconds =\n params?.timeout ?? Number(getEnv(\"ANTS_PLATFORM_TIMEOUT\") ?? 5);\n\n const exporter =\n params?.exporter ??\n new OTLPTraceExporter({\n url: `${baseUrl}/api/public/otel/v1/traces`,\n headers: {\n Authorization: `Basic ${authHeaderValue}`,\n x_antsPlatform_sdk_name: \"javascript\",\n x_antsPlatform_sdk_version: ANTS_PLATFORM_SDK_VERSION,\n x_antsPlatform_public_key: publicKey ?? \"<missing>\",\n ...params?.additionalHeaders,\n },\n timeoutMillis: timeoutSeconds * 1_000,\n });\n\n this.processor =\n params?.exportMode === \"immediate\"\n ? new SimpleSpanProcessor(exporter)\n : new BatchSpanProcessor(exporter, {\n maxExportBatchSize: flushAt ? Number(flushAt) : undefined,\n scheduledDelayMillis: flushIntervalSeconds\n ? Number(flushIntervalSeconds) * 1_000\n : undefined,\n });\n\n this.publicKey = publicKey;\n this.baseUrl = baseUrl;\n this.environment =\n params?.environment ?? getEnv(\"ANTS_PLATFORM_TRACING_ENVIRONMENT\");\n this.release = params?.release ?? getEnv(\"ANTS_PLATFORM_RELEASE\");\n this.mask = params?.mask;\n this.shouldExportSpan = params?.shouldExportSpan;\n this.apiClient = new AntsPlatformAPIClient({\n baseUrl: this.baseUrl,\n username: this.publicKey,\n password: secretKey,\n xAntsPlatformPublicKey: this.publicKey,\n xAntsPlatformSdkVersion: ANTS_PLATFORM_SDK_VERSION,\n xAntsPlatformSdkName: \"javascript\",\n environment: \"\", // noop as baseUrl is set\n headers: params?.additionalHeaders,\n });\n\n this.mediaService = new MediaService({ apiClient: this.apiClient });\n\n logger.debug(\"Initialized AntsPlatformSpanProcessor with params:\", {\n publicKey,\n baseUrl,\n environment: this.environment,\n release: this.release,\n timeoutSeconds,\n flushAt,\n flushIntervalSeconds,\n });\n }\n\n private get logger(): Logger {\n return getGlobalLogger();\n }\n\n /**\n * Called when a span is started. Adds environment and release attributes to the span.\n *\n * @param span - The span that was started\n * @param parentContext - The parent context\n *\n * @override\n */\n public onStart(span: Span, parentContext: any): void {\n span.setAttributes({\n [AntsPlatformOtelSpanAttributes.ENVIRONMENT]: this.environment,\n [AntsPlatformOtelSpanAttributes.RELEASE]: this.release,\n });\n\n return this.processor.onStart(span, parentContext);\n }\n\n /**\n * Called when a span ends. Processes the span for export to AntsPlatform.\n *\n * This method:\n * 1. Checks if the span should be exported using the shouldExportSpan function\n * 2. Applies data masking to sensitive attributes\n * 3. Handles media content extraction and upload\n * 4. Logs span details in debug mode\n * 5. Passes the span to the parent processor for export\n *\n * @param span - The span that ended\n *\n * @override\n */\n public onEnd(span: ReadableSpan): void {\n const processEndedSpanPromise = this.processEndedSpan(span).catch((err) => {\n this.logger.error(err);\n });\n\n // Enqueue this export to the pending list so it can be flushed by the user.\n this.pendingEndedSpans.add(processEndedSpanPromise);\n\n void processEndedSpanPromise.finally(() =>\n this.pendingEndedSpans.delete(processEndedSpanPromise),\n );\n }\n\n private async flush(): Promise<void> {\n await Promise.all(Array.from(this.pendingEndedSpans));\n await this.mediaService.flush();\n }\n\n /**\n * Forces an immediate flush of all pending spans and media uploads.\n *\n * @returns Promise that resolves when all pending operations are complete\n *\n * @override\n */\n public async forceFlush(): Promise<void> {\n await this.flush();\n\n return this.processor.forceFlush();\n }\n\n /**\n * Gracefully shuts down the processor, ensuring all pending operations are completed.\n *\n * @returns Promise that resolves when shutdown is complete\n *\n * @override\n */\n public async shutdown(): Promise<void> {\n await this.flush();\n\n return this.processor.shutdown();\n }\n\n private async processEndedSpan(span: ReadableSpan) {\n if (this.shouldExportSpan) {\n try {\n if (this.shouldExportSpan({ otelSpan: span }) === false) return;\n } catch (err) {\n this.logger.error(\n \"ShouldExportSpan failed with error. Excluding span. Error: \",\n err,\n );\n\n return;\n }\n }\n\n this.applyMaskInPlace(span);\n await this.mediaService.process(span);\n\n this.logger.debug(\n `Processed span:\\n${JSON.stringify(\n {\n name: span.name,\n traceId: span.spanContext().traceId,\n spanId: span.spanContext().spanId,\n parentSpanId: span.parentSpanContext?.spanId ?? null,\n attributes: span.attributes,\n startTime: new Date(hrTimeToMilliseconds(span.startTime)),\n endTime: new Date(hrTimeToMilliseconds(span.endTime)),\n durationMs: hrTimeToMilliseconds(span.duration),\n kind: span.kind,\n status: span.status,\n resource: span.resource.attributes,\n instrumentationScope: span.instrumentationScope,\n },\n null,\n 2,\n )}`,\n );\n\n this.processor.onEnd(span);\n }\n private applyMaskInPlace(span: ReadableSpan): void {\n const maskCandidates = [\n AntsPlatformOtelSpanAttributes.OBSERVATION_INPUT,\n AntsPlatformOtelSpanAttributes.TRACE_INPUT,\n AntsPlatformOtelSpanAttributes.OBSERVATION_OUTPUT,\n AntsPlatformOtelSpanAttributes.TRACE_OUTPUT,\n AntsPlatformOtelSpanAttributes.OBSERVATION_METADATA,\n AntsPlatformOtelSpanAttributes.TRACE_METADATA,\n ];\n\n for (const maskCandidate of maskCandidates) {\n if (maskCandidate in span.attributes) {\n span.attributes[maskCandidate] = this.applyMask(\n span.attributes[maskCandidate],\n );\n }\n }\n }\n\n private applyMask<T>(data: T): T | string {\n if (!this.mask) return data;\n\n try {\n return this.mask({ data });\n } catch (err) {\n this.logger.warn(\n `Applying mask function failed due to error, fully masking property. Error: ${err}`,\n );\n\n return \"<fully masked due to failed mask function>\";\n }\n }\n}\n","import {\n AntsPlatformAPIClient,\n AntsPlatformMedia,\n AntsPlatformOtelSpanAttributes,\n Logger,\n base64ToBytes,\n getGlobalLogger,\n} from \"@antsplatform/core\";\nimport { ReadableSpan } from \"@opentelemetry/sdk-trace-base\";\n\nexport class MediaService {\n private pendingMediaUploads: Set<Promise<void>> = new Set();\n private apiClient: AntsPlatformAPIClient;\n\n constructor(params: { apiClient: AntsPlatformAPIClient }) {\n this.apiClient = params.apiClient;\n }\n\n get logger(): Logger {\n return getGlobalLogger();\n }\n\n public async flush(): Promise<void> {\n await Promise.all(Array.from(this.pendingMediaUploads));\n }\n\n public async process(span: ReadableSpan) {\n const mediaAttributes = [\n AntsPlatformOtelSpanAttributes.OBSERVATION_INPUT,\n AntsPlatformOtelSpanAttributes.TRACE_INPUT,\n AntsPlatformOtelSpanAttributes.OBSERVATION_OUTPUT,\n AntsPlatformOtelSpanAttributes.TRACE_OUTPUT,\n AntsPlatformOtelSpanAttributes.OBSERVATION_METADATA,\n AntsPlatformOtelSpanAttributes.TRACE_METADATA,\n ];\n\n for (const mediaAttribute of mediaAttributes) {\n const mediaRelevantAttributeKeys = Object.keys(span.attributes).filter(\n (attributeName) => attributeName.startsWith(mediaAttribute),\n );\n\n for (const key of mediaRelevantAttributeKeys) {\n const value = span.attributes[key];\n\n if (typeof value !== \"string\") {\n this.logger.warn(\n `Span attribute ${mediaAttribute} is not a stringified object. Skipping media handling.`,\n );\n\n continue;\n }\n\n // Find media base64 data URI\n let mediaReplacedValue = value;\n const regex = /data:[^;]+;base64,[A-Za-z0-9+/]+=*/g;\n const foundMedia = [...new Set(value.match(regex) ?? [])];\n\n if (foundMedia.length === 0) continue;\n\n for (const mediaDataUri of foundMedia) {\n // For each media, create media tag and initiate upload\n const media = new AntsPlatformMedia({\n base64DataUri: mediaDataUri,\n source: \"base64_data_uri\",\n });\n\n const antsPlatformMediaTag = await media.getTag();\n\n if (!antsPlatformMediaTag) {\n this.logger.warn(\n \"Failed to create AntsPlatform media tag. Skipping media item.\",\n );\n\n continue;\n }\n\n this.scheduleUpload({\n span,\n media,\n field: mediaAttribute.includes(\"input\")\n ? \"input\"\n : mediaAttribute.includes(\"output\")\n ? \"output\"\n : \"metadata\", // todo: make more robust\n });\n\n // Replace original attribute with media escaped attribute\n mediaReplacedValue = mediaReplacedValue.replaceAll(\n mediaDataUri,\n antsPlatformMediaTag,\n );\n }\n\n span.attributes[key] = mediaReplacedValue;\n }\n }\n\n // Handle media from Vercel AI SDK\n if (span.instrumentationScope.name === \"ai\") {\n const aiSDKMediaAttributes = [\"ai.prompt.messages\", \"ai.prompt\"];\n\n for (const mediaAttribute of aiSDKMediaAttributes) {\n const value = span.attributes[mediaAttribute];\n\n if (!value || typeof value !== \"string\") {\n continue;\n }\n\n // Find media base64 data URI\n let mediaReplacedValue = value;\n\n try {\n const parsed = JSON.parse(value);\n\n if (Array.isArray(parsed)) {\n for (const message of parsed) {\n if (Array.isArray(message[\"content\"])) {\n const contentParts = message[\"content\"];\n\n for (const part of contentParts) {\n if (part[\"type\"] === \"file\") {\n let base64Content: string | null = null;\n // FilePart\n if (part[\"data\"] != null && part[\"mediaType\"] != null) {\n base64Content = part[\"data\"];\n }\n\n //ImagePart\n if (part[\"image\"] != null && part[\"mediaType\"] != null) {\n base64Content = part[\"image\"];\n }\n\n if (!base64Content) continue;\n\n const media = new AntsPlatformMedia({\n contentType: part[\"mediaType\"],\n contentBytes: base64ToBytes(base64Content),\n source: \"bytes\",\n });\n\n const antsPlatformMediaTag = await media.getTag();\n\n if (!antsPlatformMediaTag) {\n this.logger.warn(\n \"Failed to create AntsPlatform media tag. Skipping media item.\",\n );\n\n continue;\n }\n\n this.scheduleUpload({\n span,\n media,\n field: \"input\",\n });\n\n // Replace original attribute with media escaped attribute\n mediaReplacedValue = mediaReplacedValue.replaceAll(\n base64Content,\n antsPlatformMediaTag,\n );\n }\n }\n }\n }\n }\n\n span.attributes[mediaAttribute] = mediaReplacedValue;\n } catch (err) {\n this.logger.warn(\n `Failed to handle media for AI SDK attribute ${mediaAttribute} for span ${span.spanContext().spanId}`,\n err,\n );\n }\n }\n }\n }\n\n private scheduleUpload(params: {\n span: ReadableSpan;\n field: string;\n media: AntsPlatformMedia;\n }) {\n const { span, field, media } = params;\n\n const uploadPromise: Promise<void> = this.handleUpload({\n media,\n traceId: span.spanContext().traceId,\n observationId: span.spanContext().spanId,\n field,\n }).catch((err) => {\n this.logger.error(\"Media upload failed with error: \", err);\n });\n\n this.pendingMediaUploads.add(uploadPromise);\n\n uploadPromise.finally(() => {\n this.pendingMediaUploads.delete(uploadPromise);\n });\n }\n\n private async handleUpload({\n media,\n traceId,\n observationId,\n field,\n }: {\n media: AntsPlatformMedia;\n traceId: string;\n observationId?: string;\n field: string;\n }): Promise<void> {\n try {\n const contentSha256Hash = await media.getSha256Hash();\n\n if (\n !media.contentLength ||\n !media._contentType ||\n !contentSha256Hash ||\n !media._contentBytes\n ) {\n return;\n }\n\n const { uploadUrl, mediaId } = await this.apiClient.media.getUploadUrl({\n contentLength: media.contentLength,\n traceId,\n observationId,\n field,\n contentType: media._contentType,\n sha256Hash: contentSha256Hash,\n });\n\n if (!uploadUrl) {\n this.logger.debug(\n `Media status: Media with ID ${mediaId} already uploaded. Skipping duplicate upload.`,\n );\n\n return;\n }\n\n const clientSideMediaId = await media.getId();\n if (clientSideMediaId !== mediaId) {\n this.logger.error(\n `Media integrity error: Media ID mismatch between SDK (${clientSideMediaId}) and Server (${mediaId}). Upload cancelled. Please check media ID generation logic.`,\n );\n\n return;\n }\n\n this.logger.debug(`Uploading media ${mediaId}...`);\n\n const startTime = Date.now();\n\n const uploadResponse = await this.uploadWithBackoff({\n uploadUrl,\n contentBytes: media._contentBytes,\n contentType: media._contentType,\n contentSha256Hash: contentSha256Hash,\n maxRetries: 3,\n baseDelay: 1000,\n });\n\n if (!uploadResponse) {\n throw Error(\"Media upload process failed\");\n }\n\n await this.apiClient.media.patch(mediaId, {\n uploadedAt: new Date().toISOString(),\n uploadHttpStatus: uploadResponse.status,\n uploadHttpError: await uploadResponse.text(),\n uploadTimeMs: Date.now() - startTime,\n });\n\n this.logger.debug(`Media upload status reported for ${mediaId}`);\n } catch (err) {\n this.logger.error(`Error processing media item: ${err}`);\n }\n }\n\n private async uploadWithBackoff(params: {\n uploadUrl: string;\n contentType: string;\n contentSha256Hash: string;\n contentBytes: Uint8Array;\n maxRetries: number;\n baseDelay: number;\n }) {\n const {\n uploadUrl,\n contentType,\n contentSha256Hash,\n contentBytes,\n maxRetries,\n baseDelay,\n } = params;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const uploadResponse = await fetch(uploadUrl, {\n method: \"PUT\",\n body: contentBytes,\n headers: {\n \"Content-Type\": contentType,\n \"x-amz-checksum-sha256\": contentSha256Hash,\n \"x-ms-blob-type\": \"BlockBlob\",\n },\n });\n\n if (\n attempt < maxRetries &&\n uploadResponse.status !== 200 &&\n uploadResponse.status !== 201\n ) {\n throw new Error(`Upload failed with status ${uploadResponse.status}`);\n }\n\n return uploadResponse;\n } catch (e) {\n if (attempt === maxRetries) {\n throw e;\n }\n\n const delay = baseDelay * Math.pow(2, attempt);\n const jitter = Math.random() * 1000;\n\n await new Promise((resolve) => setTimeout(resolve, delay + jitter));\n }\n }\n }\n}\n"],"mappings":"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","names":["import_core"]} | ||
| {"version":3,"sources":["../src/index.ts","../src/span-processor.ts","../src/MediaService.ts"],"sourcesContent":["export * from \"./span-processor.js\";\r\n","import {\r\n Logger,\r\n getGlobalLogger,\r\n AntsPlatformAPIClient,\r\n ANTS_PLATFORM_SDK_VERSION,\r\n AntsPlatformOtelSpanAttributes,\r\n getEnv,\r\n base64Encode,\r\n} from \"@antsplatform/core\";\r\nimport { blake2b } from \"@noble/hashes/blake2.js\";\r\nimport { bytesToHex } from \"@noble/hashes/utils.js\";\r\nimport { hrTimeToMilliseconds } from \"@opentelemetry/core\";\r\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\r\nimport {\r\n Span,\r\n BatchSpanProcessor,\r\n SimpleSpanProcessor,\r\n SpanExporter,\r\n ReadableSpan,\r\n SpanProcessor,\r\n} from \"@opentelemetry/sdk-trace-base\";\r\n\r\nimport { MediaService } from \"./MediaService.js\";\r\n\r\n/**\r\n * Configuration for agent identification in Ants Platform.\r\n *\r\n * These parameters are used to identify and track agents in the AI Command Center.\r\n * The project_id is automatically fetched from the API using your credentials,\r\n * matching the behavior of the Python and Java SDKs.\r\n *\r\n * @public\r\n */\r\nexport interface AgentConfig {\r\n /**\r\n * Agent identifier (stable, required, **cannot change**).\r\n * This is used as part of the deterministic agent_id generation.\r\n * Once set, this should never be changed as it will affect the generated agent_id.\r\n *\r\n * @example \"qa_agent\", \"customer_support_bot\", \"data_processor\"\r\n */\r\n agentName: string;\r\n\r\n /**\r\n * Human-readable display name (optional, mutable).\r\n * This can be changed via the updateAgentDisplayName API without affecting the agent_id.\r\n *\r\n * @example \"QA Agent - Production\", \"Customer Support Bot v2\"\r\n */\r\n agentDisplayName?: string;\r\n}\r\n\r\n/**\r\n * Resolved agent configuration with computed agent_id.\r\n *\r\n * @internal\r\n */\r\ninterface ResolvedAgentConfig {\r\n agentId: string;\r\n agentName: string;\r\n agentDisplayName?: string;\r\n projectId: string;\r\n}\r\n\r\n/**\r\n * Maximum length for agent names.\r\n */\r\nconst MAX_AGENT_NAME_LENGTH = 255;\r\n\r\n/**\r\n * Generates a deterministic agent_id using BLAKE2b-64.\r\n *\r\n * Formula: `agent_id = BLAKE2b-64(agent_name + project_id)` = 16-character hex string\r\n *\r\n * Design Decision: Include projectId in hash for transfer safety.\r\n * When projects transfer between organizations, projectId stays constant,\r\n * so agent_id remains stable across transfers.\r\n *\r\n * @param agentName - Agent name (immutable identifier)\r\n * @param projectId - Project ID\r\n * @returns 16-character hex string (64 bits)\r\n * @internal\r\n */\r\nfunction generateAgentId(agentName: string, projectId: string): string {\r\n const logger = getGlobalLogger();\r\n\r\n // Validate inputs\r\n if (!agentName || typeof agentName !== \"string\") {\r\n throw new Error(\"agentName must be a non-empty string\");\r\n }\r\n if (!projectId || typeof projectId !== \"string\") {\r\n throw new Error(\"projectId must be a non-empty string\");\r\n }\r\n\r\n // Truncate if too long\r\n let name = agentName.trim();\r\n if (name.length > MAX_AGENT_NAME_LENGTH) {\r\n logger.warn(\r\n `agentName too long (${name.length} chars), truncated to ${MAX_AGENT_NAME_LENGTH} characters`,\r\n );\r\n name = name.slice(0, MAX_AGENT_NAME_LENGTH);\r\n }\r\n\r\n // Generate BLAKE2b-64 hash (8 bytes = 16 hex chars)\r\n const agentId = blake2b64(name, projectId.trim());\r\n\r\n logger.debug(`[AGENT_ID] Generated: ${agentId} from agent_name: ${name}`);\r\n\r\n return agentId;\r\n}\r\n\r\n/**\r\n * BLAKE2b-64 hash implementation (8 bytes = 16 hex characters).\r\n *\r\n * Uses @noble/hashes for true BLAKE2b with configurable digest size.\r\n * This matches Python's hashlib.blake2b(digest_size=8) exactly.\r\n *\r\n * @param agentName - First input to hash\r\n * @param projectId - Second input to hash\r\n * @returns 16-character hex string\r\n * @internal\r\n */\r\nfunction blake2b64(agentName: string, projectId: string): string {\r\n // Combine inputs as in Python SDK: hasher.update(agent_name); hasher.update(project_id)\r\n const combined = new TextEncoder().encode(agentName + projectId);\r\n\r\n // BLAKE2b with 8-byte (64-bit) output - matches Python's digest_size=8\r\n const hash = blake2b(combined, { dkLen: 8 });\r\n\r\n return bytesToHex(hash);\r\n}\r\n\r\n/**\r\n * Validates and resolves agent configuration with a known projectId.\r\n *\r\n * @param config - Agent configuration to validate and resolve\r\n * @param projectId - The project ID to use (either from config or auto-fetched)\r\n * @returns Resolved agent configuration with computed agent_id\r\n * @throws {Error} If configuration is invalid\r\n * @internal\r\n */\r\nfunction resolveAgentConfigWithProjectId(\r\n config: AgentConfig,\r\n projectId: string,\r\n): ResolvedAgentConfig {\r\n if (!projectId || !projectId.trim()) {\r\n throw new Error(\"projectId is required\");\r\n }\r\n\r\n if (!config.agentName || !config.agentName.trim()) {\r\n throw new Error(\"agentName is required\");\r\n }\r\n\r\n // Generate deterministic agent_id using BLAKE2b-64\r\n const agentId = generateAgentId(\r\n config.agentName.trim(),\r\n projectId.trim(),\r\n );\r\n\r\n return {\r\n agentId,\r\n agentName: config.agentName.trim(),\r\n agentDisplayName: config.agentDisplayName?.trim(),\r\n projectId: projectId.trim(),\r\n };\r\n}\r\n\r\n/**\r\n * Function type for masking sensitive data in spans before export.\r\n *\r\n * @param params - Object containing the data to be masked\r\n * @param params.data - The data that should be masked\r\n * @returns The masked data (can be of any type)\r\n *\r\n * @example\r\n * ```typescript\r\n * const maskFunction: MaskFunction = ({ data }) => {\r\n * if (typeof data === 'string') {\r\n * return data.replace(/password=\\w+/g, 'password=***');\r\n * }\r\n * return data;\r\n * };\r\n * ```\r\n *\r\n * @public\r\n */\r\nexport type MaskFunction = (params: { data: any }) => any;\r\n\r\n/**\r\n * Function type for determining whether a span should be exported to AntsPlatform.\r\n *\r\n * @param params - Object containing the span to evaluate\r\n * @param params.otelSpan - The OpenTelemetry span to evaluate\r\n * @returns `true` if the span should be exported, `false` otherwise\r\n *\r\n * @example\r\n * ```typescript\r\n * const shouldExportSpan: ShouldExportSpan = ({ otelSpan }) => {\r\n * // Only export spans that took longer than 100ms\r\n * return otelSpan.duration[0] * 1000 + otelSpan.duration[1] / 1000000 > 100;\r\n * };\r\n * ```\r\n *\r\n * @public\r\n */\r\nexport type ShouldExportSpan = (params: { otelSpan: ReadableSpan }) => boolean;\r\n\r\n/**\r\n * Configuration parameters for the AntsPlatformSpanProcessor.\r\n *\r\n * @public\r\n */\r\nexport interface AntsPlatformSpanProcessorParams {\r\n /**\r\n * Custom OpenTelemetry span exporter. If not provided, a default OTLP exporter will be used.\r\n */\r\n exporter?: SpanExporter;\r\n\r\n /**\r\n * Ants Platform public API key. Can also be set via ANTS_PLATFORM_PUBLIC_KEY environment variable.\r\n */\r\n publicKey?: string;\r\n\r\n /**\r\n * Ants Platform secret API key. Can also be set via ANTS_PLATFORM_SECRET_KEY environment variable.\r\n */\r\n secretKey?: string;\r\n\r\n /**\r\n * AntsPlatform instance base URL. Can also be set via ANTS_PLATFORM_BASE_URL environment variable.\r\n * @defaultValue \"https://api.ants-platform.com\"\r\n */\r\n baseUrl?: string;\r\n\r\n /**\r\n * Number of spans to batch before flushing. Can also be set via ANTS_PLATFORM_FLUSH_AT environment variable.\r\n */\r\n flushAt?: number;\r\n\r\n /**\r\n * Flush interval in seconds. Can also be set via ANTS_PLATFORM_FLUSH_INTERVAL environment variable.\r\n */\r\n flushInterval?: number;\r\n\r\n /**\r\n * Function to mask sensitive data in spans before export.\r\n */\r\n mask?: MaskFunction;\r\n\r\n /**\r\n * Function to determine whether a span should be exported to AntsPlatform.\r\n */\r\n shouldExportSpan?: ShouldExportSpan;\r\n\r\n /**\r\n * Environment identifier for the traces. Can also be set via ANTS_PLATFORM_TRACING_ENVIRONMENT environment variable.\r\n */\r\n environment?: string;\r\n\r\n /**\r\n * Release identifier for the traces. Can also be set via ANTS_PLATFORM_RELEASE environment variable.\r\n */\r\n release?: string;\r\n\r\n /**\r\n * Request timeout in seconds. Can also be set via ANTS_PLATFORM_TIMEOUT environment variable.\r\n * @defaultValue 5\r\n */\r\n timeout?: number;\r\n\r\n /**\r\n * Additional HTTP headers to include with requests.\r\n */\r\n additionalHeaders?: Record<string, string>;\r\n\r\n /**\r\n * Span export mode to use.\r\n *\r\n * - **batched**: Recommended for production environments with long-running processes.\r\n * Spans are batched and exported in groups for optimal performance.\r\n * - **immediate**: Recommended for short-lived environments such as serverless functions.\r\n * Spans are exported immediately to prevent data loss when the process terminates / is frozen.\r\n *\r\n * @defaultValue \"batched\"\r\n */\r\n exportMode?: \"immediate\" | \"batched\";\r\n\r\n /**\r\n * Agent configuration for AI Command Center integration.\r\n *\r\n * When provided, all spans will be automatically tagged with agent identifiers,\r\n * enabling agent-level tracking and analytics in the AI Command Center.\r\n *\r\n * The project_id is automatically fetched from the API using your credentials,\r\n * matching the behavior of the Python SDK.\r\n *\r\n * @example\r\n * ```typescript\r\n * new AntsPlatformSpanProcessor({\r\n * publicKey: 'pk_...',\r\n * secretKey: 'sk_...',\r\n * agent: {\r\n * agentName: 'qa_agent',\r\n * agentDisplayName: 'QA Agent - Production' // optional\r\n * }\r\n * });\r\n * ```\r\n */\r\n agent?: AgentConfig;\r\n\r\n /**\r\n * **Internal/Testing Only**: Override project ID for testing purposes.\r\n * When set, bypasses the API fetch and uses this value directly.\r\n * This should NOT be used in production code.\r\n *\r\n * @internal\r\n */\r\n _testProjectId?: string;\r\n}\r\n\r\n/**\r\n * OpenTelemetry span processor for sending spans to AntsPlatform.\r\n *\r\n * This processor extends the standard BatchSpanProcessor to provide:\r\n * - Automatic batching and flushing of spans to AntsPlatform\r\n * - Media content extraction and upload from base64 data URIs\r\n * - Data masking capabilities for sensitive information\r\n * - Conditional span export based on custom logic\r\n * - Environment and release tagging\r\n *\r\n * @example\r\n * ```typescript\r\n * import { NodeSDK } from '@opentelemetry/sdk-node';\r\n * import { AntsPlatformSpanProcessor } from '@antsplatform/otel';\r\n *\r\n * const sdk = new NodeSDK({\r\n * spanProcessors: [\r\n * new AntsPlatformSpanProcessor({\r\n * publicKey: 'pk_...',\r\n * secretKey: 'sk_...',\r\n * baseUrl: 'https://api.ants-platform.com',\r\n * environment: 'production',\r\n * mask: ({ data }) => {\r\n * // Mask sensitive data\r\n * return data.replace(/api_key=\\w+/g, 'api_key=***');\r\n * }\r\n * })\r\n * ]\r\n * });\r\n *\r\n * sdk.start();\r\n * ```\r\n *\r\n * @public\r\n */\r\nexport class AntsPlatformSpanProcessor implements SpanProcessor {\r\n private pendingEndedSpans: Set<Promise<void>> = new Set();\r\n\r\n private publicKey?: string;\r\n private baseUrl?: string;\r\n private environment?: string;\r\n private release?: string;\r\n private mask?: MaskFunction;\r\n private shouldExportSpan?: ShouldExportSpan;\r\n private apiClient: AntsPlatformAPIClient;\r\n private processor: SpanProcessor;\r\n private mediaService: MediaService;\r\n private resolvedAgentConfig?: ResolvedAgentConfig;\r\n private pendingAgentConfig?: AgentConfig;\r\n private agentConfigPromise?: Promise<void>;\r\n private cachedProjectId?: string;\r\n private testProjectId?: string;\r\n\r\n /**\r\n * Creates a new AntsPlatformSpanProcessor instance.\r\n *\r\n * @param params - Configuration parameters for the processor\r\n *\r\n * @example\r\n * ```typescript\r\n * const processor = new AntsPlatformSpanProcessor({\r\n * publicKey: 'pk_...',\r\n * secretKey: 'sk_...',\r\n * environment: 'staging',\r\n * flushAt: 10,\r\n * flushInterval: 2,\r\n * mask: ({ data }) => {\r\n * // Custom masking logic\r\n * return typeof data === 'string'\r\n * ? data.replace(/secret_\\w+/g, 'secret_***')\r\n * : data;\r\n * },\r\n * shouldExportSpan: ({ otelSpan }) => {\r\n * // Only export spans from specific services\r\n * return otelSpan.name.startsWith('my-service');\r\n * }\r\n * });\r\n * ```\r\n */\r\n constructor(params?: AntsPlatformSpanProcessorParams) {\r\n const logger = getGlobalLogger();\r\n\r\n const publicKey = params?.publicKey ?? getEnv(\"ANTS_PLATFORM_PUBLIC_KEY\");\r\n const secretKey = params?.secretKey ?? getEnv(\"ANTS_PLATFORM_SECRET_KEY\");\r\n const baseUrl =\r\n params?.baseUrl ??\r\n getEnv(\"ANTS_PLATFORM_BASE_URL\") ??\r\n getEnv(\"ANTS_PLATFORM_BASEURL\") ?? // legacy v2\r\n \"https://api.ants-platform.com\";\r\n\r\n if (!params?.exporter && !publicKey) {\r\n logger.warn(\r\n \"No exporter configured and no public key provided in constructor or as ANTS_PLATFORM_PUBLIC_KEY env var. Span exports will fail.\",\r\n );\r\n }\r\n if (!params?.exporter && !secretKey) {\r\n logger.warn(\r\n \"No exporter configured and no secret key provided in constructor or as ANTS_PLATFORM_SECRET_KEY env var. Span exports will fail.\",\r\n );\r\n }\r\n const flushAt = params?.flushAt ?? getEnv(\"ANTS_PLATFORM_FLUSH_AT\");\r\n const flushIntervalSeconds =\r\n params?.flushInterval ?? getEnv(\"ANTS_PLATFORM_FLUSH_INTERVAL\");\r\n\r\n const authHeaderValue = base64Encode(`${publicKey}:${secretKey}`);\r\n const timeoutSeconds =\r\n params?.timeout ?? Number(getEnv(\"ANTS_PLATFORM_TIMEOUT\") ?? 5);\r\n\r\n const exporter =\r\n params?.exporter ??\r\n new OTLPTraceExporter({\r\n url: `${baseUrl}/api/public/otel/v1/traces`,\r\n headers: {\r\n Authorization: `Basic ${authHeaderValue}`,\r\n x_antsPlatform_sdk_name: \"javascript\",\r\n x_antsPlatform_sdk_version: ANTS_PLATFORM_SDK_VERSION,\r\n x_antsPlatform_public_key: publicKey ?? \"<missing>\",\r\n ...params?.additionalHeaders,\r\n },\r\n timeoutMillis: timeoutSeconds * 1_000,\r\n });\r\n\r\n this.processor =\r\n params?.exportMode === \"immediate\"\r\n ? new SimpleSpanProcessor(exporter)\r\n : new BatchSpanProcessor(exporter, {\r\n maxExportBatchSize: flushAt ? Number(flushAt) : undefined,\r\n scheduledDelayMillis: flushIntervalSeconds\r\n ? Number(flushIntervalSeconds) * 1_000\r\n : undefined,\r\n });\r\n\r\n this.publicKey = publicKey;\r\n this.baseUrl = baseUrl;\r\n this.environment =\r\n params?.environment ?? getEnv(\"ANTS_PLATFORM_TRACING_ENVIRONMENT\");\r\n this.release = params?.release ?? getEnv(\"ANTS_PLATFORM_RELEASE\");\r\n this.mask = params?.mask;\r\n this.shouldExportSpan = params?.shouldExportSpan;\r\n this.apiClient = new AntsPlatformAPIClient({\r\n baseUrl: this.baseUrl,\r\n username: this.publicKey,\r\n password: secretKey,\r\n xAntsPlatformPublicKey: this.publicKey,\r\n xAntsPlatformSdkVersion: ANTS_PLATFORM_SDK_VERSION,\r\n xAntsPlatformSdkName: \"javascript\",\r\n environment: \"\", // noop as baseUrl is set\r\n headers: params?.additionalHeaders,\r\n });\r\n\r\n this.mediaService = new MediaService({ apiClient: this.apiClient });\r\n\r\n // Store test project ID if provided (for testing only)\r\n this.testProjectId = params?._testProjectId;\r\n\r\n // If agent config is provided, initiate async projectId fetch\r\n if (params?.agent) {\r\n this.pendingAgentConfig = params.agent;\r\n this.agentConfigPromise = this.initializeAgentConfig(params.agent);\r\n }\r\n\r\n logger.debug(\"Initialized AntsPlatformSpanProcessor with params:\", {\r\n publicKey,\r\n baseUrl,\r\n environment: this.environment,\r\n release: this.release,\r\n timeoutSeconds,\r\n flushAt,\r\n flushIntervalSeconds,\r\n hasAgentConfig: !!params?.agent,\r\n });\r\n }\r\n\r\n /**\r\n * Fetches project_id from the API and resolves agent configuration.\r\n * This matches the Java/Python SDK behavior where project_id is always fetched from the API.\r\n *\r\n * @param config - Agent configuration with agentName and optional agentDisplayName\r\n * @internal\r\n */\r\n private async initializeAgentConfig(config: AgentConfig): Promise<void> {\r\n const logger = getGlobalLogger();\r\n\r\n try {\r\n // Use test project ID if provided (for testing only), otherwise fetch from API\r\n let projectId: string | null = null;\r\n\r\n if (this.testProjectId) {\r\n logger.debug(`[AGENT_CONFIG] Using test projectId: ${this.testProjectId}`);\r\n projectId = this.testProjectId;\r\n } else {\r\n // Always fetch project_id from API (matches Java/Python SDK behavior)\r\n projectId = await this.fetchProjectId();\r\n }\r\n\r\n if (!projectId) {\r\n logger.error(\r\n \"[AGENT_CONFIG] Failed to fetch project_id from API. Agent attributes will not be added to spans.\",\r\n );\r\n return;\r\n }\r\n\r\n logger.debug(`[AGENT_CONFIG] Using projectId: ${projectId}`);\r\n\r\n // Resolve agent config with the fetched projectId\r\n this.resolvedAgentConfig = resolveAgentConfigWithProjectId(config, projectId);\r\n this.cachedProjectId = projectId;\r\n\r\n logger.info(\"[AGENT_CONFIG] Successfully initialized agent configuration:\", {\r\n agentId: this.resolvedAgentConfig.agentId,\r\n agentName: this.resolvedAgentConfig.agentName,\r\n agentDisplayName: this.resolvedAgentConfig.agentDisplayName,\r\n projectId: this.resolvedAgentConfig.projectId,\r\n });\r\n } catch (error) {\r\n logger.error(\r\n `[AGENT_CONFIG] Failed to initialize agent configuration: ${error instanceof Error ? error.message : String(error)}`,\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Fetches the project_id from the Ants Platform API.\r\n * This matches the Python SDK behavior: GET /api/public/projects -> data[0].id\r\n *\r\n * @returns Promise resolving to the project ID, or null if unavailable\r\n * @internal\r\n */\r\n private async fetchProjectId(): Promise<string | null> {\r\n const logger = getGlobalLogger();\r\n\r\n try {\r\n const response = await this.apiClient.projects.get();\r\n\r\n if (response.data && response.data.length > 0) {\r\n const projectId = response.data[0].id;\r\n logger.debug(`[PROJECT_ID] Fetched from API: ${projectId}`);\r\n return projectId;\r\n }\r\n\r\n logger.warn(\"[PROJECT_ID] No projects found in API response\");\r\n return null;\r\n } catch (error) {\r\n logger.warn(\r\n `[PROJECT_ID] Failed to fetch from API: ${error instanceof Error ? error.message : String(error)}`,\r\n );\r\n return null;\r\n }\r\n }\r\n\r\n private get logger(): Logger {\r\n return getGlobalLogger();\r\n }\r\n\r\n /**\r\n * Called when a span is started. Adds environment, release, and agent attributes to the span.\r\n *\r\n * @param span - The span that was started\r\n * @param parentContext - The parent context\r\n *\r\n * @override\r\n */\r\n public onStart(span: Span, parentContext: any): void {\r\n const attributes: Record<string, string | undefined> = {\r\n [AntsPlatformOtelSpanAttributes.ENVIRONMENT]: this.environment,\r\n [AntsPlatformOtelSpanAttributes.RELEASE]: this.release,\r\n };\r\n\r\n // Add agent attributes if configured and resolved\r\n if (this.resolvedAgentConfig) {\r\n attributes[AntsPlatformOtelSpanAttributes.AGENT_ID] =\r\n this.resolvedAgentConfig.agentId;\r\n attributes[AntsPlatformOtelSpanAttributes.AGENT_NAME] =\r\n this.resolvedAgentConfig.agentName;\r\n attributes[AntsPlatformOtelSpanAttributes.AGENT_DISPLAY_NAME] =\r\n this.resolvedAgentConfig.agentDisplayName;\r\n attributes[AntsPlatformOtelSpanAttributes.PROJECT_ID] =\r\n this.resolvedAgentConfig.projectId;\r\n } else if (this.pendingAgentConfig) {\r\n // Agent config was requested but not yet resolved (projectId still being fetched)\r\n this.logger.debug(\r\n \"[AGENT_CONFIG] Agent config not yet resolved, span will not have agent attributes. \" +\r\n \"This may happen for spans created before projectId fetch completes.\",\r\n );\r\n }\r\n\r\n span.setAttributes(attributes);\r\n\r\n return this.processor.onStart(span, parentContext);\r\n }\r\n\r\n /**\r\n * Called when a span ends. Processes the span for export to AntsPlatform.\r\n *\r\n * This method:\r\n * 1. Checks if the span should be exported using the shouldExportSpan function\r\n * 2. Applies data masking to sensitive attributes\r\n * 3. Handles media content extraction and upload\r\n * 4. Logs span details in debug mode\r\n * 5. Passes the span to the parent processor for export\r\n *\r\n * @param span - The span that ended\r\n *\r\n * @override\r\n */\r\n public onEnd(span: ReadableSpan): void {\r\n const processEndedSpanPromise = this.processEndedSpan(span).catch((err) => {\r\n this.logger.error(err);\r\n });\r\n\r\n // Enqueue this export to the pending list so it can be flushed by the user.\r\n this.pendingEndedSpans.add(processEndedSpanPromise);\r\n\r\n void processEndedSpanPromise.finally(() =>\r\n this.pendingEndedSpans.delete(processEndedSpanPromise),\r\n );\r\n }\r\n\r\n private async flush(): Promise<void> {\r\n // Wait for agent config to be resolved if pending\r\n if (this.agentConfigPromise) {\r\n await this.agentConfigPromise;\r\n }\r\n await Promise.all(Array.from(this.pendingEndedSpans));\r\n await this.mediaService.flush();\r\n }\r\n\r\n /**\r\n * Forces an immediate flush of all pending spans and media uploads.\r\n *\r\n * @returns Promise that resolves when all pending operations are complete\r\n *\r\n * @override\r\n */\r\n public async forceFlush(): Promise<void> {\r\n await this.flush();\r\n\r\n return this.processor.forceFlush();\r\n }\r\n\r\n /**\r\n * Gracefully shuts down the processor, ensuring all pending operations are completed.\r\n *\r\n * @returns Promise that resolves when shutdown is complete\r\n *\r\n * @override\r\n */\r\n public async shutdown(): Promise<void> {\r\n await this.flush();\r\n\r\n return this.processor.shutdown();\r\n }\r\n\r\n private async processEndedSpan(span: ReadableSpan) {\r\n if (this.shouldExportSpan) {\r\n try {\r\n if (this.shouldExportSpan({ otelSpan: span }) === false) return;\r\n } catch (err) {\r\n this.logger.error(\r\n \"ShouldExportSpan failed with error. Excluding span. Error: \",\r\n err,\r\n );\r\n\r\n return;\r\n }\r\n }\r\n\r\n this.applyMaskInPlace(span);\r\n await this.mediaService.process(span);\r\n\r\n this.logger.debug(\r\n `Processed span:\\n${JSON.stringify(\r\n {\r\n name: span.name,\r\n traceId: span.spanContext().traceId,\r\n spanId: span.spanContext().spanId,\r\n parentSpanId: span.parentSpanContext?.spanId ?? null,\r\n attributes: span.attributes,\r\n startTime: new Date(hrTimeToMilliseconds(span.startTime)),\r\n endTime: new Date(hrTimeToMilliseconds(span.endTime)),\r\n durationMs: hrTimeToMilliseconds(span.duration),\r\n kind: span.kind,\r\n status: span.status,\r\n resource: span.resource.attributes,\r\n instrumentationScope: span.instrumentationScope,\r\n },\r\n null,\r\n 2,\r\n )}`,\r\n );\r\n\r\n this.processor.onEnd(span);\r\n }\r\n private applyMaskInPlace(span: ReadableSpan): void {\r\n const maskCandidates = [\r\n AntsPlatformOtelSpanAttributes.OBSERVATION_INPUT,\r\n AntsPlatformOtelSpanAttributes.TRACE_INPUT,\r\n AntsPlatformOtelSpanAttributes.OBSERVATION_OUTPUT,\r\n AntsPlatformOtelSpanAttributes.TRACE_OUTPUT,\r\n AntsPlatformOtelSpanAttributes.OBSERVATION_METADATA,\r\n AntsPlatformOtelSpanAttributes.TRACE_METADATA,\r\n ];\r\n\r\n for (const maskCandidate of maskCandidates) {\r\n if (maskCandidate in span.attributes) {\r\n span.attributes[maskCandidate] = this.applyMask(\r\n span.attributes[maskCandidate],\r\n );\r\n }\r\n }\r\n }\r\n\r\n private applyMask<T>(data: T): T | string {\r\n if (!this.mask) return data;\r\n\r\n try {\r\n return this.mask({ data });\r\n } catch (err) {\r\n this.logger.warn(\r\n `Applying mask function failed due to error, fully masking property. Error: ${err}`,\r\n );\r\n\r\n return \"<fully masked due to failed mask function>\";\r\n }\r\n }\r\n}\r\n","import {\r\n AntsPlatformAPIClient,\r\n AntsPlatformMedia,\r\n AntsPlatformOtelSpanAttributes,\r\n Logger,\r\n base64ToBytes,\r\n getGlobalLogger,\r\n} from \"@antsplatform/core\";\r\nimport { ReadableSpan } from \"@opentelemetry/sdk-trace-base\";\r\n\r\nexport class MediaService {\r\n private pendingMediaUploads: Set<Promise<void>> = new Set();\r\n private apiClient: AntsPlatformAPIClient;\r\n\r\n constructor(params: { apiClient: AntsPlatformAPIClient }) {\r\n this.apiClient = params.apiClient;\r\n }\r\n\r\n get logger(): Logger {\r\n return getGlobalLogger();\r\n }\r\n\r\n public async flush(): Promise<void> {\r\n await Promise.all(Array.from(this.pendingMediaUploads));\r\n }\r\n\r\n public async process(span: ReadableSpan) {\r\n const mediaAttributes = [\r\n AntsPlatformOtelSpanAttributes.OBSERVATION_INPUT,\r\n AntsPlatformOtelSpanAttributes.TRACE_INPUT,\r\n AntsPlatformOtelSpanAttributes.OBSERVATION_OUTPUT,\r\n AntsPlatformOtelSpanAttributes.TRACE_OUTPUT,\r\n AntsPlatformOtelSpanAttributes.OBSERVATION_METADATA,\r\n AntsPlatformOtelSpanAttributes.TRACE_METADATA,\r\n ];\r\n\r\n for (const mediaAttribute of mediaAttributes) {\r\n const mediaRelevantAttributeKeys = Object.keys(span.attributes).filter(\r\n (attributeName) => attributeName.startsWith(mediaAttribute),\r\n );\r\n\r\n for (const key of mediaRelevantAttributeKeys) {\r\n const value = span.attributes[key];\r\n\r\n if (typeof value !== \"string\") {\r\n this.logger.warn(\r\n `Span attribute ${mediaAttribute} is not a stringified object. Skipping media handling.`,\r\n );\r\n\r\n continue;\r\n }\r\n\r\n // Find media base64 data URI\r\n let mediaReplacedValue = value;\r\n const regex = /data:[^;]+;base64,[A-Za-z0-9+/]+=*/g;\r\n const foundMedia = [...new Set(value.match(regex) ?? [])];\r\n\r\n if (foundMedia.length === 0) continue;\r\n\r\n for (const mediaDataUri of foundMedia) {\r\n // For each media, create media tag and initiate upload\r\n const media = new AntsPlatformMedia({\r\n base64DataUri: mediaDataUri,\r\n source: \"base64_data_uri\",\r\n });\r\n\r\n const antsPlatformMediaTag = await media.getTag();\r\n\r\n if (!antsPlatformMediaTag) {\r\n this.logger.warn(\r\n \"Failed to create AntsPlatform media tag. Skipping media item.\",\r\n );\r\n\r\n continue;\r\n }\r\n\r\n this.scheduleUpload({\r\n span,\r\n media,\r\n field: mediaAttribute.includes(\"input\")\r\n ? \"input\"\r\n : mediaAttribute.includes(\"output\")\r\n ? \"output\"\r\n : \"metadata\", // todo: make more robust\r\n });\r\n\r\n // Replace original attribute with media escaped attribute\r\n mediaReplacedValue = mediaReplacedValue.replaceAll(\r\n mediaDataUri,\r\n antsPlatformMediaTag,\r\n );\r\n }\r\n\r\n span.attributes[key] = mediaReplacedValue;\r\n }\r\n }\r\n\r\n // Handle media from Vercel AI SDK\r\n if (span.instrumentationScope.name === \"ai\") {\r\n const aiSDKMediaAttributes = [\"ai.prompt.messages\", \"ai.prompt\"];\r\n\r\n for (const mediaAttribute of aiSDKMediaAttributes) {\r\n const value = span.attributes[mediaAttribute];\r\n\r\n if (!value || typeof value !== \"string\") {\r\n continue;\r\n }\r\n\r\n // Find media base64 data URI\r\n let mediaReplacedValue = value;\r\n\r\n try {\r\n const parsed = JSON.parse(value);\r\n\r\n if (Array.isArray(parsed)) {\r\n for (const message of parsed) {\r\n if (Array.isArray(message[\"content\"])) {\r\n const contentParts = message[\"content\"];\r\n\r\n for (const part of contentParts) {\r\n if (part[\"type\"] === \"file\") {\r\n let base64Content: string | null = null;\r\n // FilePart\r\n if (part[\"data\"] != null && part[\"mediaType\"] != null) {\r\n base64Content = part[\"data\"];\r\n }\r\n\r\n //ImagePart\r\n if (part[\"image\"] != null && part[\"mediaType\"] != null) {\r\n base64Content = part[\"image\"];\r\n }\r\n\r\n if (!base64Content) continue;\r\n\r\n const media = new AntsPlatformMedia({\r\n contentType: part[\"mediaType\"],\r\n contentBytes: base64ToBytes(base64Content),\r\n source: \"bytes\",\r\n });\r\n\r\n const antsPlatformMediaTag = await media.getTag();\r\n\r\n if (!antsPlatformMediaTag) {\r\n this.logger.warn(\r\n \"Failed to create AntsPlatform media tag. Skipping media item.\",\r\n );\r\n\r\n continue;\r\n }\r\n\r\n this.scheduleUpload({\r\n span,\r\n media,\r\n field: \"input\",\r\n });\r\n\r\n // Replace original attribute with media escaped attribute\r\n mediaReplacedValue = mediaReplacedValue.replaceAll(\r\n base64Content,\r\n antsPlatformMediaTag,\r\n );\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n span.attributes[mediaAttribute] = mediaReplacedValue;\r\n } catch (err) {\r\n this.logger.warn(\r\n `Failed to handle media for AI SDK attribute ${mediaAttribute} for span ${span.spanContext().spanId}`,\r\n err,\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n private scheduleUpload(params: {\r\n span: ReadableSpan;\r\n field: string;\r\n media: AntsPlatformMedia;\r\n }) {\r\n const { span, field, media } = params;\r\n\r\n const uploadPromise: Promise<void> = this.handleUpload({\r\n media,\r\n traceId: span.spanContext().traceId,\r\n observationId: span.spanContext().spanId,\r\n field,\r\n }).catch((err) => {\r\n this.logger.error(\"Media upload failed with error: \", err);\r\n });\r\n\r\n this.pendingMediaUploads.add(uploadPromise);\r\n\r\n uploadPromise.finally(() => {\r\n this.pendingMediaUploads.delete(uploadPromise);\r\n });\r\n }\r\n\r\n private async handleUpload({\r\n media,\r\n traceId,\r\n observationId,\r\n field,\r\n }: {\r\n media: AntsPlatformMedia;\r\n traceId: string;\r\n observationId?: string;\r\n field: string;\r\n }): Promise<void> {\r\n try {\r\n const contentSha256Hash = await media.getSha256Hash();\r\n\r\n if (\r\n !media.contentLength ||\r\n !media._contentType ||\r\n !contentSha256Hash ||\r\n !media._contentBytes\r\n ) {\r\n return;\r\n }\r\n\r\n const { uploadUrl, mediaId } = await this.apiClient.media.getUploadUrl({\r\n contentLength: media.contentLength,\r\n traceId,\r\n observationId,\r\n field,\r\n contentType: media._contentType,\r\n sha256Hash: contentSha256Hash,\r\n });\r\n\r\n if (!uploadUrl) {\r\n this.logger.debug(\r\n `Media status: Media with ID ${mediaId} already uploaded. Skipping duplicate upload.`,\r\n );\r\n\r\n return;\r\n }\r\n\r\n const clientSideMediaId = await media.getId();\r\n if (clientSideMediaId !== mediaId) {\r\n this.logger.error(\r\n `Media integrity error: Media ID mismatch between SDK (${clientSideMediaId}) and Server (${mediaId}). Upload cancelled. Please check media ID generation logic.`,\r\n );\r\n\r\n return;\r\n }\r\n\r\n this.logger.debug(`Uploading media ${mediaId}...`);\r\n\r\n const startTime = Date.now();\r\n\r\n const uploadResponse = await this.uploadWithBackoff({\r\n uploadUrl,\r\n contentBytes: media._contentBytes,\r\n contentType: media._contentType,\r\n contentSha256Hash: contentSha256Hash,\r\n maxRetries: 3,\r\n baseDelay: 1000,\r\n });\r\n\r\n if (!uploadResponse) {\r\n throw Error(\"Media upload process failed\");\r\n }\r\n\r\n await this.apiClient.media.patch(mediaId, {\r\n uploadedAt: new Date().toISOString(),\r\n uploadHttpStatus: uploadResponse.status,\r\n uploadHttpError: await uploadResponse.text(),\r\n uploadTimeMs: Date.now() - startTime,\r\n });\r\n\r\n this.logger.debug(`Media upload status reported for ${mediaId}`);\r\n } catch (err) {\r\n this.logger.error(`Error processing media item: ${err}`);\r\n }\r\n }\r\n\r\n private async uploadWithBackoff(params: {\r\n uploadUrl: string;\r\n contentType: string;\r\n contentSha256Hash: string;\r\n contentBytes: Uint8Array;\r\n maxRetries: number;\r\n baseDelay: number;\r\n }) {\r\n const {\r\n uploadUrl,\r\n contentType,\r\n contentSha256Hash,\r\n contentBytes,\r\n maxRetries,\r\n baseDelay,\r\n } = params;\r\n\r\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\r\n try {\r\n const uploadResponse = await fetch(uploadUrl, {\r\n method: \"PUT\",\r\n body: contentBytes,\r\n headers: {\r\n \"Content-Type\": contentType,\r\n \"x-amz-checksum-sha256\": contentSha256Hash,\r\n \"x-ms-blob-type\": \"BlockBlob\",\r\n },\r\n });\r\n\r\n if (\r\n attempt < maxRetries &&\r\n uploadResponse.status !== 200 &&\r\n uploadResponse.status !== 201\r\n ) {\r\n throw new Error(`Upload failed with status ${uploadResponse.status}`);\r\n }\r\n\r\n return uploadResponse;\r\n } catch (e) {\r\n if (attempt === maxRetries) {\r\n throw e;\r\n }\r\n\r\n const delay = baseDelay * Math.pow(2, attempt);\r\n const jitter = Math.random() * 1000;\r\n\r\n await new Promise((resolve) => setTimeout(resolve, delay + jitter));\r\n }\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["import_core"]} |
+79
-2
| import { ReadableSpan, SpanExporter, SpanProcessor, Span } from '@opentelemetry/sdk-trace-base'; | ||
| /** | ||
| * Configuration for agent identification in Ants Platform. | ||
| * | ||
| * These parameters are used to identify and track agents in the AI Command Center. | ||
| * The project_id is automatically fetched from the API using your credentials, | ||
| * matching the behavior of the Python and Java SDKs. | ||
| * | ||
| * @public | ||
| */ | ||
| interface AgentConfig { | ||
| /** | ||
| * Agent identifier (stable, required, **cannot change**). | ||
| * This is used as part of the deterministic agent_id generation. | ||
| * Once set, this should never be changed as it will affect the generated agent_id. | ||
| * | ||
| * @example "qa_agent", "customer_support_bot", "data_processor" | ||
| */ | ||
| agentName: string; | ||
| /** | ||
| * Human-readable display name (optional, mutable). | ||
| * This can be changed via the updateAgentDisplayName API without affecting the agent_id. | ||
| * | ||
| * @example "QA Agent - Production", "Customer Support Bot v2" | ||
| */ | ||
| agentDisplayName?: string; | ||
| } | ||
| /** | ||
| * Function type for masking sensitive data in spans before export. | ||
@@ -112,2 +138,32 @@ * | ||
| exportMode?: "immediate" | "batched"; | ||
| /** | ||
| * Agent configuration for AI Command Center integration. | ||
| * | ||
| * When provided, all spans will be automatically tagged with agent identifiers, | ||
| * enabling agent-level tracking and analytics in the AI Command Center. | ||
| * | ||
| * The project_id is automatically fetched from the API using your credentials, | ||
| * matching the behavior of the Python SDK. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * new AntsPlatformSpanProcessor({ | ||
| * publicKey: 'pk_...', | ||
| * secretKey: 'sk_...', | ||
| * agent: { | ||
| * agentName: 'qa_agent', | ||
| * agentDisplayName: 'QA Agent - Production' // optional | ||
| * } | ||
| * }); | ||
| * ``` | ||
| */ | ||
| agent?: AgentConfig; | ||
| /** | ||
| * **Internal/Testing Only**: Override project ID for testing purposes. | ||
| * When set, bypasses the API fetch and uses this value directly. | ||
| * This should NOT be used in production code. | ||
| * | ||
| * @internal | ||
| */ | ||
| _testProjectId?: string; | ||
| } | ||
@@ -160,2 +216,7 @@ /** | ||
| private mediaService; | ||
| private resolvedAgentConfig?; | ||
| private pendingAgentConfig?; | ||
| private agentConfigPromise?; | ||
| private cachedProjectId?; | ||
| private testProjectId?; | ||
| /** | ||
@@ -188,5 +249,21 @@ * Creates a new AntsPlatformSpanProcessor instance. | ||
| constructor(params?: AntsPlatformSpanProcessorParams); | ||
| /** | ||
| * Fetches project_id from the API and resolves agent configuration. | ||
| * This matches the Java/Python SDK behavior where project_id is always fetched from the API. | ||
| * | ||
| * @param config - Agent configuration with agentName and optional agentDisplayName | ||
| * @internal | ||
| */ | ||
| private initializeAgentConfig; | ||
| /** | ||
| * Fetches the project_id from the Ants Platform API. | ||
| * This matches the Python SDK behavior: GET /api/public/projects -> data[0].id | ||
| * | ||
| * @returns Promise resolving to the project ID, or null if unavailable | ||
| * @internal | ||
| */ | ||
| private fetchProjectId; | ||
| private get logger(); | ||
| /** | ||
| * Called when a span is started. Adds environment and release attributes to the span. | ||
| * Called when a span is started. Adds environment, release, and agent attributes to the span. | ||
| * | ||
@@ -236,2 +313,2 @@ * @param span - The span that was started | ||
| export { AntsPlatformSpanProcessor, type AntsPlatformSpanProcessorParams, type MaskFunction, type ShouldExportSpan }; | ||
| export { type AgentConfig, AntsPlatformSpanProcessor, type AntsPlatformSpanProcessorParams, type MaskFunction, type ShouldExportSpan }; |
+79
-2
| import { ReadableSpan, SpanExporter, SpanProcessor, Span } from '@opentelemetry/sdk-trace-base'; | ||
| /** | ||
| * Configuration for agent identification in Ants Platform. | ||
| * | ||
| * These parameters are used to identify and track agents in the AI Command Center. | ||
| * The project_id is automatically fetched from the API using your credentials, | ||
| * matching the behavior of the Python and Java SDKs. | ||
| * | ||
| * @public | ||
| */ | ||
| interface AgentConfig { | ||
| /** | ||
| * Agent identifier (stable, required, **cannot change**). | ||
| * This is used as part of the deterministic agent_id generation. | ||
| * Once set, this should never be changed as it will affect the generated agent_id. | ||
| * | ||
| * @example "qa_agent", "customer_support_bot", "data_processor" | ||
| */ | ||
| agentName: string; | ||
| /** | ||
| * Human-readable display name (optional, mutable). | ||
| * This can be changed via the updateAgentDisplayName API without affecting the agent_id. | ||
| * | ||
| * @example "QA Agent - Production", "Customer Support Bot v2" | ||
| */ | ||
| agentDisplayName?: string; | ||
| } | ||
| /** | ||
| * Function type for masking sensitive data in spans before export. | ||
@@ -112,2 +138,32 @@ * | ||
| exportMode?: "immediate" | "batched"; | ||
| /** | ||
| * Agent configuration for AI Command Center integration. | ||
| * | ||
| * When provided, all spans will be automatically tagged with agent identifiers, | ||
| * enabling agent-level tracking and analytics in the AI Command Center. | ||
| * | ||
| * The project_id is automatically fetched from the API using your credentials, | ||
| * matching the behavior of the Python SDK. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * new AntsPlatformSpanProcessor({ | ||
| * publicKey: 'pk_...', | ||
| * secretKey: 'sk_...', | ||
| * agent: { | ||
| * agentName: 'qa_agent', | ||
| * agentDisplayName: 'QA Agent - Production' // optional | ||
| * } | ||
| * }); | ||
| * ``` | ||
| */ | ||
| agent?: AgentConfig; | ||
| /** | ||
| * **Internal/Testing Only**: Override project ID for testing purposes. | ||
| * When set, bypasses the API fetch and uses this value directly. | ||
| * This should NOT be used in production code. | ||
| * | ||
| * @internal | ||
| */ | ||
| _testProjectId?: string; | ||
| } | ||
@@ -160,2 +216,7 @@ /** | ||
| private mediaService; | ||
| private resolvedAgentConfig?; | ||
| private pendingAgentConfig?; | ||
| private agentConfigPromise?; | ||
| private cachedProjectId?; | ||
| private testProjectId?; | ||
| /** | ||
@@ -188,5 +249,21 @@ * Creates a new AntsPlatformSpanProcessor instance. | ||
| constructor(params?: AntsPlatformSpanProcessorParams); | ||
| /** | ||
| * Fetches project_id from the API and resolves agent configuration. | ||
| * This matches the Java/Python SDK behavior where project_id is always fetched from the API. | ||
| * | ||
| * @param config - Agent configuration with agentName and optional agentDisplayName | ||
| * @internal | ||
| */ | ||
| private initializeAgentConfig; | ||
| /** | ||
| * Fetches the project_id from the Ants Platform API. | ||
| * This matches the Python SDK behavior: GET /api/public/projects -> data[0].id | ||
| * | ||
| * @returns Promise resolving to the project ID, or null if unavailable | ||
| * @internal | ||
| */ | ||
| private fetchProjectId; | ||
| private get logger(); | ||
| /** | ||
| * Called when a span is started. Adds environment and release attributes to the span. | ||
| * Called when a span is started. Adds environment, release, and agent attributes to the span. | ||
| * | ||
@@ -236,2 +313,2 @@ * @param span - The span that was started | ||
| export { AntsPlatformSpanProcessor, type AntsPlatformSpanProcessorParams, type MaskFunction, type ShouldExportSpan }; | ||
| export { type AgentConfig, AntsPlatformSpanProcessor, type AntsPlatformSpanProcessorParams, type MaskFunction, type ShouldExportSpan }; |
+133
-4
@@ -10,2 +10,4 @@ // src/span-processor.ts | ||
| } from "@antsplatform/core"; | ||
| import { blake2b } from "@noble/hashes/blake2.js"; | ||
| import { bytesToHex } from "@noble/hashes/utils.js"; | ||
| import { hrTimeToMilliseconds } from "@opentelemetry/core"; | ||
@@ -256,2 +258,46 @@ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; | ||
| // src/span-processor.ts | ||
| var MAX_AGENT_NAME_LENGTH = 255; | ||
| function generateAgentId(agentName, projectId) { | ||
| const logger = getGlobalLogger2(); | ||
| if (!agentName || typeof agentName !== "string") { | ||
| throw new Error("agentName must be a non-empty string"); | ||
| } | ||
| if (!projectId || typeof projectId !== "string") { | ||
| throw new Error("projectId must be a non-empty string"); | ||
| } | ||
| let name = agentName.trim(); | ||
| if (name.length > MAX_AGENT_NAME_LENGTH) { | ||
| logger.warn( | ||
| `agentName too long (${name.length} chars), truncated to ${MAX_AGENT_NAME_LENGTH} characters` | ||
| ); | ||
| name = name.slice(0, MAX_AGENT_NAME_LENGTH); | ||
| } | ||
| const agentId = blake2b64(name, projectId.trim()); | ||
| logger.debug(`[AGENT_ID] Generated: ${agentId} from agent_name: ${name}`); | ||
| return agentId; | ||
| } | ||
| function blake2b64(agentName, projectId) { | ||
| const combined = new TextEncoder().encode(agentName + projectId); | ||
| const hash = blake2b(combined, { dkLen: 8 }); | ||
| return bytesToHex(hash); | ||
| } | ||
| function resolveAgentConfigWithProjectId(config, projectId) { | ||
| var _a; | ||
| if (!projectId || !projectId.trim()) { | ||
| throw new Error("projectId is required"); | ||
| } | ||
| if (!config.agentName || !config.agentName.trim()) { | ||
| throw new Error("agentName is required"); | ||
| } | ||
| const agentId = generateAgentId( | ||
| config.agentName.trim(), | ||
| projectId.trim() | ||
| ); | ||
| return { | ||
| agentId, | ||
| agentName: config.agentName.trim(), | ||
| agentDisplayName: (_a = config.agentDisplayName) == null ? void 0 : _a.trim(), | ||
| projectId: projectId.trim() | ||
| }; | ||
| } | ||
| var AntsPlatformSpanProcessor = class { | ||
@@ -341,2 +387,7 @@ /** | ||
| this.mediaService = new MediaService({ apiClient: this.apiClient }); | ||
| this.testProjectId = params == null ? void 0 : params._testProjectId; | ||
| if (params == null ? void 0 : params.agent) { | ||
| this.pendingAgentConfig = params.agent; | ||
| this.agentConfigPromise = this.initializeAgentConfig(params.agent); | ||
| } | ||
| logger.debug("Initialized AntsPlatformSpanProcessor with params:", { | ||
@@ -349,5 +400,69 @@ publicKey, | ||
| flushAt, | ||
| flushIntervalSeconds | ||
| flushIntervalSeconds, | ||
| hasAgentConfig: !!(params == null ? void 0 : params.agent) | ||
| }); | ||
| } | ||
| /** | ||
| * Fetches project_id from the API and resolves agent configuration. | ||
| * This matches the Java/Python SDK behavior where project_id is always fetched from the API. | ||
| * | ||
| * @param config - Agent configuration with agentName and optional agentDisplayName | ||
| * @internal | ||
| */ | ||
| async initializeAgentConfig(config) { | ||
| const logger = getGlobalLogger2(); | ||
| try { | ||
| let projectId = null; | ||
| if (this.testProjectId) { | ||
| logger.debug(`[AGENT_CONFIG] Using test projectId: ${this.testProjectId}`); | ||
| projectId = this.testProjectId; | ||
| } else { | ||
| projectId = await this.fetchProjectId(); | ||
| } | ||
| if (!projectId) { | ||
| logger.error( | ||
| "[AGENT_CONFIG] Failed to fetch project_id from API. Agent attributes will not be added to spans." | ||
| ); | ||
| return; | ||
| } | ||
| logger.debug(`[AGENT_CONFIG] Using projectId: ${projectId}`); | ||
| this.resolvedAgentConfig = resolveAgentConfigWithProjectId(config, projectId); | ||
| this.cachedProjectId = projectId; | ||
| logger.info("[AGENT_CONFIG] Successfully initialized agent configuration:", { | ||
| agentId: this.resolvedAgentConfig.agentId, | ||
| agentName: this.resolvedAgentConfig.agentName, | ||
| agentDisplayName: this.resolvedAgentConfig.agentDisplayName, | ||
| projectId: this.resolvedAgentConfig.projectId | ||
| }); | ||
| } catch (error) { | ||
| logger.error( | ||
| `[AGENT_CONFIG] Failed to initialize agent configuration: ${error instanceof Error ? error.message : String(error)}` | ||
| ); | ||
| } | ||
| } | ||
| /** | ||
| * Fetches the project_id from the Ants Platform API. | ||
| * This matches the Python SDK behavior: GET /api/public/projects -> data[0].id | ||
| * | ||
| * @returns Promise resolving to the project ID, or null if unavailable | ||
| * @internal | ||
| */ | ||
| async fetchProjectId() { | ||
| const logger = getGlobalLogger2(); | ||
| try { | ||
| const response = await this.apiClient.projects.get(); | ||
| if (response.data && response.data.length > 0) { | ||
| const projectId = response.data[0].id; | ||
| logger.debug(`[PROJECT_ID] Fetched from API: ${projectId}`); | ||
| return projectId; | ||
| } | ||
| logger.warn("[PROJECT_ID] No projects found in API response"); | ||
| return null; | ||
| } catch (error) { | ||
| logger.warn( | ||
| `[PROJECT_ID] Failed to fetch from API: ${error instanceof Error ? error.message : String(error)}` | ||
| ); | ||
| return null; | ||
| } | ||
| } | ||
| get logger() { | ||
@@ -357,3 +472,3 @@ return getGlobalLogger2(); | ||
| /** | ||
| * Called when a span is started. Adds environment and release attributes to the span. | ||
| * Called when a span is started. Adds environment, release, and agent attributes to the span. | ||
| * | ||
@@ -366,6 +481,17 @@ * @param span - The span that was started | ||
| onStart(span, parentContext) { | ||
| span.setAttributes({ | ||
| const attributes = { | ||
| [AntsPlatformOtelSpanAttributes2.ENVIRONMENT]: this.environment, | ||
| [AntsPlatformOtelSpanAttributes2.RELEASE]: this.release | ||
| }); | ||
| }; | ||
| if (this.resolvedAgentConfig) { | ||
| attributes[AntsPlatformOtelSpanAttributes2.AGENT_ID] = this.resolvedAgentConfig.agentId; | ||
| attributes[AntsPlatformOtelSpanAttributes2.AGENT_NAME] = this.resolvedAgentConfig.agentName; | ||
| attributes[AntsPlatformOtelSpanAttributes2.AGENT_DISPLAY_NAME] = this.resolvedAgentConfig.agentDisplayName; | ||
| attributes[AntsPlatformOtelSpanAttributes2.PROJECT_ID] = this.resolvedAgentConfig.projectId; | ||
| } else if (this.pendingAgentConfig) { | ||
| this.logger.debug( | ||
| "[AGENT_CONFIG] Agent config not yet resolved, span will not have agent attributes. This may happen for spans created before projectId fetch completes." | ||
| ); | ||
| } | ||
| span.setAttributes(attributes); | ||
| return this.processor.onStart(span, parentContext); | ||
@@ -397,2 +523,5 @@ } | ||
| async flush() { | ||
| if (this.agentConfigPromise) { | ||
| await this.agentConfigPromise; | ||
| } | ||
| await Promise.all(Array.from(this.pendingEndedSpans)); | ||
@@ -399,0 +528,0 @@ await this.mediaService.flush(); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/span-processor.ts","../src/MediaService.ts"],"sourcesContent":["import {\n Logger,\n getGlobalLogger,\n AntsPlatformAPIClient,\n ANTS_PLATFORM_SDK_VERSION,\n AntsPlatformOtelSpanAttributes,\n getEnv,\n base64Encode,\n} from \"@antsplatform/core\";\nimport { hrTimeToMilliseconds } from \"@opentelemetry/core\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport {\n Span,\n BatchSpanProcessor,\n SimpleSpanProcessor,\n SpanExporter,\n ReadableSpan,\n SpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\n\nimport { MediaService } from \"./MediaService.js\";\n\n/**\n * Function type for masking sensitive data in spans before export.\n *\n * @param params - Object containing the data to be masked\n * @param params.data - The data that should be masked\n * @returns The masked data (can be of any type)\n *\n * @example\n * ```typescript\n * const maskFunction: MaskFunction = ({ data }) => {\n * if (typeof data === 'string') {\n * return data.replace(/password=\\w+/g, 'password=***');\n * }\n * return data;\n * };\n * ```\n *\n * @public\n */\nexport type MaskFunction = (params: { data: any }) => any;\n\n/**\n * Function type for determining whether a span should be exported to AntsPlatform.\n *\n * @param params - Object containing the span to evaluate\n * @param params.otelSpan - The OpenTelemetry span to evaluate\n * @returns `true` if the span should be exported, `false` otherwise\n *\n * @example\n * ```typescript\n * const shouldExportSpan: ShouldExportSpan = ({ otelSpan }) => {\n * // Only export spans that took longer than 100ms\n * return otelSpan.duration[0] * 1000 + otelSpan.duration[1] / 1000000 > 100;\n * };\n * ```\n *\n * @public\n */\nexport type ShouldExportSpan = (params: { otelSpan: ReadableSpan }) => boolean;\n\n/**\n * Configuration parameters for the AntsPlatformSpanProcessor.\n *\n * @public\n */\nexport interface AntsPlatformSpanProcessorParams {\n /**\n * Custom OpenTelemetry span exporter. If not provided, a default OTLP exporter will be used.\n */\n exporter?: SpanExporter;\n\n /**\n * Ants Platform public API key. Can also be set via ANTS_PLATFORM_PUBLIC_KEY environment variable.\n */\n publicKey?: string;\n\n /**\n * Ants Platform secret API key. Can also be set via ANTS_PLATFORM_SECRET_KEY environment variable.\n */\n secretKey?: string;\n\n /**\n * AntsPlatform instance base URL. Can also be set via ANTS_PLATFORM_BASE_URL environment variable.\n * @defaultValue \"https://api.ants-platform.com\"\n */\n baseUrl?: string;\n\n /**\n * Number of spans to batch before flushing. Can also be set via ANTS_PLATFORM_FLUSH_AT environment variable.\n */\n flushAt?: number;\n\n /**\n * Flush interval in seconds. Can also be set via ANTS_PLATFORM_FLUSH_INTERVAL environment variable.\n */\n flushInterval?: number;\n\n /**\n * Function to mask sensitive data in spans before export.\n */\n mask?: MaskFunction;\n\n /**\n * Function to determine whether a span should be exported to AntsPlatform.\n */\n shouldExportSpan?: ShouldExportSpan;\n\n /**\n * Environment identifier for the traces. Can also be set via ANTS_PLATFORM_TRACING_ENVIRONMENT environment variable.\n */\n environment?: string;\n\n /**\n * Release identifier for the traces. Can also be set via ANTS_PLATFORM_RELEASE environment variable.\n */\n release?: string;\n\n /**\n * Request timeout in seconds. Can also be set via ANTS_PLATFORM_TIMEOUT environment variable.\n * @defaultValue 5\n */\n timeout?: number;\n\n /**\n * Additional HTTP headers to include with requests.\n */\n additionalHeaders?: Record<string, string>;\n /**\n * Span export mode to use.\n *\n * - **batched**: Recommended for production environments with long-running processes.\n * Spans are batched and exported in groups for optimal performance.\n * - **immediate**: Recommended for short-lived environments such as serverless functions.\n * Spans are exported immediately to prevent data loss when the process terminates / is frozen.\n *\n * @defaultValue \"batched\"\n */\n exportMode?: \"immediate\" | \"batched\";\n}\n\n/**\n * OpenTelemetry span processor for sending spans to AntsPlatform.\n *\n * This processor extends the standard BatchSpanProcessor to provide:\n * - Automatic batching and flushing of spans to AntsPlatform\n * - Media content extraction and upload from base64 data URIs\n * - Data masking capabilities for sensitive information\n * - Conditional span export based on custom logic\n * - Environment and release tagging\n *\n * @example\n * ```typescript\n * import { NodeSDK } from '@opentelemetry/sdk-node';\n * import { AntsPlatformSpanProcessor } from '@antsplatform/otel';\n *\n * const sdk = new NodeSDK({\n * spanProcessors: [\n * new AntsPlatformSpanProcessor({\n * publicKey: 'pk_...',\n * secretKey: 'sk_...',\n * baseUrl: 'https://api.ants-platform.com',\n * environment: 'production',\n * mask: ({ data }) => {\n * // Mask sensitive data\n * return data.replace(/api_key=\\w+/g, 'api_key=***');\n * }\n * })\n * ]\n * });\n *\n * sdk.start();\n * ```\n *\n * @public\n */\nexport class AntsPlatformSpanProcessor implements SpanProcessor {\n private pendingEndedSpans: Set<Promise<void>> = new Set();\n\n private publicKey?: string;\n private baseUrl?: string;\n private environment?: string;\n private release?: string;\n private mask?: MaskFunction;\n private shouldExportSpan?: ShouldExportSpan;\n private apiClient: AntsPlatformAPIClient;\n private processor: SpanProcessor;\n private mediaService: MediaService;\n\n /**\n * Creates a new AntsPlatformSpanProcessor instance.\n *\n * @param params - Configuration parameters for the processor\n *\n * @example\n * ```typescript\n * const processor = new AntsPlatformSpanProcessor({\n * publicKey: 'pk_...',\n * secretKey: 'sk_...',\n * environment: 'staging',\n * flushAt: 10,\n * flushInterval: 2,\n * mask: ({ data }) => {\n * // Custom masking logic\n * return typeof data === 'string'\n * ? data.replace(/secret_\\w+/g, 'secret_***')\n * : data;\n * },\n * shouldExportSpan: ({ otelSpan }) => {\n * // Only export spans from specific services\n * return otelSpan.name.startsWith('my-service');\n * }\n * });\n * ```\n */\n constructor(params?: AntsPlatformSpanProcessorParams) {\n const logger = getGlobalLogger();\n\n const publicKey = params?.publicKey ?? getEnv(\"ANTS_PLATFORM_PUBLIC_KEY\");\n const secretKey = params?.secretKey ?? getEnv(\"ANTS_PLATFORM_SECRET_KEY\");\n const baseUrl =\n params?.baseUrl ??\n getEnv(\"ANTS_PLATFORM_BASE_URL\") ??\n getEnv(\"ANTS_PLATFORM_BASEURL\") ?? // legacy v2\n \"https://api.ants-platform.com\";\n\n if (!params?.exporter && !publicKey) {\n logger.warn(\n \"No exporter configured and no public key provided in constructor or as ANTS_PLATFORM_PUBLIC_KEY env var. Span exports will fail.\",\n );\n }\n if (!params?.exporter && !secretKey) {\n logger.warn(\n \"No exporter configured and no secret key provided in constructor or as ANTS_PLATFORM_SECRET_KEY env var. Span exports will fail.\",\n );\n }\n const flushAt = params?.flushAt ?? getEnv(\"ANTS_PLATFORM_FLUSH_AT\");\n const flushIntervalSeconds =\n params?.flushInterval ?? getEnv(\"ANTS_PLATFORM_FLUSH_INTERVAL\");\n\n const authHeaderValue = base64Encode(`${publicKey}:${secretKey}`);\n const timeoutSeconds =\n params?.timeout ?? Number(getEnv(\"ANTS_PLATFORM_TIMEOUT\") ?? 5);\n\n const exporter =\n params?.exporter ??\n new OTLPTraceExporter({\n url: `${baseUrl}/api/public/otel/v1/traces`,\n headers: {\n Authorization: `Basic ${authHeaderValue}`,\n x_antsPlatform_sdk_name: \"javascript\",\n x_antsPlatform_sdk_version: ANTS_PLATFORM_SDK_VERSION,\n x_antsPlatform_public_key: publicKey ?? \"<missing>\",\n ...params?.additionalHeaders,\n },\n timeoutMillis: timeoutSeconds * 1_000,\n });\n\n this.processor =\n params?.exportMode === \"immediate\"\n ? new SimpleSpanProcessor(exporter)\n : new BatchSpanProcessor(exporter, {\n maxExportBatchSize: flushAt ? Number(flushAt) : undefined,\n scheduledDelayMillis: flushIntervalSeconds\n ? Number(flushIntervalSeconds) * 1_000\n : undefined,\n });\n\n this.publicKey = publicKey;\n this.baseUrl = baseUrl;\n this.environment =\n params?.environment ?? getEnv(\"ANTS_PLATFORM_TRACING_ENVIRONMENT\");\n this.release = params?.release ?? getEnv(\"ANTS_PLATFORM_RELEASE\");\n this.mask = params?.mask;\n this.shouldExportSpan = params?.shouldExportSpan;\n this.apiClient = new AntsPlatformAPIClient({\n baseUrl: this.baseUrl,\n username: this.publicKey,\n password: secretKey,\n xAntsPlatformPublicKey: this.publicKey,\n xAntsPlatformSdkVersion: ANTS_PLATFORM_SDK_VERSION,\n xAntsPlatformSdkName: \"javascript\",\n environment: \"\", // noop as baseUrl is set\n headers: params?.additionalHeaders,\n });\n\n this.mediaService = new MediaService({ apiClient: this.apiClient });\n\n logger.debug(\"Initialized AntsPlatformSpanProcessor with params:\", {\n publicKey,\n baseUrl,\n environment: this.environment,\n release: this.release,\n timeoutSeconds,\n flushAt,\n flushIntervalSeconds,\n });\n }\n\n private get logger(): Logger {\n return getGlobalLogger();\n }\n\n /**\n * Called when a span is started. Adds environment and release attributes to the span.\n *\n * @param span - The span that was started\n * @param parentContext - The parent context\n *\n * @override\n */\n public onStart(span: Span, parentContext: any): void {\n span.setAttributes({\n [AntsPlatformOtelSpanAttributes.ENVIRONMENT]: this.environment,\n [AntsPlatformOtelSpanAttributes.RELEASE]: this.release,\n });\n\n return this.processor.onStart(span, parentContext);\n }\n\n /**\n * Called when a span ends. Processes the span for export to AntsPlatform.\n *\n * This method:\n * 1. Checks if the span should be exported using the shouldExportSpan function\n * 2. Applies data masking to sensitive attributes\n * 3. Handles media content extraction and upload\n * 4. Logs span details in debug mode\n * 5. Passes the span to the parent processor for export\n *\n * @param span - The span that ended\n *\n * @override\n */\n public onEnd(span: ReadableSpan): void {\n const processEndedSpanPromise = this.processEndedSpan(span).catch((err) => {\n this.logger.error(err);\n });\n\n // Enqueue this export to the pending list so it can be flushed by the user.\n this.pendingEndedSpans.add(processEndedSpanPromise);\n\n void processEndedSpanPromise.finally(() =>\n this.pendingEndedSpans.delete(processEndedSpanPromise),\n );\n }\n\n private async flush(): Promise<void> {\n await Promise.all(Array.from(this.pendingEndedSpans));\n await this.mediaService.flush();\n }\n\n /**\n * Forces an immediate flush of all pending spans and media uploads.\n *\n * @returns Promise that resolves when all pending operations are complete\n *\n * @override\n */\n public async forceFlush(): Promise<void> {\n await this.flush();\n\n return this.processor.forceFlush();\n }\n\n /**\n * Gracefully shuts down the processor, ensuring all pending operations are completed.\n *\n * @returns Promise that resolves when shutdown is complete\n *\n * @override\n */\n public async shutdown(): Promise<void> {\n await this.flush();\n\n return this.processor.shutdown();\n }\n\n private async processEndedSpan(span: ReadableSpan) {\n if (this.shouldExportSpan) {\n try {\n if (this.shouldExportSpan({ otelSpan: span }) === false) return;\n } catch (err) {\n this.logger.error(\n \"ShouldExportSpan failed with error. Excluding span. Error: \",\n err,\n );\n\n return;\n }\n }\n\n this.applyMaskInPlace(span);\n await this.mediaService.process(span);\n\n this.logger.debug(\n `Processed span:\\n${JSON.stringify(\n {\n name: span.name,\n traceId: span.spanContext().traceId,\n spanId: span.spanContext().spanId,\n parentSpanId: span.parentSpanContext?.spanId ?? null,\n attributes: span.attributes,\n startTime: new Date(hrTimeToMilliseconds(span.startTime)),\n endTime: new Date(hrTimeToMilliseconds(span.endTime)),\n durationMs: hrTimeToMilliseconds(span.duration),\n kind: span.kind,\n status: span.status,\n resource: span.resource.attributes,\n instrumentationScope: span.instrumentationScope,\n },\n null,\n 2,\n )}`,\n );\n\n this.processor.onEnd(span);\n }\n private applyMaskInPlace(span: ReadableSpan): void {\n const maskCandidates = [\n AntsPlatformOtelSpanAttributes.OBSERVATION_INPUT,\n AntsPlatformOtelSpanAttributes.TRACE_INPUT,\n AntsPlatformOtelSpanAttributes.OBSERVATION_OUTPUT,\n AntsPlatformOtelSpanAttributes.TRACE_OUTPUT,\n AntsPlatformOtelSpanAttributes.OBSERVATION_METADATA,\n AntsPlatformOtelSpanAttributes.TRACE_METADATA,\n ];\n\n for (const maskCandidate of maskCandidates) {\n if (maskCandidate in span.attributes) {\n span.attributes[maskCandidate] = this.applyMask(\n span.attributes[maskCandidate],\n );\n }\n }\n }\n\n private applyMask<T>(data: T): T | string {\n if (!this.mask) return data;\n\n try {\n return this.mask({ data });\n } catch (err) {\n this.logger.warn(\n `Applying mask function failed due to error, fully masking property. Error: ${err}`,\n );\n\n return \"<fully masked due to failed mask function>\";\n }\n }\n}\n","import {\n AntsPlatformAPIClient,\n AntsPlatformMedia,\n AntsPlatformOtelSpanAttributes,\n Logger,\n base64ToBytes,\n getGlobalLogger,\n} from \"@antsplatform/core\";\nimport { ReadableSpan } from \"@opentelemetry/sdk-trace-base\";\n\nexport class MediaService {\n private pendingMediaUploads: Set<Promise<void>> = new Set();\n private apiClient: AntsPlatformAPIClient;\n\n constructor(params: { apiClient: AntsPlatformAPIClient }) {\n this.apiClient = params.apiClient;\n }\n\n get logger(): Logger {\n return getGlobalLogger();\n }\n\n public async flush(): Promise<void> {\n await Promise.all(Array.from(this.pendingMediaUploads));\n }\n\n public async process(span: ReadableSpan) {\n const mediaAttributes = [\n AntsPlatformOtelSpanAttributes.OBSERVATION_INPUT,\n AntsPlatformOtelSpanAttributes.TRACE_INPUT,\n AntsPlatformOtelSpanAttributes.OBSERVATION_OUTPUT,\n AntsPlatformOtelSpanAttributes.TRACE_OUTPUT,\n AntsPlatformOtelSpanAttributes.OBSERVATION_METADATA,\n AntsPlatformOtelSpanAttributes.TRACE_METADATA,\n ];\n\n for (const mediaAttribute of mediaAttributes) {\n const mediaRelevantAttributeKeys = Object.keys(span.attributes).filter(\n (attributeName) => attributeName.startsWith(mediaAttribute),\n );\n\n for (const key of mediaRelevantAttributeKeys) {\n const value = span.attributes[key];\n\n if (typeof value !== \"string\") {\n this.logger.warn(\n `Span attribute ${mediaAttribute} is not a stringified object. Skipping media handling.`,\n );\n\n continue;\n }\n\n // Find media base64 data URI\n let mediaReplacedValue = value;\n const regex = /data:[^;]+;base64,[A-Za-z0-9+/]+=*/g;\n const foundMedia = [...new Set(value.match(regex) ?? [])];\n\n if (foundMedia.length === 0) continue;\n\n for (const mediaDataUri of foundMedia) {\n // For each media, create media tag and initiate upload\n const media = new AntsPlatformMedia({\n base64DataUri: mediaDataUri,\n source: \"base64_data_uri\",\n });\n\n const antsPlatformMediaTag = await media.getTag();\n\n if (!antsPlatformMediaTag) {\n this.logger.warn(\n \"Failed to create AntsPlatform media tag. Skipping media item.\",\n );\n\n continue;\n }\n\n this.scheduleUpload({\n span,\n media,\n field: mediaAttribute.includes(\"input\")\n ? \"input\"\n : mediaAttribute.includes(\"output\")\n ? \"output\"\n : \"metadata\", // todo: make more robust\n });\n\n // Replace original attribute with media escaped attribute\n mediaReplacedValue = mediaReplacedValue.replaceAll(\n mediaDataUri,\n antsPlatformMediaTag,\n );\n }\n\n span.attributes[key] = mediaReplacedValue;\n }\n }\n\n // Handle media from Vercel AI SDK\n if (span.instrumentationScope.name === \"ai\") {\n const aiSDKMediaAttributes = [\"ai.prompt.messages\", \"ai.prompt\"];\n\n for (const mediaAttribute of aiSDKMediaAttributes) {\n const value = span.attributes[mediaAttribute];\n\n if (!value || typeof value !== \"string\") {\n continue;\n }\n\n // Find media base64 data URI\n let mediaReplacedValue = value;\n\n try {\n const parsed = JSON.parse(value);\n\n if (Array.isArray(parsed)) {\n for (const message of parsed) {\n if (Array.isArray(message[\"content\"])) {\n const contentParts = message[\"content\"];\n\n for (const part of contentParts) {\n if (part[\"type\"] === \"file\") {\n let base64Content: string | null = null;\n // FilePart\n if (part[\"data\"] != null && part[\"mediaType\"] != null) {\n base64Content = part[\"data\"];\n }\n\n //ImagePart\n if (part[\"image\"] != null && part[\"mediaType\"] != null) {\n base64Content = part[\"image\"];\n }\n\n if (!base64Content) continue;\n\n const media = new AntsPlatformMedia({\n contentType: part[\"mediaType\"],\n contentBytes: base64ToBytes(base64Content),\n source: \"bytes\",\n });\n\n const antsPlatformMediaTag = await media.getTag();\n\n if (!antsPlatformMediaTag) {\n this.logger.warn(\n \"Failed to create AntsPlatform media tag. Skipping media item.\",\n );\n\n continue;\n }\n\n this.scheduleUpload({\n span,\n media,\n field: \"input\",\n });\n\n // Replace original attribute with media escaped attribute\n mediaReplacedValue = mediaReplacedValue.replaceAll(\n base64Content,\n antsPlatformMediaTag,\n );\n }\n }\n }\n }\n }\n\n span.attributes[mediaAttribute] = mediaReplacedValue;\n } catch (err) {\n this.logger.warn(\n `Failed to handle media for AI SDK attribute ${mediaAttribute} for span ${span.spanContext().spanId}`,\n err,\n );\n }\n }\n }\n }\n\n private scheduleUpload(params: {\n span: ReadableSpan;\n field: string;\n media: AntsPlatformMedia;\n }) {\n const { span, field, media } = params;\n\n const uploadPromise: Promise<void> = this.handleUpload({\n media,\n traceId: span.spanContext().traceId,\n observationId: span.spanContext().spanId,\n field,\n }).catch((err) => {\n this.logger.error(\"Media upload failed with error: \", err);\n });\n\n this.pendingMediaUploads.add(uploadPromise);\n\n uploadPromise.finally(() => {\n this.pendingMediaUploads.delete(uploadPromise);\n });\n }\n\n private async handleUpload({\n media,\n traceId,\n observationId,\n field,\n }: {\n media: AntsPlatformMedia;\n traceId: string;\n observationId?: string;\n field: string;\n }): Promise<void> {\n try {\n const contentSha256Hash = await media.getSha256Hash();\n\n if (\n !media.contentLength ||\n !media._contentType ||\n !contentSha256Hash ||\n !media._contentBytes\n ) {\n return;\n }\n\n const { uploadUrl, mediaId } = await this.apiClient.media.getUploadUrl({\n contentLength: media.contentLength,\n traceId,\n observationId,\n field,\n contentType: media._contentType,\n sha256Hash: contentSha256Hash,\n });\n\n if (!uploadUrl) {\n this.logger.debug(\n `Media status: Media with ID ${mediaId} already uploaded. Skipping duplicate upload.`,\n );\n\n return;\n }\n\n const clientSideMediaId = await media.getId();\n if (clientSideMediaId !== mediaId) {\n this.logger.error(\n `Media integrity error: Media ID mismatch between SDK (${clientSideMediaId}) and Server (${mediaId}). Upload cancelled. Please check media ID generation logic.`,\n );\n\n return;\n }\n\n this.logger.debug(`Uploading media ${mediaId}...`);\n\n const startTime = Date.now();\n\n const uploadResponse = await this.uploadWithBackoff({\n uploadUrl,\n contentBytes: media._contentBytes,\n contentType: media._contentType,\n contentSha256Hash: contentSha256Hash,\n maxRetries: 3,\n baseDelay: 1000,\n });\n\n if (!uploadResponse) {\n throw Error(\"Media upload process failed\");\n }\n\n await this.apiClient.media.patch(mediaId, {\n uploadedAt: new Date().toISOString(),\n uploadHttpStatus: uploadResponse.status,\n uploadHttpError: await uploadResponse.text(),\n uploadTimeMs: Date.now() - startTime,\n });\n\n this.logger.debug(`Media upload status reported for ${mediaId}`);\n } catch (err) {\n this.logger.error(`Error processing media item: ${err}`);\n }\n }\n\n private async uploadWithBackoff(params: {\n uploadUrl: string;\n contentType: string;\n contentSha256Hash: string;\n contentBytes: Uint8Array;\n maxRetries: number;\n baseDelay: number;\n }) {\n const {\n uploadUrl,\n contentType,\n contentSha256Hash,\n contentBytes,\n maxRetries,\n baseDelay,\n } = params;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const uploadResponse = await fetch(uploadUrl, {\n method: \"PUT\",\n body: contentBytes,\n headers: {\n \"Content-Type\": contentType,\n \"x-amz-checksum-sha256\": contentSha256Hash,\n \"x-ms-blob-type\": \"BlockBlob\",\n },\n });\n\n if (\n attempt < maxRetries &&\n uploadResponse.status !== 200 &&\n uploadResponse.status !== 201\n ) {\n throw new Error(`Upload failed with status ${uploadResponse.status}`);\n }\n\n return uploadResponse;\n } catch (e) {\n if (attempt === maxRetries) {\n throw e;\n }\n\n const delay = baseDelay * Math.pow(2, attempt);\n const jitter = Math.random() * 1000;\n\n await new Promise((resolve) => setTimeout(resolve, delay + jitter));\n }\n }\n }\n}\n"],"mappings":"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","names":["getGlobalLogger","AntsPlatformAPIClient","AntsPlatformOtelSpanAttributes","getGlobalLogger","AntsPlatformAPIClient","AntsPlatformOtelSpanAttributes"]} | ||
| {"version":3,"sources":["../src/span-processor.ts","../src/MediaService.ts"],"sourcesContent":["import {\r\n Logger,\r\n getGlobalLogger,\r\n AntsPlatformAPIClient,\r\n ANTS_PLATFORM_SDK_VERSION,\r\n AntsPlatformOtelSpanAttributes,\r\n getEnv,\r\n base64Encode,\r\n} from \"@antsplatform/core\";\r\nimport { blake2b } from \"@noble/hashes/blake2.js\";\r\nimport { bytesToHex } from \"@noble/hashes/utils.js\";\r\nimport { hrTimeToMilliseconds } from \"@opentelemetry/core\";\r\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\r\nimport {\r\n Span,\r\n BatchSpanProcessor,\r\n SimpleSpanProcessor,\r\n SpanExporter,\r\n ReadableSpan,\r\n SpanProcessor,\r\n} from \"@opentelemetry/sdk-trace-base\";\r\n\r\nimport { MediaService } from \"./MediaService.js\";\r\n\r\n/**\r\n * Configuration for agent identification in Ants Platform.\r\n *\r\n * These parameters are used to identify and track agents in the AI Command Center.\r\n * The project_id is automatically fetched from the API using your credentials,\r\n * matching the behavior of the Python and Java SDKs.\r\n *\r\n * @public\r\n */\r\nexport interface AgentConfig {\r\n /**\r\n * Agent identifier (stable, required, **cannot change**).\r\n * This is used as part of the deterministic agent_id generation.\r\n * Once set, this should never be changed as it will affect the generated agent_id.\r\n *\r\n * @example \"qa_agent\", \"customer_support_bot\", \"data_processor\"\r\n */\r\n agentName: string;\r\n\r\n /**\r\n * Human-readable display name (optional, mutable).\r\n * This can be changed via the updateAgentDisplayName API without affecting the agent_id.\r\n *\r\n * @example \"QA Agent - Production\", \"Customer Support Bot v2\"\r\n */\r\n agentDisplayName?: string;\r\n}\r\n\r\n/**\r\n * Resolved agent configuration with computed agent_id.\r\n *\r\n * @internal\r\n */\r\ninterface ResolvedAgentConfig {\r\n agentId: string;\r\n agentName: string;\r\n agentDisplayName?: string;\r\n projectId: string;\r\n}\r\n\r\n/**\r\n * Maximum length for agent names.\r\n */\r\nconst MAX_AGENT_NAME_LENGTH = 255;\r\n\r\n/**\r\n * Generates a deterministic agent_id using BLAKE2b-64.\r\n *\r\n * Formula: `agent_id = BLAKE2b-64(agent_name + project_id)` = 16-character hex string\r\n *\r\n * Design Decision: Include projectId in hash for transfer safety.\r\n * When projects transfer between organizations, projectId stays constant,\r\n * so agent_id remains stable across transfers.\r\n *\r\n * @param agentName - Agent name (immutable identifier)\r\n * @param projectId - Project ID\r\n * @returns 16-character hex string (64 bits)\r\n * @internal\r\n */\r\nfunction generateAgentId(agentName: string, projectId: string): string {\r\n const logger = getGlobalLogger();\r\n\r\n // Validate inputs\r\n if (!agentName || typeof agentName !== \"string\") {\r\n throw new Error(\"agentName must be a non-empty string\");\r\n }\r\n if (!projectId || typeof projectId !== \"string\") {\r\n throw new Error(\"projectId must be a non-empty string\");\r\n }\r\n\r\n // Truncate if too long\r\n let name = agentName.trim();\r\n if (name.length > MAX_AGENT_NAME_LENGTH) {\r\n logger.warn(\r\n `agentName too long (${name.length} chars), truncated to ${MAX_AGENT_NAME_LENGTH} characters`,\r\n );\r\n name = name.slice(0, MAX_AGENT_NAME_LENGTH);\r\n }\r\n\r\n // Generate BLAKE2b-64 hash (8 bytes = 16 hex chars)\r\n const agentId = blake2b64(name, projectId.trim());\r\n\r\n logger.debug(`[AGENT_ID] Generated: ${agentId} from agent_name: ${name}`);\r\n\r\n return agentId;\r\n}\r\n\r\n/**\r\n * BLAKE2b-64 hash implementation (8 bytes = 16 hex characters).\r\n *\r\n * Uses @noble/hashes for true BLAKE2b with configurable digest size.\r\n * This matches Python's hashlib.blake2b(digest_size=8) exactly.\r\n *\r\n * @param agentName - First input to hash\r\n * @param projectId - Second input to hash\r\n * @returns 16-character hex string\r\n * @internal\r\n */\r\nfunction blake2b64(agentName: string, projectId: string): string {\r\n // Combine inputs as in Python SDK: hasher.update(agent_name); hasher.update(project_id)\r\n const combined = new TextEncoder().encode(agentName + projectId);\r\n\r\n // BLAKE2b with 8-byte (64-bit) output - matches Python's digest_size=8\r\n const hash = blake2b(combined, { dkLen: 8 });\r\n\r\n return bytesToHex(hash);\r\n}\r\n\r\n/**\r\n * Validates and resolves agent configuration with a known projectId.\r\n *\r\n * @param config - Agent configuration to validate and resolve\r\n * @param projectId - The project ID to use (either from config or auto-fetched)\r\n * @returns Resolved agent configuration with computed agent_id\r\n * @throws {Error} If configuration is invalid\r\n * @internal\r\n */\r\nfunction resolveAgentConfigWithProjectId(\r\n config: AgentConfig,\r\n projectId: string,\r\n): ResolvedAgentConfig {\r\n if (!projectId || !projectId.trim()) {\r\n throw new Error(\"projectId is required\");\r\n }\r\n\r\n if (!config.agentName || !config.agentName.trim()) {\r\n throw new Error(\"agentName is required\");\r\n }\r\n\r\n // Generate deterministic agent_id using BLAKE2b-64\r\n const agentId = generateAgentId(\r\n config.agentName.trim(),\r\n projectId.trim(),\r\n );\r\n\r\n return {\r\n agentId,\r\n agentName: config.agentName.trim(),\r\n agentDisplayName: config.agentDisplayName?.trim(),\r\n projectId: projectId.trim(),\r\n };\r\n}\r\n\r\n/**\r\n * Function type for masking sensitive data in spans before export.\r\n *\r\n * @param params - Object containing the data to be masked\r\n * @param params.data - The data that should be masked\r\n * @returns The masked data (can be of any type)\r\n *\r\n * @example\r\n * ```typescript\r\n * const maskFunction: MaskFunction = ({ data }) => {\r\n * if (typeof data === 'string') {\r\n * return data.replace(/password=\\w+/g, 'password=***');\r\n * }\r\n * return data;\r\n * };\r\n * ```\r\n *\r\n * @public\r\n */\r\nexport type MaskFunction = (params: { data: any }) => any;\r\n\r\n/**\r\n * Function type for determining whether a span should be exported to AntsPlatform.\r\n *\r\n * @param params - Object containing the span to evaluate\r\n * @param params.otelSpan - The OpenTelemetry span to evaluate\r\n * @returns `true` if the span should be exported, `false` otherwise\r\n *\r\n * @example\r\n * ```typescript\r\n * const shouldExportSpan: ShouldExportSpan = ({ otelSpan }) => {\r\n * // Only export spans that took longer than 100ms\r\n * return otelSpan.duration[0] * 1000 + otelSpan.duration[1] / 1000000 > 100;\r\n * };\r\n * ```\r\n *\r\n * @public\r\n */\r\nexport type ShouldExportSpan = (params: { otelSpan: ReadableSpan }) => boolean;\r\n\r\n/**\r\n * Configuration parameters for the AntsPlatformSpanProcessor.\r\n *\r\n * @public\r\n */\r\nexport interface AntsPlatformSpanProcessorParams {\r\n /**\r\n * Custom OpenTelemetry span exporter. If not provided, a default OTLP exporter will be used.\r\n */\r\n exporter?: SpanExporter;\r\n\r\n /**\r\n * Ants Platform public API key. Can also be set via ANTS_PLATFORM_PUBLIC_KEY environment variable.\r\n */\r\n publicKey?: string;\r\n\r\n /**\r\n * Ants Platform secret API key. Can also be set via ANTS_PLATFORM_SECRET_KEY environment variable.\r\n */\r\n secretKey?: string;\r\n\r\n /**\r\n * AntsPlatform instance base URL. Can also be set via ANTS_PLATFORM_BASE_URL environment variable.\r\n * @defaultValue \"https://api.ants-platform.com\"\r\n */\r\n baseUrl?: string;\r\n\r\n /**\r\n * Number of spans to batch before flushing. Can also be set via ANTS_PLATFORM_FLUSH_AT environment variable.\r\n */\r\n flushAt?: number;\r\n\r\n /**\r\n * Flush interval in seconds. Can also be set via ANTS_PLATFORM_FLUSH_INTERVAL environment variable.\r\n */\r\n flushInterval?: number;\r\n\r\n /**\r\n * Function to mask sensitive data in spans before export.\r\n */\r\n mask?: MaskFunction;\r\n\r\n /**\r\n * Function to determine whether a span should be exported to AntsPlatform.\r\n */\r\n shouldExportSpan?: ShouldExportSpan;\r\n\r\n /**\r\n * Environment identifier for the traces. Can also be set via ANTS_PLATFORM_TRACING_ENVIRONMENT environment variable.\r\n */\r\n environment?: string;\r\n\r\n /**\r\n * Release identifier for the traces. Can also be set via ANTS_PLATFORM_RELEASE environment variable.\r\n */\r\n release?: string;\r\n\r\n /**\r\n * Request timeout in seconds. Can also be set via ANTS_PLATFORM_TIMEOUT environment variable.\r\n * @defaultValue 5\r\n */\r\n timeout?: number;\r\n\r\n /**\r\n * Additional HTTP headers to include with requests.\r\n */\r\n additionalHeaders?: Record<string, string>;\r\n\r\n /**\r\n * Span export mode to use.\r\n *\r\n * - **batched**: Recommended for production environments with long-running processes.\r\n * Spans are batched and exported in groups for optimal performance.\r\n * - **immediate**: Recommended for short-lived environments such as serverless functions.\r\n * Spans are exported immediately to prevent data loss when the process terminates / is frozen.\r\n *\r\n * @defaultValue \"batched\"\r\n */\r\n exportMode?: \"immediate\" | \"batched\";\r\n\r\n /**\r\n * Agent configuration for AI Command Center integration.\r\n *\r\n * When provided, all spans will be automatically tagged with agent identifiers,\r\n * enabling agent-level tracking and analytics in the AI Command Center.\r\n *\r\n * The project_id is automatically fetched from the API using your credentials,\r\n * matching the behavior of the Python SDK.\r\n *\r\n * @example\r\n * ```typescript\r\n * new AntsPlatformSpanProcessor({\r\n * publicKey: 'pk_...',\r\n * secretKey: 'sk_...',\r\n * agent: {\r\n * agentName: 'qa_agent',\r\n * agentDisplayName: 'QA Agent - Production' // optional\r\n * }\r\n * });\r\n * ```\r\n */\r\n agent?: AgentConfig;\r\n\r\n /**\r\n * **Internal/Testing Only**: Override project ID for testing purposes.\r\n * When set, bypasses the API fetch and uses this value directly.\r\n * This should NOT be used in production code.\r\n *\r\n * @internal\r\n */\r\n _testProjectId?: string;\r\n}\r\n\r\n/**\r\n * OpenTelemetry span processor for sending spans to AntsPlatform.\r\n *\r\n * This processor extends the standard BatchSpanProcessor to provide:\r\n * - Automatic batching and flushing of spans to AntsPlatform\r\n * - Media content extraction and upload from base64 data URIs\r\n * - Data masking capabilities for sensitive information\r\n * - Conditional span export based on custom logic\r\n * - Environment and release tagging\r\n *\r\n * @example\r\n * ```typescript\r\n * import { NodeSDK } from '@opentelemetry/sdk-node';\r\n * import { AntsPlatformSpanProcessor } from '@antsplatform/otel';\r\n *\r\n * const sdk = new NodeSDK({\r\n * spanProcessors: [\r\n * new AntsPlatformSpanProcessor({\r\n * publicKey: 'pk_...',\r\n * secretKey: 'sk_...',\r\n * baseUrl: 'https://api.ants-platform.com',\r\n * environment: 'production',\r\n * mask: ({ data }) => {\r\n * // Mask sensitive data\r\n * return data.replace(/api_key=\\w+/g, 'api_key=***');\r\n * }\r\n * })\r\n * ]\r\n * });\r\n *\r\n * sdk.start();\r\n * ```\r\n *\r\n * @public\r\n */\r\nexport class AntsPlatformSpanProcessor implements SpanProcessor {\r\n private pendingEndedSpans: Set<Promise<void>> = new Set();\r\n\r\n private publicKey?: string;\r\n private baseUrl?: string;\r\n private environment?: string;\r\n private release?: string;\r\n private mask?: MaskFunction;\r\n private shouldExportSpan?: ShouldExportSpan;\r\n private apiClient: AntsPlatformAPIClient;\r\n private processor: SpanProcessor;\r\n private mediaService: MediaService;\r\n private resolvedAgentConfig?: ResolvedAgentConfig;\r\n private pendingAgentConfig?: AgentConfig;\r\n private agentConfigPromise?: Promise<void>;\r\n private cachedProjectId?: string;\r\n private testProjectId?: string;\r\n\r\n /**\r\n * Creates a new AntsPlatformSpanProcessor instance.\r\n *\r\n * @param params - Configuration parameters for the processor\r\n *\r\n * @example\r\n * ```typescript\r\n * const processor = new AntsPlatformSpanProcessor({\r\n * publicKey: 'pk_...',\r\n * secretKey: 'sk_...',\r\n * environment: 'staging',\r\n * flushAt: 10,\r\n * flushInterval: 2,\r\n * mask: ({ data }) => {\r\n * // Custom masking logic\r\n * return typeof data === 'string'\r\n * ? data.replace(/secret_\\w+/g, 'secret_***')\r\n * : data;\r\n * },\r\n * shouldExportSpan: ({ otelSpan }) => {\r\n * // Only export spans from specific services\r\n * return otelSpan.name.startsWith('my-service');\r\n * }\r\n * });\r\n * ```\r\n */\r\n constructor(params?: AntsPlatformSpanProcessorParams) {\r\n const logger = getGlobalLogger();\r\n\r\n const publicKey = params?.publicKey ?? getEnv(\"ANTS_PLATFORM_PUBLIC_KEY\");\r\n const secretKey = params?.secretKey ?? getEnv(\"ANTS_PLATFORM_SECRET_KEY\");\r\n const baseUrl =\r\n params?.baseUrl ??\r\n getEnv(\"ANTS_PLATFORM_BASE_URL\") ??\r\n getEnv(\"ANTS_PLATFORM_BASEURL\") ?? // legacy v2\r\n \"https://api.ants-platform.com\";\r\n\r\n if (!params?.exporter && !publicKey) {\r\n logger.warn(\r\n \"No exporter configured and no public key provided in constructor or as ANTS_PLATFORM_PUBLIC_KEY env var. Span exports will fail.\",\r\n );\r\n }\r\n if (!params?.exporter && !secretKey) {\r\n logger.warn(\r\n \"No exporter configured and no secret key provided in constructor or as ANTS_PLATFORM_SECRET_KEY env var. Span exports will fail.\",\r\n );\r\n }\r\n const flushAt = params?.flushAt ?? getEnv(\"ANTS_PLATFORM_FLUSH_AT\");\r\n const flushIntervalSeconds =\r\n params?.flushInterval ?? getEnv(\"ANTS_PLATFORM_FLUSH_INTERVAL\");\r\n\r\n const authHeaderValue = base64Encode(`${publicKey}:${secretKey}`);\r\n const timeoutSeconds =\r\n params?.timeout ?? Number(getEnv(\"ANTS_PLATFORM_TIMEOUT\") ?? 5);\r\n\r\n const exporter =\r\n params?.exporter ??\r\n new OTLPTraceExporter({\r\n url: `${baseUrl}/api/public/otel/v1/traces`,\r\n headers: {\r\n Authorization: `Basic ${authHeaderValue}`,\r\n x_antsPlatform_sdk_name: \"javascript\",\r\n x_antsPlatform_sdk_version: ANTS_PLATFORM_SDK_VERSION,\r\n x_antsPlatform_public_key: publicKey ?? \"<missing>\",\r\n ...params?.additionalHeaders,\r\n },\r\n timeoutMillis: timeoutSeconds * 1_000,\r\n });\r\n\r\n this.processor =\r\n params?.exportMode === \"immediate\"\r\n ? new SimpleSpanProcessor(exporter)\r\n : new BatchSpanProcessor(exporter, {\r\n maxExportBatchSize: flushAt ? Number(flushAt) : undefined,\r\n scheduledDelayMillis: flushIntervalSeconds\r\n ? Number(flushIntervalSeconds) * 1_000\r\n : undefined,\r\n });\r\n\r\n this.publicKey = publicKey;\r\n this.baseUrl = baseUrl;\r\n this.environment =\r\n params?.environment ?? getEnv(\"ANTS_PLATFORM_TRACING_ENVIRONMENT\");\r\n this.release = params?.release ?? getEnv(\"ANTS_PLATFORM_RELEASE\");\r\n this.mask = params?.mask;\r\n this.shouldExportSpan = params?.shouldExportSpan;\r\n this.apiClient = new AntsPlatformAPIClient({\r\n baseUrl: this.baseUrl,\r\n username: this.publicKey,\r\n password: secretKey,\r\n xAntsPlatformPublicKey: this.publicKey,\r\n xAntsPlatformSdkVersion: ANTS_PLATFORM_SDK_VERSION,\r\n xAntsPlatformSdkName: \"javascript\",\r\n environment: \"\", // noop as baseUrl is set\r\n headers: params?.additionalHeaders,\r\n });\r\n\r\n this.mediaService = new MediaService({ apiClient: this.apiClient });\r\n\r\n // Store test project ID if provided (for testing only)\r\n this.testProjectId = params?._testProjectId;\r\n\r\n // If agent config is provided, initiate async projectId fetch\r\n if (params?.agent) {\r\n this.pendingAgentConfig = params.agent;\r\n this.agentConfigPromise = this.initializeAgentConfig(params.agent);\r\n }\r\n\r\n logger.debug(\"Initialized AntsPlatformSpanProcessor with params:\", {\r\n publicKey,\r\n baseUrl,\r\n environment: this.environment,\r\n release: this.release,\r\n timeoutSeconds,\r\n flushAt,\r\n flushIntervalSeconds,\r\n hasAgentConfig: !!params?.agent,\r\n });\r\n }\r\n\r\n /**\r\n * Fetches project_id from the API and resolves agent configuration.\r\n * This matches the Java/Python SDK behavior where project_id is always fetched from the API.\r\n *\r\n * @param config - Agent configuration with agentName and optional agentDisplayName\r\n * @internal\r\n */\r\n private async initializeAgentConfig(config: AgentConfig): Promise<void> {\r\n const logger = getGlobalLogger();\r\n\r\n try {\r\n // Use test project ID if provided (for testing only), otherwise fetch from API\r\n let projectId: string | null = null;\r\n\r\n if (this.testProjectId) {\r\n logger.debug(`[AGENT_CONFIG] Using test projectId: ${this.testProjectId}`);\r\n projectId = this.testProjectId;\r\n } else {\r\n // Always fetch project_id from API (matches Java/Python SDK behavior)\r\n projectId = await this.fetchProjectId();\r\n }\r\n\r\n if (!projectId) {\r\n logger.error(\r\n \"[AGENT_CONFIG] Failed to fetch project_id from API. Agent attributes will not be added to spans.\",\r\n );\r\n return;\r\n }\r\n\r\n logger.debug(`[AGENT_CONFIG] Using projectId: ${projectId}`);\r\n\r\n // Resolve agent config with the fetched projectId\r\n this.resolvedAgentConfig = resolveAgentConfigWithProjectId(config, projectId);\r\n this.cachedProjectId = projectId;\r\n\r\n logger.info(\"[AGENT_CONFIG] Successfully initialized agent configuration:\", {\r\n agentId: this.resolvedAgentConfig.agentId,\r\n agentName: this.resolvedAgentConfig.agentName,\r\n agentDisplayName: this.resolvedAgentConfig.agentDisplayName,\r\n projectId: this.resolvedAgentConfig.projectId,\r\n });\r\n } catch (error) {\r\n logger.error(\r\n `[AGENT_CONFIG] Failed to initialize agent configuration: ${error instanceof Error ? error.message : String(error)}`,\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Fetches the project_id from the Ants Platform API.\r\n * This matches the Python SDK behavior: GET /api/public/projects -> data[0].id\r\n *\r\n * @returns Promise resolving to the project ID, or null if unavailable\r\n * @internal\r\n */\r\n private async fetchProjectId(): Promise<string | null> {\r\n const logger = getGlobalLogger();\r\n\r\n try {\r\n const response = await this.apiClient.projects.get();\r\n\r\n if (response.data && response.data.length > 0) {\r\n const projectId = response.data[0].id;\r\n logger.debug(`[PROJECT_ID] Fetched from API: ${projectId}`);\r\n return projectId;\r\n }\r\n\r\n logger.warn(\"[PROJECT_ID] No projects found in API response\");\r\n return null;\r\n } catch (error) {\r\n logger.warn(\r\n `[PROJECT_ID] Failed to fetch from API: ${error instanceof Error ? error.message : String(error)}`,\r\n );\r\n return null;\r\n }\r\n }\r\n\r\n private get logger(): Logger {\r\n return getGlobalLogger();\r\n }\r\n\r\n /**\r\n * Called when a span is started. Adds environment, release, and agent attributes to the span.\r\n *\r\n * @param span - The span that was started\r\n * @param parentContext - The parent context\r\n *\r\n * @override\r\n */\r\n public onStart(span: Span, parentContext: any): void {\r\n const attributes: Record<string, string | undefined> = {\r\n [AntsPlatformOtelSpanAttributes.ENVIRONMENT]: this.environment,\r\n [AntsPlatformOtelSpanAttributes.RELEASE]: this.release,\r\n };\r\n\r\n // Add agent attributes if configured and resolved\r\n if (this.resolvedAgentConfig) {\r\n attributes[AntsPlatformOtelSpanAttributes.AGENT_ID] =\r\n this.resolvedAgentConfig.agentId;\r\n attributes[AntsPlatformOtelSpanAttributes.AGENT_NAME] =\r\n this.resolvedAgentConfig.agentName;\r\n attributes[AntsPlatformOtelSpanAttributes.AGENT_DISPLAY_NAME] =\r\n this.resolvedAgentConfig.agentDisplayName;\r\n attributes[AntsPlatformOtelSpanAttributes.PROJECT_ID] =\r\n this.resolvedAgentConfig.projectId;\r\n } else if (this.pendingAgentConfig) {\r\n // Agent config was requested but not yet resolved (projectId still being fetched)\r\n this.logger.debug(\r\n \"[AGENT_CONFIG] Agent config not yet resolved, span will not have agent attributes. \" +\r\n \"This may happen for spans created before projectId fetch completes.\",\r\n );\r\n }\r\n\r\n span.setAttributes(attributes);\r\n\r\n return this.processor.onStart(span, parentContext);\r\n }\r\n\r\n /**\r\n * Called when a span ends. Processes the span for export to AntsPlatform.\r\n *\r\n * This method:\r\n * 1. Checks if the span should be exported using the shouldExportSpan function\r\n * 2. Applies data masking to sensitive attributes\r\n * 3. Handles media content extraction and upload\r\n * 4. Logs span details in debug mode\r\n * 5. Passes the span to the parent processor for export\r\n *\r\n * @param span - The span that ended\r\n *\r\n * @override\r\n */\r\n public onEnd(span: ReadableSpan): void {\r\n const processEndedSpanPromise = this.processEndedSpan(span).catch((err) => {\r\n this.logger.error(err);\r\n });\r\n\r\n // Enqueue this export to the pending list so it can be flushed by the user.\r\n this.pendingEndedSpans.add(processEndedSpanPromise);\r\n\r\n void processEndedSpanPromise.finally(() =>\r\n this.pendingEndedSpans.delete(processEndedSpanPromise),\r\n );\r\n }\r\n\r\n private async flush(): Promise<void> {\r\n // Wait for agent config to be resolved if pending\r\n if (this.agentConfigPromise) {\r\n await this.agentConfigPromise;\r\n }\r\n await Promise.all(Array.from(this.pendingEndedSpans));\r\n await this.mediaService.flush();\r\n }\r\n\r\n /**\r\n * Forces an immediate flush of all pending spans and media uploads.\r\n *\r\n * @returns Promise that resolves when all pending operations are complete\r\n *\r\n * @override\r\n */\r\n public async forceFlush(): Promise<void> {\r\n await this.flush();\r\n\r\n return this.processor.forceFlush();\r\n }\r\n\r\n /**\r\n * Gracefully shuts down the processor, ensuring all pending operations are completed.\r\n *\r\n * @returns Promise that resolves when shutdown is complete\r\n *\r\n * @override\r\n */\r\n public async shutdown(): Promise<void> {\r\n await this.flush();\r\n\r\n return this.processor.shutdown();\r\n }\r\n\r\n private async processEndedSpan(span: ReadableSpan) {\r\n if (this.shouldExportSpan) {\r\n try {\r\n if (this.shouldExportSpan({ otelSpan: span }) === false) return;\r\n } catch (err) {\r\n this.logger.error(\r\n \"ShouldExportSpan failed with error. Excluding span. Error: \",\r\n err,\r\n );\r\n\r\n return;\r\n }\r\n }\r\n\r\n this.applyMaskInPlace(span);\r\n await this.mediaService.process(span);\r\n\r\n this.logger.debug(\r\n `Processed span:\\n${JSON.stringify(\r\n {\r\n name: span.name,\r\n traceId: span.spanContext().traceId,\r\n spanId: span.spanContext().spanId,\r\n parentSpanId: span.parentSpanContext?.spanId ?? null,\r\n attributes: span.attributes,\r\n startTime: new Date(hrTimeToMilliseconds(span.startTime)),\r\n endTime: new Date(hrTimeToMilliseconds(span.endTime)),\r\n durationMs: hrTimeToMilliseconds(span.duration),\r\n kind: span.kind,\r\n status: span.status,\r\n resource: span.resource.attributes,\r\n instrumentationScope: span.instrumentationScope,\r\n },\r\n null,\r\n 2,\r\n )}`,\r\n );\r\n\r\n this.processor.onEnd(span);\r\n }\r\n private applyMaskInPlace(span: ReadableSpan): void {\r\n const maskCandidates = [\r\n AntsPlatformOtelSpanAttributes.OBSERVATION_INPUT,\r\n AntsPlatformOtelSpanAttributes.TRACE_INPUT,\r\n AntsPlatformOtelSpanAttributes.OBSERVATION_OUTPUT,\r\n AntsPlatformOtelSpanAttributes.TRACE_OUTPUT,\r\n AntsPlatformOtelSpanAttributes.OBSERVATION_METADATA,\r\n AntsPlatformOtelSpanAttributes.TRACE_METADATA,\r\n ];\r\n\r\n for (const maskCandidate of maskCandidates) {\r\n if (maskCandidate in span.attributes) {\r\n span.attributes[maskCandidate] = this.applyMask(\r\n span.attributes[maskCandidate],\r\n );\r\n }\r\n }\r\n }\r\n\r\n private applyMask<T>(data: T): T | string {\r\n if (!this.mask) return data;\r\n\r\n try {\r\n return this.mask({ data });\r\n } catch (err) {\r\n this.logger.warn(\r\n `Applying mask function failed due to error, fully masking property. Error: ${err}`,\r\n );\r\n\r\n return \"<fully masked due to failed mask function>\";\r\n }\r\n }\r\n}\r\n","import {\r\n AntsPlatformAPIClient,\r\n AntsPlatformMedia,\r\n AntsPlatformOtelSpanAttributes,\r\n Logger,\r\n base64ToBytes,\r\n getGlobalLogger,\r\n} from \"@antsplatform/core\";\r\nimport { ReadableSpan } from \"@opentelemetry/sdk-trace-base\";\r\n\r\nexport class MediaService {\r\n private pendingMediaUploads: Set<Promise<void>> = new Set();\r\n private apiClient: AntsPlatformAPIClient;\r\n\r\n constructor(params: { apiClient: AntsPlatformAPIClient }) {\r\n this.apiClient = params.apiClient;\r\n }\r\n\r\n get logger(): Logger {\r\n return getGlobalLogger();\r\n }\r\n\r\n public async flush(): Promise<void> {\r\n await Promise.all(Array.from(this.pendingMediaUploads));\r\n }\r\n\r\n public async process(span: ReadableSpan) {\r\n const mediaAttributes = [\r\n AntsPlatformOtelSpanAttributes.OBSERVATION_INPUT,\r\n AntsPlatformOtelSpanAttributes.TRACE_INPUT,\r\n AntsPlatformOtelSpanAttributes.OBSERVATION_OUTPUT,\r\n AntsPlatformOtelSpanAttributes.TRACE_OUTPUT,\r\n AntsPlatformOtelSpanAttributes.OBSERVATION_METADATA,\r\n AntsPlatformOtelSpanAttributes.TRACE_METADATA,\r\n ];\r\n\r\n for (const mediaAttribute of mediaAttributes) {\r\n const mediaRelevantAttributeKeys = Object.keys(span.attributes).filter(\r\n (attributeName) => attributeName.startsWith(mediaAttribute),\r\n );\r\n\r\n for (const key of mediaRelevantAttributeKeys) {\r\n const value = span.attributes[key];\r\n\r\n if (typeof value !== \"string\") {\r\n this.logger.warn(\r\n `Span attribute ${mediaAttribute} is not a stringified object. Skipping media handling.`,\r\n );\r\n\r\n continue;\r\n }\r\n\r\n // Find media base64 data URI\r\n let mediaReplacedValue = value;\r\n const regex = /data:[^;]+;base64,[A-Za-z0-9+/]+=*/g;\r\n const foundMedia = [...new Set(value.match(regex) ?? [])];\r\n\r\n if (foundMedia.length === 0) continue;\r\n\r\n for (const mediaDataUri of foundMedia) {\r\n // For each media, create media tag and initiate upload\r\n const media = new AntsPlatformMedia({\r\n base64DataUri: mediaDataUri,\r\n source: \"base64_data_uri\",\r\n });\r\n\r\n const antsPlatformMediaTag = await media.getTag();\r\n\r\n if (!antsPlatformMediaTag) {\r\n this.logger.warn(\r\n \"Failed to create AntsPlatform media tag. Skipping media item.\",\r\n );\r\n\r\n continue;\r\n }\r\n\r\n this.scheduleUpload({\r\n span,\r\n media,\r\n field: mediaAttribute.includes(\"input\")\r\n ? \"input\"\r\n : mediaAttribute.includes(\"output\")\r\n ? \"output\"\r\n : \"metadata\", // todo: make more robust\r\n });\r\n\r\n // Replace original attribute with media escaped attribute\r\n mediaReplacedValue = mediaReplacedValue.replaceAll(\r\n mediaDataUri,\r\n antsPlatformMediaTag,\r\n );\r\n }\r\n\r\n span.attributes[key] = mediaReplacedValue;\r\n }\r\n }\r\n\r\n // Handle media from Vercel AI SDK\r\n if (span.instrumentationScope.name === \"ai\") {\r\n const aiSDKMediaAttributes = [\"ai.prompt.messages\", \"ai.prompt\"];\r\n\r\n for (const mediaAttribute of aiSDKMediaAttributes) {\r\n const value = span.attributes[mediaAttribute];\r\n\r\n if (!value || typeof value !== \"string\") {\r\n continue;\r\n }\r\n\r\n // Find media base64 data URI\r\n let mediaReplacedValue = value;\r\n\r\n try {\r\n const parsed = JSON.parse(value);\r\n\r\n if (Array.isArray(parsed)) {\r\n for (const message of parsed) {\r\n if (Array.isArray(message[\"content\"])) {\r\n const contentParts = message[\"content\"];\r\n\r\n for (const part of contentParts) {\r\n if (part[\"type\"] === \"file\") {\r\n let base64Content: string | null = null;\r\n // FilePart\r\n if (part[\"data\"] != null && part[\"mediaType\"] != null) {\r\n base64Content = part[\"data\"];\r\n }\r\n\r\n //ImagePart\r\n if (part[\"image\"] != null && part[\"mediaType\"] != null) {\r\n base64Content = part[\"image\"];\r\n }\r\n\r\n if (!base64Content) continue;\r\n\r\n const media = new AntsPlatformMedia({\r\n contentType: part[\"mediaType\"],\r\n contentBytes: base64ToBytes(base64Content),\r\n source: \"bytes\",\r\n });\r\n\r\n const antsPlatformMediaTag = await media.getTag();\r\n\r\n if (!antsPlatformMediaTag) {\r\n this.logger.warn(\r\n \"Failed to create AntsPlatform media tag. Skipping media item.\",\r\n );\r\n\r\n continue;\r\n }\r\n\r\n this.scheduleUpload({\r\n span,\r\n media,\r\n field: \"input\",\r\n });\r\n\r\n // Replace original attribute with media escaped attribute\r\n mediaReplacedValue = mediaReplacedValue.replaceAll(\r\n base64Content,\r\n antsPlatformMediaTag,\r\n );\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n span.attributes[mediaAttribute] = mediaReplacedValue;\r\n } catch (err) {\r\n this.logger.warn(\r\n `Failed to handle media for AI SDK attribute ${mediaAttribute} for span ${span.spanContext().spanId}`,\r\n err,\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n private scheduleUpload(params: {\r\n span: ReadableSpan;\r\n field: string;\r\n media: AntsPlatformMedia;\r\n }) {\r\n const { span, field, media } = params;\r\n\r\n const uploadPromise: Promise<void> = this.handleUpload({\r\n media,\r\n traceId: span.spanContext().traceId,\r\n observationId: span.spanContext().spanId,\r\n field,\r\n }).catch((err) => {\r\n this.logger.error(\"Media upload failed with error: \", err);\r\n });\r\n\r\n this.pendingMediaUploads.add(uploadPromise);\r\n\r\n uploadPromise.finally(() => {\r\n this.pendingMediaUploads.delete(uploadPromise);\r\n });\r\n }\r\n\r\n private async handleUpload({\r\n media,\r\n traceId,\r\n observationId,\r\n field,\r\n }: {\r\n media: AntsPlatformMedia;\r\n traceId: string;\r\n observationId?: string;\r\n field: string;\r\n }): Promise<void> {\r\n try {\r\n const contentSha256Hash = await media.getSha256Hash();\r\n\r\n if (\r\n !media.contentLength ||\r\n !media._contentType ||\r\n !contentSha256Hash ||\r\n !media._contentBytes\r\n ) {\r\n return;\r\n }\r\n\r\n const { uploadUrl, mediaId } = await this.apiClient.media.getUploadUrl({\r\n contentLength: media.contentLength,\r\n traceId,\r\n observationId,\r\n field,\r\n contentType: media._contentType,\r\n sha256Hash: contentSha256Hash,\r\n });\r\n\r\n if (!uploadUrl) {\r\n this.logger.debug(\r\n `Media status: Media with ID ${mediaId} already uploaded. Skipping duplicate upload.`,\r\n );\r\n\r\n return;\r\n }\r\n\r\n const clientSideMediaId = await media.getId();\r\n if (clientSideMediaId !== mediaId) {\r\n this.logger.error(\r\n `Media integrity error: Media ID mismatch between SDK (${clientSideMediaId}) and Server (${mediaId}). Upload cancelled. Please check media ID generation logic.`,\r\n );\r\n\r\n return;\r\n }\r\n\r\n this.logger.debug(`Uploading media ${mediaId}...`);\r\n\r\n const startTime = Date.now();\r\n\r\n const uploadResponse = await this.uploadWithBackoff({\r\n uploadUrl,\r\n contentBytes: media._contentBytes,\r\n contentType: media._contentType,\r\n contentSha256Hash: contentSha256Hash,\r\n maxRetries: 3,\r\n baseDelay: 1000,\r\n });\r\n\r\n if (!uploadResponse) {\r\n throw Error(\"Media upload process failed\");\r\n }\r\n\r\n await this.apiClient.media.patch(mediaId, {\r\n uploadedAt: new Date().toISOString(),\r\n uploadHttpStatus: uploadResponse.status,\r\n uploadHttpError: await uploadResponse.text(),\r\n uploadTimeMs: Date.now() - startTime,\r\n });\r\n\r\n this.logger.debug(`Media upload status reported for ${mediaId}`);\r\n } catch (err) {\r\n this.logger.error(`Error processing media item: ${err}`);\r\n }\r\n }\r\n\r\n private async uploadWithBackoff(params: {\r\n uploadUrl: string;\r\n contentType: string;\r\n contentSha256Hash: string;\r\n contentBytes: Uint8Array;\r\n maxRetries: number;\r\n baseDelay: number;\r\n }) {\r\n const {\r\n uploadUrl,\r\n contentType,\r\n contentSha256Hash,\r\n contentBytes,\r\n maxRetries,\r\n baseDelay,\r\n } = params;\r\n\r\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\r\n try {\r\n const uploadResponse = await fetch(uploadUrl, {\r\n method: \"PUT\",\r\n body: contentBytes,\r\n headers: {\r\n \"Content-Type\": contentType,\r\n \"x-amz-checksum-sha256\": contentSha256Hash,\r\n \"x-ms-blob-type\": \"BlockBlob\",\r\n },\r\n });\r\n\r\n if (\r\n attempt < maxRetries &&\r\n uploadResponse.status !== 200 &&\r\n uploadResponse.status !== 201\r\n ) {\r\n throw new Error(`Upload failed with status ${uploadResponse.status}`);\r\n }\r\n\r\n return uploadResponse;\r\n } catch (e) {\r\n if (attempt === maxRetries) {\r\n throw e;\r\n }\r\n\r\n const delay = baseDelay * Math.pow(2, attempt);\r\n const jitter = Math.random() * 1000;\r\n\r\n await new Promise((resolve) => setTimeout(resolve, delay + jitter));\r\n }\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["getGlobalLogger","AntsPlatformAPIClient","AntsPlatformOtelSpanAttributes","getGlobalLogger","AntsPlatformAPIClient","AntsPlatformOtelSpanAttributes"]} |
+3
-2
| { | ||
| "name": "@antsplatform/otel", | ||
| "version": "1.0.11", | ||
| "version": "1.0.12", | ||
| "author": "Ants Platform", | ||
@@ -27,3 +27,4 @@ "license": "MIT", | ||
| "dependencies": { | ||
| "@antsplatform/core": "^1.0.11" | ||
| "@noble/hashes": "^2.0.1", | ||
| "@antsplatform/core": "1.0.12" | ||
| }, | ||
@@ -30,0 +31,0 @@ "peerDependencies": { |
+0
-0
@@ -0,0 +0,0 @@  |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
166713
37.32%1542
27.75%6
20%10
400%+ Added
Updated