@analytix402/sdk 0.1.0

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/dist/index.js ADDED
@@ -0,0 +1,413 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Analytix402: () => Analytix402,
24
+ analytix402: () => analytix402,
25
+ createAnalytix402Client: () => createAnalytix402Client,
26
+ createClient: () => createClient
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+
30
+ // src/client.ts
31
+ var SDK_NAME = "@analytix402/sdk";
32
+ var SDK_VERSION = "0.1.0";
33
+ var DEFAULT_CONFIG = {
34
+ baseUrl: "https://api.analytix402.com",
35
+ debug: false,
36
+ batchSize: 100,
37
+ flushInterval: 5e3,
38
+ maxRetries: 3,
39
+ maxQueueSize: 1e3,
40
+ timeout: 1e4,
41
+ excludePaths: ["/health", "/healthz", "/ready", "/metrics", "/favicon.ico"]
42
+ };
43
+ function createClient(config) {
44
+ if (!config.apiKey) {
45
+ throw new Error("Analytix402: apiKey is required");
46
+ }
47
+ if (!config.apiKey.startsWith("ax_live_") && !config.apiKey.startsWith("ax_test_")) {
48
+ console.warn("Analytix402: API key should start with ax_live_ or ax_test_");
49
+ }
50
+ const cfg = {
51
+ ...DEFAULT_CONFIG,
52
+ ...config
53
+ };
54
+ const agentId = cfg.agentId;
55
+ const queue = [];
56
+ let flushTimer = null;
57
+ let isFlushing = false;
58
+ let isShutdown = false;
59
+ const log = (...args) => {
60
+ if (cfg.debug) {
61
+ console.log("[Analytix402]", ...args);
62
+ }
63
+ };
64
+ const warn = (...args) => {
65
+ console.warn("[Analytix402]", ...args);
66
+ };
67
+ function enqueue(event) {
68
+ if (isShutdown) {
69
+ warn("Client is shutdown, event dropped");
70
+ return;
71
+ }
72
+ if (queue.length >= cfg.maxQueueSize) {
73
+ warn(`Queue full (${cfg.maxQueueSize}), dropping oldest event`);
74
+ queue.shift();
75
+ }
76
+ queue.push({
77
+ event,
78
+ attempts: 0,
79
+ addedAt: Date.now()
80
+ });
81
+ log(`Event queued (${queue.length} in queue)`);
82
+ if (queue.length >= cfg.batchSize) {
83
+ log("Batch size reached, flushing");
84
+ flush();
85
+ } else if (!flushTimer) {
86
+ flushTimer = setTimeout(() => {
87
+ flushTimer = null;
88
+ flush();
89
+ }, cfg.flushInterval);
90
+ }
91
+ }
92
+ function track(event) {
93
+ if (agentId && !event.agentId) {
94
+ event.agentId = agentId;
95
+ }
96
+ enqueue(event);
97
+ }
98
+ function heartbeat(status = "healthy", metadata) {
99
+ if (!agentId) {
100
+ warn("heartbeat() requires agentId in config");
101
+ return;
102
+ }
103
+ const event = {
104
+ type: "heartbeat",
105
+ agentId,
106
+ status,
107
+ metadata,
108
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
109
+ };
110
+ enqueue(event);
111
+ }
112
+ function reportOutcome(taskId, success, options) {
113
+ const event = {
114
+ type: "task_outcome",
115
+ agentId,
116
+ taskId,
117
+ success,
118
+ durationMs: options?.durationMs,
119
+ cost: options?.cost,
120
+ metadata: options?.metadata,
121
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
122
+ };
123
+ enqueue(event);
124
+ }
125
+ function startTask(taskId) {
126
+ const id = taskId || `task_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
127
+ const startTime = Date.now();
128
+ return {
129
+ taskId: id,
130
+ end(success, metadata) {
131
+ const durationMs = Date.now() - startTime;
132
+ reportOutcome(id, success, { durationMs, metadata });
133
+ }
134
+ };
135
+ }
136
+ function trackLLM(usage) {
137
+ const event = {
138
+ type: "llm_usage",
139
+ agentId,
140
+ taskId: usage.taskId,
141
+ model: usage.model,
142
+ provider: usage.provider,
143
+ inputTokens: usage.inputTokens,
144
+ outputTokens: usage.outputTokens,
145
+ totalTokens: usage.inputTokens + usage.outputTokens,
146
+ costUsd: usage.costUsd,
147
+ durationMs: usage.durationMs,
148
+ metadata: usage.metadata,
149
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
150
+ };
151
+ enqueue(event);
152
+ }
153
+ async function sendBatch(events) {
154
+ if (events.length === 0) return true;
155
+ const payload = {
156
+ events,
157
+ sdk: {
158
+ name: SDK_NAME,
159
+ version: SDK_VERSION
160
+ },
161
+ sentAt: (/* @__PURE__ */ new Date()).toISOString()
162
+ };
163
+ try {
164
+ const controller = new AbortController();
165
+ const timeoutId = setTimeout(() => controller.abort(), cfg.timeout);
166
+ const response = await fetch(`${cfg.baseUrl}/api/ingest/batch`, {
167
+ method: "POST",
168
+ headers: {
169
+ "Content-Type": "application/json",
170
+ "X-API-Key": cfg.apiKey,
171
+ "User-Agent": `${SDK_NAME}/${SDK_VERSION}`
172
+ },
173
+ body: JSON.stringify(payload),
174
+ signal: controller.signal
175
+ });
176
+ clearTimeout(timeoutId);
177
+ if (!response.ok) {
178
+ const text = await response.text().catch(() => "Unknown error");
179
+ throw new Error(`HTTP ${response.status}: ${text}`);
180
+ }
181
+ log(`Sent ${events.length} events successfully`);
182
+ return true;
183
+ } catch (error) {
184
+ if (error instanceof Error && error.name === "AbortError") {
185
+ warn("Request timed out");
186
+ } else {
187
+ warn("Failed to send events:", error);
188
+ }
189
+ return false;
190
+ }
191
+ }
192
+ async function flush() {
193
+ if (isFlushing || queue.length === 0) {
194
+ return;
195
+ }
196
+ isFlushing = true;
197
+ if (flushTimer) {
198
+ clearTimeout(flushTimer);
199
+ flushTimer = null;
200
+ }
201
+ const batch = queue.splice(0, cfg.batchSize);
202
+ const events = batch.map((q) => q.event);
203
+ log(`Flushing ${events.length} events`);
204
+ const success = await sendBatch(events);
205
+ if (!success) {
206
+ const retriable = batch.filter((q) => q.attempts < cfg.maxRetries);
207
+ if (retriable.length > 0) {
208
+ log(`Re-queuing ${retriable.length} events for retry`);
209
+ for (const item of retriable.reverse()) {
210
+ item.attempts++;
211
+ queue.unshift(item);
212
+ }
213
+ const backoff = Math.min(1e3 * Math.pow(2, retriable[0].attempts), 3e4);
214
+ log(`Retry scheduled in ${backoff}ms`);
215
+ flushTimer = setTimeout(() => {
216
+ flushTimer = null;
217
+ flush();
218
+ }, backoff);
219
+ } else {
220
+ warn(`Dropped ${batch.length} events after ${cfg.maxRetries} retries`);
221
+ }
222
+ }
223
+ isFlushing = false;
224
+ if (queue.length > 0 && !flushTimer) {
225
+ flushTimer = setTimeout(() => {
226
+ flushTimer = null;
227
+ flush();
228
+ }, cfg.flushInterval);
229
+ }
230
+ }
231
+ async function shutdown() {
232
+ log("Shutting down...");
233
+ isShutdown = true;
234
+ if (flushTimer) {
235
+ clearTimeout(flushTimer);
236
+ flushTimer = null;
237
+ }
238
+ if (queue.length > 0) {
239
+ log(`Flushing ${queue.length} remaining events`);
240
+ isFlushing = false;
241
+ await flush();
242
+ }
243
+ log("Shutdown complete");
244
+ }
245
+ if (typeof process !== "undefined") {
246
+ const handleExit = () => {
247
+ shutdown().catch(console.error);
248
+ };
249
+ process.on("beforeExit", handleExit);
250
+ process.on("SIGINT", handleExit);
251
+ process.on("SIGTERM", handleExit);
252
+ }
253
+ return {
254
+ track,
255
+ flush,
256
+ shutdown,
257
+ heartbeat,
258
+ reportOutcome,
259
+ startTask,
260
+ trackLLM
261
+ };
262
+ }
263
+
264
+ // src/middleware.ts
265
+ var X402_HEADERS = {
266
+ // Payment receipt header (contains payment proof)
267
+ PAYMENT: "x-payment",
268
+ // Payment token (JWT with payment info)
269
+ PAYMENT_TOKEN: "x-payment-token",
270
+ // Payer wallet address
271
+ PAYER: "x-payer",
272
+ // Payment amount
273
+ AMOUNT: "x-payment-amount",
274
+ // Payment currency
275
+ CURRENCY: "x-payment-currency",
276
+ // Transaction hash
277
+ TX_HASH: "x-payment-tx",
278
+ // Facilitator that processed payment
279
+ FACILITATOR: "x-facilitator"
280
+ };
281
+ var DEFAULT_EXCLUDE_PATHS = [
282
+ "/health",
283
+ "/healthz",
284
+ "/ready",
285
+ "/readyz",
286
+ "/live",
287
+ "/livez",
288
+ "/metrics",
289
+ "/favicon.ico",
290
+ "/.well-known"
291
+ ];
292
+ function matchPath(path, pattern) {
293
+ if (pattern.includes("*")) {
294
+ const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
295
+ return regex.test(path);
296
+ }
297
+ return path === pattern || path.startsWith(pattern + "/");
298
+ }
299
+ function extractPaymentInfo(req) {
300
+ const paymentHeader = req.get(X402_HEADERS.PAYMENT) || req.get(X402_HEADERS.PAYMENT_TOKEN);
301
+ if (!paymentHeader) {
302
+ return void 0;
303
+ }
304
+ const wallet = req.get(X402_HEADERS.PAYER) || "";
305
+ const amount = req.get(X402_HEADERS.AMOUNT) || "0";
306
+ const currency = req.get(X402_HEADERS.CURRENCY) || "USDC";
307
+ const txHash = req.get(X402_HEADERS.TX_HASH);
308
+ const facilitator = req.get(X402_HEADERS.FACILITATOR);
309
+ return {
310
+ amount,
311
+ currency,
312
+ wallet,
313
+ status: "success",
314
+ // If we have payment header, assume success (verified by facilitator)
315
+ txHash: txHash || void 0,
316
+ facilitator: facilitator || void 0
317
+ };
318
+ }
319
+ function getHeader(req, name) {
320
+ const value = req.headers[name.toLowerCase()];
321
+ if (Array.isArray(value)) {
322
+ return value[0];
323
+ }
324
+ return value;
325
+ }
326
+ function analytix402(config) {
327
+ const client = createClient(config);
328
+ const excludePaths = [
329
+ ...DEFAULT_EXCLUDE_PATHS,
330
+ ...config.excludePaths || []
331
+ ];
332
+ const debug = config.debug || false;
333
+ const agentId = config.agentId;
334
+ const agentName = config.agentName;
335
+ const log = (...args) => {
336
+ if (debug) {
337
+ console.log("[Analytix402]", ...args);
338
+ }
339
+ };
340
+ return function middleware(req, res, next) {
341
+ const startTime = Date.now();
342
+ const path = req.path || req.url;
343
+ const isExcluded = excludePaths.some((pattern) => matchPath(path, pattern));
344
+ if (isExcluded) {
345
+ log(`Excluded path: ${path}`);
346
+ return next();
347
+ }
348
+ if (config.shouldTrack) {
349
+ const requestInfo = {
350
+ method: req.method,
351
+ path,
352
+ url: req.originalUrl || req.url,
353
+ headers: req.headers,
354
+ ip: req.ip
355
+ };
356
+ if (!config.shouldTrack(requestInfo)) {
357
+ log(`Skipped by shouldTrack: ${path}`);
358
+ return next();
359
+ }
360
+ }
361
+ res.on("finish", () => {
362
+ const responseTime = Date.now() - startTime;
363
+ const statusCode = res.statusCode;
364
+ const payment = extractPaymentInfo(req);
365
+ let endpoint = path;
366
+ if (config.getEndpointName) {
367
+ endpoint = config.getEndpointName({
368
+ method: req.method,
369
+ path,
370
+ url: req.originalUrl || req.url,
371
+ headers: req.headers,
372
+ ip: req.ip
373
+ });
374
+ }
375
+ const event = {
376
+ type: "request",
377
+ method: req.method,
378
+ path,
379
+ endpoint,
380
+ statusCode,
381
+ responseTimeMs: responseTime,
382
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
383
+ ip: req.ip,
384
+ userAgent: getHeader(req, "user-agent")
385
+ };
386
+ if (agentId) {
387
+ event.agentId = agentId;
388
+ }
389
+ const taskId = getHeader(req, "x-task-id");
390
+ if (taskId) {
391
+ event.taskId = taskId;
392
+ }
393
+ if (payment) {
394
+ event.payment = payment;
395
+ log(`Payment captured: ${payment.amount} ${payment.currency} from ${payment.wallet}`);
396
+ }
397
+ client.track(event);
398
+ log(`Tracked: ${req.method} ${path} ${statusCode} ${responseTime}ms`);
399
+ });
400
+ next();
401
+ };
402
+ }
403
+ var Analytix402 = analytix402;
404
+ function createAnalytix402Client(config) {
405
+ return createClient(config);
406
+ }
407
+ // Annotate the CommonJS export names for ESM import in node:
408
+ 0 && (module.exports = {
409
+ Analytix402,
410
+ analytix402,
411
+ createAnalytix402Client,
412
+ createClient
413
+ });