@analytix402/sdk 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -106,6 +106,52 @@ These paths are excluded from tracking by default:
106
106
 
107
107
  `/health`, `/healthz`, `/ready`, `/readyz`, `/live`, `/livez`, `/metrics`, `/favicon.ico`, `/.well-known`
108
108
 
109
+ ## Agent Tracking
110
+
111
+ Track AI agent health, tasks, and LLM usage:
112
+
113
+ ```typescript
114
+ import { createAnalytix402Client } from '@analytix402/sdk';
115
+
116
+ const client = createAnalytix402Client({
117
+ apiKey: 'ax_live_xxxxx',
118
+ agentId: 'my-trading-agent',
119
+ agentName: 'Trading Bot',
120
+ });
121
+
122
+ // Send heartbeats to show agent is alive
123
+ setInterval(() => {
124
+ client.heartbeat('healthy', { uptime: process.uptime() });
125
+ }, 30000);
126
+
127
+ // Track tasks with automatic duration measurement
128
+ const task = client.startTask('rebalance-portfolio-42');
129
+ try {
130
+ await doRebalance();
131
+ task.end(true, { positions: 12 });
132
+ } catch (err) {
133
+ task.end(false, { error: err.message });
134
+ }
135
+
136
+ // Track LLM token usage
137
+ client.trackLLM({
138
+ model: 'claude-sonnet-4-5-20250929',
139
+ provider: 'anthropic',
140
+ inputTokens: 1500,
141
+ outputTokens: 300,
142
+ costUsd: 0.012,
143
+ durationMs: 820,
144
+ taskId: 'rebalance-portfolio-42',
145
+ });
146
+
147
+ // Report task outcomes directly
148
+ client.reportOutcome('task-123', true, {
149
+ durationMs: 2400,
150
+ cost: 0.05,
151
+ metadata: { pnl: 12.50 },
152
+ });
153
+ ```
154
+
109
155
  ## Manual Tracking
110
156
 
111
157
  For non-Express frameworks or custom tracking:
@@ -140,6 +186,29 @@ await client.flush();
140
186
  await client.shutdown();
141
187
  ```
142
188
 
189
+ ## Proxy URL Integration
190
+
191
+ Instead of the SDK middleware, you can route API calls through Analytix402's proxy:
192
+
193
+ ```
194
+ https://analytix402.com/api/p/{your-api-slug}/your/endpoint
195
+ ```
196
+
197
+ Pass your spend key as a header:
198
+
199
+ ```typescript
200
+ const response = await fetch(
201
+ 'https://analytix402.com/api/p/weather-api/forecast?city=NYC',
202
+ {
203
+ headers: {
204
+ 'X-Spend-Key': 'sk_live_your_spend_key',
205
+ },
206
+ }
207
+ );
208
+ ```
209
+
210
+ The proxy handles payment, analytics tracking, and circuit breaker enforcement automatically.
211
+
143
212
  ## Performance
144
213
 
145
214
  - **< 50ms overhead** per request
@@ -0,0 +1,259 @@
1
+ // src/client.ts
2
+ var SDK_NAME = "@analytix402/sdk";
3
+ var SDK_VERSION = "0.1.1";
4
+ var DEFAULT_CONFIG = {
5
+ baseUrl: "https://analytix402.com",
6
+ debug: false,
7
+ batchSize: 100,
8
+ flushInterval: 5e3,
9
+ maxRetries: 3,
10
+ maxQueueSize: 1e3,
11
+ timeout: 1e4,
12
+ excludePaths: ["/health", "/healthz", "/ready", "/metrics", "/favicon.ico"],
13
+ autoConnect: false,
14
+ heartbeatIntervalMs: 0
15
+ };
16
+ function createClient(config) {
17
+ if (!config.apiKey) {
18
+ throw new Error("Analytix402: apiKey is required");
19
+ }
20
+ if (!config.apiKey.startsWith("ax_live_") && !config.apiKey.startsWith("ax_test_")) {
21
+ console.warn("Analytix402: API key should start with ax_live_ or ax_test_");
22
+ }
23
+ const cfg = {
24
+ ...DEFAULT_CONFIG,
25
+ ...config
26
+ };
27
+ const agentId = cfg.agentId;
28
+ const queue = [];
29
+ let flushTimer = null;
30
+ let heartbeatTimer = null;
31
+ let isFlushing = false;
32
+ let isShutdown = false;
33
+ const log = (...args) => {
34
+ if (cfg.debug) {
35
+ console.log("[Analytix402]", ...args);
36
+ }
37
+ };
38
+ const warn = (...args) => {
39
+ console.warn("[Analytix402]", ...args);
40
+ };
41
+ function enqueue(event) {
42
+ if (isShutdown) {
43
+ warn("Client is shutdown, event dropped");
44
+ return;
45
+ }
46
+ if (queue.length >= cfg.maxQueueSize) {
47
+ warn(`Queue full (${cfg.maxQueueSize}), dropping oldest event`);
48
+ queue.shift();
49
+ }
50
+ queue.push({
51
+ event,
52
+ attempts: 0,
53
+ addedAt: Date.now()
54
+ });
55
+ log(`Event queued (${queue.length} in queue)`);
56
+ if (queue.length >= cfg.batchSize) {
57
+ log("Batch size reached, flushing");
58
+ flush();
59
+ } else if (!flushTimer) {
60
+ flushTimer = setTimeout(() => {
61
+ flushTimer = null;
62
+ flush();
63
+ }, cfg.flushInterval);
64
+ }
65
+ }
66
+ function track(event) {
67
+ if (agentId && !event.agentId) {
68
+ event.agentId = agentId;
69
+ }
70
+ enqueue(event);
71
+ }
72
+ function heartbeat(status = "healthy", metadata) {
73
+ if (!agentId) {
74
+ warn("heartbeat() requires agentId in config");
75
+ return;
76
+ }
77
+ const event = {
78
+ type: "heartbeat",
79
+ agentId,
80
+ status,
81
+ metadata,
82
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
83
+ };
84
+ enqueue(event);
85
+ }
86
+ function reportOutcome(taskId, success, options) {
87
+ const event = {
88
+ type: "task_outcome",
89
+ agentId,
90
+ taskId,
91
+ success,
92
+ durationMs: options?.durationMs,
93
+ cost: options?.cost,
94
+ metadata: options?.metadata,
95
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
96
+ };
97
+ enqueue(event);
98
+ }
99
+ function startTask(taskId) {
100
+ const id = taskId || `task_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
101
+ const startTime = Date.now();
102
+ return {
103
+ taskId: id,
104
+ end(success, metadata) {
105
+ const durationMs = Date.now() - startTime;
106
+ reportOutcome(id, success, { durationMs, metadata });
107
+ }
108
+ };
109
+ }
110
+ function trackLLM(usage) {
111
+ const event = {
112
+ type: "llm_usage",
113
+ agentId,
114
+ taskId: usage.taskId,
115
+ model: usage.model,
116
+ provider: usage.provider,
117
+ inputTokens: usage.inputTokens,
118
+ outputTokens: usage.outputTokens,
119
+ totalTokens: usage.inputTokens + usage.outputTokens,
120
+ costUsd: usage.costUsd,
121
+ durationMs: usage.durationMs,
122
+ metadata: usage.metadata,
123
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
124
+ };
125
+ enqueue(event);
126
+ }
127
+ async function sendBatch(events) {
128
+ if (events.length === 0) return true;
129
+ const payload = {
130
+ events,
131
+ sdk: {
132
+ name: SDK_NAME,
133
+ version: SDK_VERSION
134
+ },
135
+ sentAt: (/* @__PURE__ */ new Date()).toISOString()
136
+ };
137
+ try {
138
+ const controller = new AbortController();
139
+ const timeoutId = setTimeout(() => controller.abort(), cfg.timeout);
140
+ const response = await fetch(`${cfg.baseUrl}/api/ingest/batch`, {
141
+ method: "POST",
142
+ headers: {
143
+ "Content-Type": "application/json",
144
+ "X-API-Key": cfg.apiKey,
145
+ "User-Agent": `${SDK_NAME}/${SDK_VERSION}`
146
+ },
147
+ body: JSON.stringify(payload),
148
+ signal: controller.signal
149
+ });
150
+ clearTimeout(timeoutId);
151
+ if (!response.ok) {
152
+ const text = await response.text().catch(() => "Unknown error");
153
+ throw new Error(`HTTP ${response.status}: ${text}`);
154
+ }
155
+ log(`Sent ${events.length} events successfully`);
156
+ return true;
157
+ } catch (error) {
158
+ if (error instanceof Error && error.name === "AbortError") {
159
+ warn("Request timed out");
160
+ } else {
161
+ warn("Failed to send events:", error);
162
+ }
163
+ return false;
164
+ }
165
+ }
166
+ async function flush() {
167
+ if (isFlushing || queue.length === 0) {
168
+ return;
169
+ }
170
+ isFlushing = true;
171
+ if (flushTimer) {
172
+ clearTimeout(flushTimer);
173
+ flushTimer = null;
174
+ }
175
+ const batch = queue.splice(0, cfg.batchSize);
176
+ const events = batch.map((q) => q.event);
177
+ log(`Flushing ${events.length} events`);
178
+ const success = await sendBatch(events);
179
+ if (!success) {
180
+ const retriable = batch.filter((q) => q.attempts < cfg.maxRetries);
181
+ if (retriable.length > 0) {
182
+ log(`Re-queuing ${retriable.length} events for retry`);
183
+ for (const item of retriable.reverse()) {
184
+ item.attempts++;
185
+ queue.unshift(item);
186
+ }
187
+ const backoff = Math.min(1e3 * Math.pow(2, retriable[0].attempts), 3e4);
188
+ log(`Retry scheduled in ${backoff}ms`);
189
+ flushTimer = setTimeout(() => {
190
+ flushTimer = null;
191
+ flush();
192
+ }, backoff);
193
+ } else {
194
+ warn(`Dropped ${batch.length} events after ${cfg.maxRetries} retries`);
195
+ }
196
+ }
197
+ isFlushing = false;
198
+ if (queue.length > 0 && !flushTimer) {
199
+ flushTimer = setTimeout(() => {
200
+ flushTimer = null;
201
+ flush();
202
+ }, cfg.flushInterval);
203
+ }
204
+ }
205
+ async function shutdown() {
206
+ log("Shutting down...");
207
+ isShutdown = true;
208
+ if (flushTimer) {
209
+ clearTimeout(flushTimer);
210
+ flushTimer = null;
211
+ }
212
+ if (heartbeatTimer) {
213
+ clearInterval(heartbeatTimer);
214
+ heartbeatTimer = null;
215
+ }
216
+ if (queue.length > 0) {
217
+ log(`Flushing ${queue.length} remaining events`);
218
+ isFlushing = false;
219
+ await flush();
220
+ }
221
+ log("Shutdown complete");
222
+ }
223
+ if (typeof process !== "undefined") {
224
+ const handleExit = () => {
225
+ shutdown().catch(console.error);
226
+ };
227
+ process.on("beforeExit", handleExit);
228
+ process.on("SIGINT", handleExit);
229
+ process.on("SIGTERM", handleExit);
230
+ }
231
+ if (cfg.autoConnect && agentId) {
232
+ log("Auto-connect enabled, sending registration heartbeat");
233
+ heartbeat("healthy", { event: "sdk_connected" });
234
+ }
235
+ if (cfg.heartbeatIntervalMs && cfg.heartbeatIntervalMs > 0 && agentId) {
236
+ log(`Starting periodic heartbeat every ${cfg.heartbeatIntervalMs}ms`);
237
+ heartbeatTimer = setInterval(() => {
238
+ if (!isShutdown) {
239
+ heartbeat("healthy", { event: "periodic" });
240
+ }
241
+ }, cfg.heartbeatIntervalMs);
242
+ if (heartbeatTimer && typeof heartbeatTimer === "object" && "unref" in heartbeatTimer) {
243
+ heartbeatTimer.unref();
244
+ }
245
+ }
246
+ return {
247
+ track,
248
+ flush,
249
+ shutdown,
250
+ heartbeat,
251
+ reportOutcome,
252
+ startTask,
253
+ trackLLM
254
+ };
255
+ }
256
+
257
+ export {
258
+ createClient
259
+ };
@@ -0,0 +1,156 @@
1
+ import {
2
+ createClient
3
+ } from "./chunk-GV5RDHUW.mjs";
4
+
5
+ // src/wrappers/fetch.ts
6
+ var X402_HEADERS = {
7
+ PAYMENT: "x-payment",
8
+ PAYMENT_RESPONSE: "x-payment-response",
9
+ PAYER: "x-payer",
10
+ AMOUNT: "x-payment-amount",
11
+ CURRENCY: "x-payment-currency",
12
+ TX_HASH: "x-payment-tx",
13
+ FACILITATOR: "x-facilitator"
14
+ };
15
+ function extractPaymentFromResponse(headers) {
16
+ const txHash = headers.get(X402_HEADERS.TX_HASH) || headers.get(X402_HEADERS.PAYMENT_RESPONSE);
17
+ const amount = headers.get(X402_HEADERS.AMOUNT);
18
+ if (!txHash && !amount) return void 0;
19
+ return {
20
+ amount: amount || "0",
21
+ currency: headers.get(X402_HEADERS.CURRENCY) || "USDC",
22
+ wallet: headers.get(X402_HEADERS.PAYER) || "",
23
+ status: "success",
24
+ txHash: txHash || void 0,
25
+ facilitator: headers.get(X402_HEADERS.FACILITATOR) || void 0
26
+ };
27
+ }
28
+ function extractPaymentFromRequest(headers) {
29
+ const paymentProof = headers[X402_HEADERS.PAYMENT] || headers[X402_HEADERS.TX_HASH];
30
+ if (!paymentProof) return void 0;
31
+ return {
32
+ amount: headers[X402_HEADERS.AMOUNT] || "0",
33
+ currency: headers[X402_HEADERS.CURRENCY] || "USDC",
34
+ wallet: headers[X402_HEADERS.PAYER] || "",
35
+ status: "pending",
36
+ // outbound — not yet confirmed
37
+ txHash: headers[X402_HEADERS.TX_HASH] || void 0,
38
+ facilitator: headers[X402_HEADERS.FACILITATOR] || void 0
39
+ };
40
+ }
41
+ function shouldExclude(url, patterns) {
42
+ for (const pattern of patterns) {
43
+ if (pattern.includes("*")) {
44
+ const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
45
+ if (regex.test(url)) return true;
46
+ } else if (url.includes(pattern)) {
47
+ return true;
48
+ }
49
+ }
50
+ return false;
51
+ }
52
+ function parseUrl(input) {
53
+ let urlStr;
54
+ if (typeof input === "string") {
55
+ urlStr = input;
56
+ } else if (input instanceof URL) {
57
+ urlStr = input.toString();
58
+ } else if (typeof input === "object" && "url" in input) {
59
+ urlStr = input.url;
60
+ } else {
61
+ urlStr = String(input);
62
+ }
63
+ try {
64
+ const parsed = new URL(urlStr);
65
+ return { url: urlStr, hostname: parsed.hostname, pathname: parsed.pathname };
66
+ } catch {
67
+ return { url: urlStr, hostname: "", pathname: urlStr };
68
+ }
69
+ }
70
+ function headersToRecord(init) {
71
+ const raw = init?.headers;
72
+ if (!raw) return {};
73
+ const result = {};
74
+ if (raw instanceof Headers) {
75
+ raw.forEach((v, k) => {
76
+ result[k.toLowerCase()] = v;
77
+ });
78
+ } else if (Array.isArray(raw)) {
79
+ for (const [k, v] of raw) {
80
+ result[k.toLowerCase()] = v;
81
+ }
82
+ } else {
83
+ for (const [k, v] of Object.entries(raw)) {
84
+ result[k.toLowerCase()] = Array.isArray(v) ? v.join(", ") : String(v);
85
+ }
86
+ }
87
+ return result;
88
+ }
89
+ function createAnalytix402Fetch(options) {
90
+ const client = createClient(options);
91
+ const onlyPayments = options.onlyPayments ?? false;
92
+ const excludeUrls = [
93
+ ...options.excludeUrls || [],
94
+ // Never track calls back to Analytix402 itself
95
+ "*analytix402.com*"
96
+ ];
97
+ const wrappedFetch = async function analytix402Fetch(input, init) {
98
+ const { url, pathname } = parseUrl(input);
99
+ if (shouldExclude(url, excludeUrls)) {
100
+ return fetch(input, init);
101
+ }
102
+ const method = init?.method?.toUpperCase() || "GET";
103
+ const requestHeaders = headersToRecord(init);
104
+ const outboundPayment = extractPaymentFromRequest(requestHeaders);
105
+ const start = Date.now();
106
+ let response;
107
+ try {
108
+ response = await fetch(input, init);
109
+ } catch (error) {
110
+ const event2 = {
111
+ type: "request",
112
+ method,
113
+ path: pathname,
114
+ endpoint: url,
115
+ statusCode: 0,
116
+ responseTimeMs: Date.now() - start,
117
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
118
+ agentId: options.agentId,
119
+ payment: outboundPayment,
120
+ metadata: {
121
+ error: error instanceof Error ? error.message : String(error)
122
+ }
123
+ };
124
+ client.track(event2);
125
+ throw error;
126
+ }
127
+ const responseTimeMs = Date.now() - start;
128
+ const responsePayment = extractPaymentFromResponse(response.headers);
129
+ const is402 = response.status === 402;
130
+ const hasPayment = !!(outboundPayment || responsePayment);
131
+ if (onlyPayments && !hasPayment && !is402) {
132
+ return response;
133
+ }
134
+ const payment = responsePayment || outboundPayment;
135
+ const event = {
136
+ type: "request",
137
+ method,
138
+ path: pathname,
139
+ endpoint: url,
140
+ statusCode: response.status,
141
+ responseTimeMs,
142
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
143
+ agentId: options.agentId,
144
+ payment: payment || void 0,
145
+ metadata: is402 ? { paymentRequired: true } : void 0
146
+ };
147
+ client.track(event);
148
+ return response;
149
+ };
150
+ wrappedFetch.client = client;
151
+ return wrappedFetch;
152
+ }
153
+
154
+ export {
155
+ createAnalytix402Fetch
156
+ };
@@ -0,0 +1,85 @@
1
+ import {
2
+ createClient
3
+ } from "./chunk-GV5RDHUW.mjs";
4
+
5
+ // src/wrappers/openai.ts
6
+ var DEFAULT_COSTS = {
7
+ "gpt-4o": { input: 2.5, output: 10 },
8
+ "gpt-4o-mini": { input: 0.15, output: 0.6 },
9
+ "gpt-4-turbo": { input: 10, output: 30 },
10
+ "gpt-4": { input: 30, output: 60 },
11
+ "gpt-3.5-turbo": { input: 0.5, output: 1.5 },
12
+ "o1": { input: 15, output: 60 },
13
+ "o1-mini": { input: 3, output: 12 },
14
+ "o3-mini": { input: 1.1, output: 4.4 }
15
+ };
16
+ function estimateCost(model, inputTokens, outputTokens, customCosts) {
17
+ const costs = customCosts ?? DEFAULT_COSTS;
18
+ let pricing = costs[model];
19
+ if (!pricing) {
20
+ const prefix = Object.keys(costs).find((k) => model.startsWith(k));
21
+ if (prefix) pricing = costs[prefix];
22
+ }
23
+ if (!pricing) return void 0;
24
+ return inputTokens / 1e6 * pricing.input + outputTokens / 1e6 * pricing.output;
25
+ }
26
+ function withAnalytix(openai, options) {
27
+ const client = createClient(options);
28
+ const customCosts = options.costPerMillionTokens;
29
+ const originalCreate = openai.chat.completions.create.bind(openai.chat.completions);
30
+ const trackedCreate = async function(body, opts) {
31
+ const start = Date.now();
32
+ const model = body.model || "unknown";
33
+ try {
34
+ const result = await originalCreate(body, opts);
35
+ const durationMs = Date.now() - start;
36
+ const inputTokens = result.usage?.prompt_tokens ?? 0;
37
+ const outputTokens = result.usage?.completion_tokens ?? 0;
38
+ const costUsd = estimateCost(model, inputTokens, outputTokens, customCosts);
39
+ client.trackLLM({
40
+ model,
41
+ provider: "openai",
42
+ inputTokens,
43
+ outputTokens,
44
+ costUsd,
45
+ durationMs,
46
+ metadata: {
47
+ completionId: result.id
48
+ }
49
+ });
50
+ return result;
51
+ } catch (error) {
52
+ const durationMs = Date.now() - start;
53
+ client.trackLLM({
54
+ model,
55
+ provider: "openai",
56
+ inputTokens: 0,
57
+ outputTokens: 0,
58
+ durationMs,
59
+ metadata: {
60
+ error: error instanceof Error ? error.message : String(error)
61
+ }
62
+ });
63
+ throw error;
64
+ }
65
+ };
66
+ const proxiedCompletions = Object.create(openai.chat.completions, {
67
+ create: { value: trackedCreate, writable: true, configurable: true }
68
+ });
69
+ const proxiedChat = Object.create(openai.chat, {
70
+ completions: { value: proxiedCompletions, writable: true, configurable: true }
71
+ });
72
+ const proxied = Object.create(openai, {
73
+ chat: { value: proxiedChat, writable: true, configurable: true }
74
+ });
75
+ proxied.__analytix402 = client;
76
+ return proxied;
77
+ }
78
+ function getAnalytixClient(openai) {
79
+ return openai.__analytix402;
80
+ }
81
+
82
+ export {
83
+ withAnalytix,
84
+ getAnalytixClient
85
+ };
@@ -0,0 +1,82 @@
1
+ import {
2
+ createClient
3
+ } from "./chunk-GV5RDHUW.mjs";
4
+
5
+ // src/wrappers/anthropic.ts
6
+ var DEFAULT_COSTS = {
7
+ "claude-opus-4-6": { input: 15, output: 75 },
8
+ "claude-sonnet-4-5-20250929": { input: 3, output: 15 },
9
+ "claude-haiku-4-5-20251001": { input: 0.8, output: 4 },
10
+ "claude-3-5-sonnet": { input: 3, output: 15 },
11
+ "claude-3-5-haiku": { input: 0.8, output: 4 },
12
+ "claude-3-opus": { input: 15, output: 75 },
13
+ "claude-3-sonnet": { input: 3, output: 15 },
14
+ "claude-3-haiku": { input: 0.25, output: 1.25 }
15
+ };
16
+ function estimateCost(model, inputTokens, outputTokens, customCosts) {
17
+ const costs = customCosts ?? DEFAULT_COSTS;
18
+ let pricing = costs[model];
19
+ if (!pricing) {
20
+ const prefix = Object.keys(costs).find((k) => model.startsWith(k));
21
+ if (prefix) pricing = costs[prefix];
22
+ }
23
+ if (!pricing) return void 0;
24
+ return inputTokens / 1e6 * pricing.input + outputTokens / 1e6 * pricing.output;
25
+ }
26
+ function withAnalytix(anthropic, options) {
27
+ const client = createClient(options);
28
+ const customCosts = options.costPerMillionTokens;
29
+ const originalCreate = anthropic.messages.create.bind(anthropic.messages);
30
+ const trackedCreate = async function(body, opts) {
31
+ const start = Date.now();
32
+ const model = body.model || "unknown";
33
+ try {
34
+ const result = await originalCreate(body, opts);
35
+ const durationMs = Date.now() - start;
36
+ const inputTokens = result.usage?.input_tokens ?? 0;
37
+ const outputTokens = result.usage?.output_tokens ?? 0;
38
+ const costUsd = estimateCost(model, inputTokens, outputTokens, customCosts);
39
+ client.trackLLM({
40
+ model,
41
+ provider: "anthropic",
42
+ inputTokens,
43
+ outputTokens,
44
+ costUsd,
45
+ durationMs,
46
+ metadata: {
47
+ messageId: result.id
48
+ }
49
+ });
50
+ return result;
51
+ } catch (error) {
52
+ const durationMs = Date.now() - start;
53
+ client.trackLLM({
54
+ model,
55
+ provider: "anthropic",
56
+ inputTokens: 0,
57
+ outputTokens: 0,
58
+ durationMs,
59
+ metadata: {
60
+ error: error instanceof Error ? error.message : String(error)
61
+ }
62
+ });
63
+ throw error;
64
+ }
65
+ };
66
+ const proxiedMessages = Object.create(anthropic.messages, {
67
+ create: { value: trackedCreate, writable: true, configurable: true }
68
+ });
69
+ const proxied = Object.create(anthropic, {
70
+ messages: { value: proxiedMessages, writable: true, configurable: true }
71
+ });
72
+ proxied.__analytix402 = client;
73
+ return proxied;
74
+ }
75
+ function getAnalytixClient(anthropic) {
76
+ return anthropic.__analytix402;
77
+ }
78
+
79
+ export {
80
+ withAnalytix,
81
+ getAnalytixClient
82
+ };