@analytix402/sdk 0.1.1 → 0.1.2
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 +69 -0
- package/dist/chunk-GV5RDHUW.mjs +259 -0
- package/dist/chunk-KSO5EEA2.mjs +156 -0
- package/dist/chunk-MIPQLCB6.mjs +85 -0
- package/dist/chunk-PQO3A7RN.mjs +82 -0
- package/dist/index.d.mts +6 -210
- package/dist/index.d.ts +6 -210
- package/dist/index.js +325 -3
- package/dist/index.mjs +16 -234
- package/dist/types-C67qDpYb.d.mts +290 -0
- package/dist/types-C67qDpYb.d.ts +290 -0
- package/dist/wrappers/anthropic.d.mts +67 -0
- package/dist/wrappers/anthropic.d.ts +67 -0
- package/dist/wrappers/anthropic.js +361 -0
- package/dist/wrappers/anthropic.mjs +9 -0
- package/dist/wrappers/fetch.d.mts +45 -0
- package/dist/wrappers/fetch.d.ts +45 -0
- package/dist/wrappers/fetch.js +434 -0
- package/dist/wrappers/fetch.mjs +7 -0
- package/dist/wrappers/openai.d.mts +69 -0
- package/dist/wrappers/openai.d.ts +69 -0
- package/dist/wrappers/openai.js +364 -0
- package/dist/wrappers/openai.mjs +9 -0
- package/package.json +36 -5
package/dist/index.js
CHANGED
|
@@ -23,7 +23,10 @@ __export(index_exports, {
|
|
|
23
23
|
Analytix402: () => Analytix402,
|
|
24
24
|
analytix402: () => analytix402,
|
|
25
25
|
createAnalytix402Client: () => createAnalytix402Client,
|
|
26
|
-
|
|
26
|
+
createAnalytix402Fetch: () => createAnalytix402Fetch,
|
|
27
|
+
createClient: () => createClient,
|
|
28
|
+
withAnalytixAnthropic: () => withAnalytix2,
|
|
29
|
+
withAnalytixOpenAI: () => withAnalytix
|
|
27
30
|
});
|
|
28
31
|
module.exports = __toCommonJS(index_exports);
|
|
29
32
|
|
|
@@ -38,7 +41,9 @@ var DEFAULT_CONFIG = {
|
|
|
38
41
|
maxRetries: 3,
|
|
39
42
|
maxQueueSize: 1e3,
|
|
40
43
|
timeout: 1e4,
|
|
41
|
-
excludePaths: ["/health", "/healthz", "/ready", "/metrics", "/favicon.ico"]
|
|
44
|
+
excludePaths: ["/health", "/healthz", "/ready", "/metrics", "/favicon.ico"],
|
|
45
|
+
autoConnect: false,
|
|
46
|
+
heartbeatIntervalMs: 0
|
|
42
47
|
};
|
|
43
48
|
function createClient(config) {
|
|
44
49
|
if (!config.apiKey) {
|
|
@@ -54,6 +59,7 @@ function createClient(config) {
|
|
|
54
59
|
const agentId = cfg.agentId;
|
|
55
60
|
const queue = [];
|
|
56
61
|
let flushTimer = null;
|
|
62
|
+
let heartbeatTimer = null;
|
|
57
63
|
let isFlushing = false;
|
|
58
64
|
let isShutdown = false;
|
|
59
65
|
const log = (...args) => {
|
|
@@ -235,6 +241,10 @@ function createClient(config) {
|
|
|
235
241
|
clearTimeout(flushTimer);
|
|
236
242
|
flushTimer = null;
|
|
237
243
|
}
|
|
244
|
+
if (heartbeatTimer) {
|
|
245
|
+
clearInterval(heartbeatTimer);
|
|
246
|
+
heartbeatTimer = null;
|
|
247
|
+
}
|
|
238
248
|
if (queue.length > 0) {
|
|
239
249
|
log(`Flushing ${queue.length} remaining events`);
|
|
240
250
|
isFlushing = false;
|
|
@@ -250,6 +260,21 @@ function createClient(config) {
|
|
|
250
260
|
process.on("SIGINT", handleExit);
|
|
251
261
|
process.on("SIGTERM", handleExit);
|
|
252
262
|
}
|
|
263
|
+
if (cfg.autoConnect && agentId) {
|
|
264
|
+
log("Auto-connect enabled, sending registration heartbeat");
|
|
265
|
+
heartbeat("healthy", { event: "sdk_connected" });
|
|
266
|
+
}
|
|
267
|
+
if (cfg.heartbeatIntervalMs && cfg.heartbeatIntervalMs > 0 && agentId) {
|
|
268
|
+
log(`Starting periodic heartbeat every ${cfg.heartbeatIntervalMs}ms`);
|
|
269
|
+
heartbeatTimer = setInterval(() => {
|
|
270
|
+
if (!isShutdown) {
|
|
271
|
+
heartbeat("healthy", { event: "periodic" });
|
|
272
|
+
}
|
|
273
|
+
}, cfg.heartbeatIntervalMs);
|
|
274
|
+
if (heartbeatTimer && typeof heartbeatTimer === "object" && "unref" in heartbeatTimer) {
|
|
275
|
+
heartbeatTimer.unref();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
253
278
|
return {
|
|
254
279
|
track,
|
|
255
280
|
flush,
|
|
@@ -404,10 +429,307 @@ var Analytix402 = analytix402;
|
|
|
404
429
|
function createAnalytix402Client(config) {
|
|
405
430
|
return createClient(config);
|
|
406
431
|
}
|
|
432
|
+
|
|
433
|
+
// src/wrappers/openai.ts
|
|
434
|
+
var DEFAULT_COSTS = {
|
|
435
|
+
"gpt-4o": { input: 2.5, output: 10 },
|
|
436
|
+
"gpt-4o-mini": { input: 0.15, output: 0.6 },
|
|
437
|
+
"gpt-4-turbo": { input: 10, output: 30 },
|
|
438
|
+
"gpt-4": { input: 30, output: 60 },
|
|
439
|
+
"gpt-3.5-turbo": { input: 0.5, output: 1.5 },
|
|
440
|
+
"o1": { input: 15, output: 60 },
|
|
441
|
+
"o1-mini": { input: 3, output: 12 },
|
|
442
|
+
"o3-mini": { input: 1.1, output: 4.4 }
|
|
443
|
+
};
|
|
444
|
+
function estimateCost(model, inputTokens, outputTokens, customCosts) {
|
|
445
|
+
const costs = customCosts ?? DEFAULT_COSTS;
|
|
446
|
+
let pricing = costs[model];
|
|
447
|
+
if (!pricing) {
|
|
448
|
+
const prefix = Object.keys(costs).find((k) => model.startsWith(k));
|
|
449
|
+
if (prefix) pricing = costs[prefix];
|
|
450
|
+
}
|
|
451
|
+
if (!pricing) return void 0;
|
|
452
|
+
return inputTokens / 1e6 * pricing.input + outputTokens / 1e6 * pricing.output;
|
|
453
|
+
}
|
|
454
|
+
function withAnalytix(openai, options) {
|
|
455
|
+
const client = createClient(options);
|
|
456
|
+
const customCosts = options.costPerMillionTokens;
|
|
457
|
+
const originalCreate = openai.chat.completions.create.bind(openai.chat.completions);
|
|
458
|
+
const trackedCreate = async function(body, opts) {
|
|
459
|
+
const start = Date.now();
|
|
460
|
+
const model = body.model || "unknown";
|
|
461
|
+
try {
|
|
462
|
+
const result = await originalCreate(body, opts);
|
|
463
|
+
const durationMs = Date.now() - start;
|
|
464
|
+
const inputTokens = result.usage?.prompt_tokens ?? 0;
|
|
465
|
+
const outputTokens = result.usage?.completion_tokens ?? 0;
|
|
466
|
+
const costUsd = estimateCost(model, inputTokens, outputTokens, customCosts);
|
|
467
|
+
client.trackLLM({
|
|
468
|
+
model,
|
|
469
|
+
provider: "openai",
|
|
470
|
+
inputTokens,
|
|
471
|
+
outputTokens,
|
|
472
|
+
costUsd,
|
|
473
|
+
durationMs,
|
|
474
|
+
metadata: {
|
|
475
|
+
completionId: result.id
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
return result;
|
|
479
|
+
} catch (error) {
|
|
480
|
+
const durationMs = Date.now() - start;
|
|
481
|
+
client.trackLLM({
|
|
482
|
+
model,
|
|
483
|
+
provider: "openai",
|
|
484
|
+
inputTokens: 0,
|
|
485
|
+
outputTokens: 0,
|
|
486
|
+
durationMs,
|
|
487
|
+
metadata: {
|
|
488
|
+
error: error instanceof Error ? error.message : String(error)
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
throw error;
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
const proxiedCompletions = Object.create(openai.chat.completions, {
|
|
495
|
+
create: { value: trackedCreate, writable: true, configurable: true }
|
|
496
|
+
});
|
|
497
|
+
const proxiedChat = Object.create(openai.chat, {
|
|
498
|
+
completions: { value: proxiedCompletions, writable: true, configurable: true }
|
|
499
|
+
});
|
|
500
|
+
const proxied = Object.create(openai, {
|
|
501
|
+
chat: { value: proxiedChat, writable: true, configurable: true }
|
|
502
|
+
});
|
|
503
|
+
proxied.__analytix402 = client;
|
|
504
|
+
return proxied;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// src/wrappers/anthropic.ts
|
|
508
|
+
var DEFAULT_COSTS2 = {
|
|
509
|
+
"claude-opus-4-6": { input: 15, output: 75 },
|
|
510
|
+
"claude-sonnet-4-5-20250929": { input: 3, output: 15 },
|
|
511
|
+
"claude-haiku-4-5-20251001": { input: 0.8, output: 4 },
|
|
512
|
+
"claude-3-5-sonnet": { input: 3, output: 15 },
|
|
513
|
+
"claude-3-5-haiku": { input: 0.8, output: 4 },
|
|
514
|
+
"claude-3-opus": { input: 15, output: 75 },
|
|
515
|
+
"claude-3-sonnet": { input: 3, output: 15 },
|
|
516
|
+
"claude-3-haiku": { input: 0.25, output: 1.25 }
|
|
517
|
+
};
|
|
518
|
+
function estimateCost2(model, inputTokens, outputTokens, customCosts) {
|
|
519
|
+
const costs = customCosts ?? DEFAULT_COSTS2;
|
|
520
|
+
let pricing = costs[model];
|
|
521
|
+
if (!pricing) {
|
|
522
|
+
const prefix = Object.keys(costs).find((k) => model.startsWith(k));
|
|
523
|
+
if (prefix) pricing = costs[prefix];
|
|
524
|
+
}
|
|
525
|
+
if (!pricing) return void 0;
|
|
526
|
+
return inputTokens / 1e6 * pricing.input + outputTokens / 1e6 * pricing.output;
|
|
527
|
+
}
|
|
528
|
+
function withAnalytix2(anthropic, options) {
|
|
529
|
+
const client = createClient(options);
|
|
530
|
+
const customCosts = options.costPerMillionTokens;
|
|
531
|
+
const originalCreate = anthropic.messages.create.bind(anthropic.messages);
|
|
532
|
+
const trackedCreate = async function(body, opts) {
|
|
533
|
+
const start = Date.now();
|
|
534
|
+
const model = body.model || "unknown";
|
|
535
|
+
try {
|
|
536
|
+
const result = await originalCreate(body, opts);
|
|
537
|
+
const durationMs = Date.now() - start;
|
|
538
|
+
const inputTokens = result.usage?.input_tokens ?? 0;
|
|
539
|
+
const outputTokens = result.usage?.output_tokens ?? 0;
|
|
540
|
+
const costUsd = estimateCost2(model, inputTokens, outputTokens, customCosts);
|
|
541
|
+
client.trackLLM({
|
|
542
|
+
model,
|
|
543
|
+
provider: "anthropic",
|
|
544
|
+
inputTokens,
|
|
545
|
+
outputTokens,
|
|
546
|
+
costUsd,
|
|
547
|
+
durationMs,
|
|
548
|
+
metadata: {
|
|
549
|
+
messageId: result.id
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
return result;
|
|
553
|
+
} catch (error) {
|
|
554
|
+
const durationMs = Date.now() - start;
|
|
555
|
+
client.trackLLM({
|
|
556
|
+
model,
|
|
557
|
+
provider: "anthropic",
|
|
558
|
+
inputTokens: 0,
|
|
559
|
+
outputTokens: 0,
|
|
560
|
+
durationMs,
|
|
561
|
+
metadata: {
|
|
562
|
+
error: error instanceof Error ? error.message : String(error)
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
throw error;
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
const proxiedMessages = Object.create(anthropic.messages, {
|
|
569
|
+
create: { value: trackedCreate, writable: true, configurable: true }
|
|
570
|
+
});
|
|
571
|
+
const proxied = Object.create(anthropic, {
|
|
572
|
+
messages: { value: proxiedMessages, writable: true, configurable: true }
|
|
573
|
+
});
|
|
574
|
+
proxied.__analytix402 = client;
|
|
575
|
+
return proxied;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// src/wrappers/fetch.ts
|
|
579
|
+
var X402_HEADERS2 = {
|
|
580
|
+
PAYMENT: "x-payment",
|
|
581
|
+
PAYMENT_RESPONSE: "x-payment-response",
|
|
582
|
+
PAYER: "x-payer",
|
|
583
|
+
AMOUNT: "x-payment-amount",
|
|
584
|
+
CURRENCY: "x-payment-currency",
|
|
585
|
+
TX_HASH: "x-payment-tx",
|
|
586
|
+
FACILITATOR: "x-facilitator"
|
|
587
|
+
};
|
|
588
|
+
function extractPaymentFromResponse(headers) {
|
|
589
|
+
const txHash = headers.get(X402_HEADERS2.TX_HASH) || headers.get(X402_HEADERS2.PAYMENT_RESPONSE);
|
|
590
|
+
const amount = headers.get(X402_HEADERS2.AMOUNT);
|
|
591
|
+
if (!txHash && !amount) return void 0;
|
|
592
|
+
return {
|
|
593
|
+
amount: amount || "0",
|
|
594
|
+
currency: headers.get(X402_HEADERS2.CURRENCY) || "USDC",
|
|
595
|
+
wallet: headers.get(X402_HEADERS2.PAYER) || "",
|
|
596
|
+
status: "success",
|
|
597
|
+
txHash: txHash || void 0,
|
|
598
|
+
facilitator: headers.get(X402_HEADERS2.FACILITATOR) || void 0
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
function extractPaymentFromRequest(headers) {
|
|
602
|
+
const paymentProof = headers[X402_HEADERS2.PAYMENT] || headers[X402_HEADERS2.TX_HASH];
|
|
603
|
+
if (!paymentProof) return void 0;
|
|
604
|
+
return {
|
|
605
|
+
amount: headers[X402_HEADERS2.AMOUNT] || "0",
|
|
606
|
+
currency: headers[X402_HEADERS2.CURRENCY] || "USDC",
|
|
607
|
+
wallet: headers[X402_HEADERS2.PAYER] || "",
|
|
608
|
+
status: "pending",
|
|
609
|
+
// outbound — not yet confirmed
|
|
610
|
+
txHash: headers[X402_HEADERS2.TX_HASH] || void 0,
|
|
611
|
+
facilitator: headers[X402_HEADERS2.FACILITATOR] || void 0
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
function shouldExclude(url, patterns) {
|
|
615
|
+
for (const pattern of patterns) {
|
|
616
|
+
if (pattern.includes("*")) {
|
|
617
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
618
|
+
if (regex.test(url)) return true;
|
|
619
|
+
} else if (url.includes(pattern)) {
|
|
620
|
+
return true;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
function parseUrl(input) {
|
|
626
|
+
let urlStr;
|
|
627
|
+
if (typeof input === "string") {
|
|
628
|
+
urlStr = input;
|
|
629
|
+
} else if (input instanceof URL) {
|
|
630
|
+
urlStr = input.toString();
|
|
631
|
+
} else if (typeof input === "object" && "url" in input) {
|
|
632
|
+
urlStr = input.url;
|
|
633
|
+
} else {
|
|
634
|
+
urlStr = String(input);
|
|
635
|
+
}
|
|
636
|
+
try {
|
|
637
|
+
const parsed = new URL(urlStr);
|
|
638
|
+
return { url: urlStr, hostname: parsed.hostname, pathname: parsed.pathname };
|
|
639
|
+
} catch {
|
|
640
|
+
return { url: urlStr, hostname: "", pathname: urlStr };
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
function headersToRecord(init) {
|
|
644
|
+
const raw = init?.headers;
|
|
645
|
+
if (!raw) return {};
|
|
646
|
+
const result = {};
|
|
647
|
+
if (raw instanceof Headers) {
|
|
648
|
+
raw.forEach((v, k) => {
|
|
649
|
+
result[k.toLowerCase()] = v;
|
|
650
|
+
});
|
|
651
|
+
} else if (Array.isArray(raw)) {
|
|
652
|
+
for (const [k, v] of raw) {
|
|
653
|
+
result[k.toLowerCase()] = v;
|
|
654
|
+
}
|
|
655
|
+
} else {
|
|
656
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
657
|
+
result[k.toLowerCase()] = Array.isArray(v) ? v.join(", ") : String(v);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return result;
|
|
661
|
+
}
|
|
662
|
+
function createAnalytix402Fetch(options) {
|
|
663
|
+
const client = createClient(options);
|
|
664
|
+
const onlyPayments = options.onlyPayments ?? false;
|
|
665
|
+
const excludeUrls = [
|
|
666
|
+
...options.excludeUrls || [],
|
|
667
|
+
// Never track calls back to Analytix402 itself
|
|
668
|
+
"*analytix402.com*"
|
|
669
|
+
];
|
|
670
|
+
const wrappedFetch = async function analytix402Fetch(input, init) {
|
|
671
|
+
const { url, pathname } = parseUrl(input);
|
|
672
|
+
if (shouldExclude(url, excludeUrls)) {
|
|
673
|
+
return fetch(input, init);
|
|
674
|
+
}
|
|
675
|
+
const method = init?.method?.toUpperCase() || "GET";
|
|
676
|
+
const requestHeaders = headersToRecord(init);
|
|
677
|
+
const outboundPayment = extractPaymentFromRequest(requestHeaders);
|
|
678
|
+
const start = Date.now();
|
|
679
|
+
let response;
|
|
680
|
+
try {
|
|
681
|
+
response = await fetch(input, init);
|
|
682
|
+
} catch (error) {
|
|
683
|
+
const event2 = {
|
|
684
|
+
type: "request",
|
|
685
|
+
method,
|
|
686
|
+
path: pathname,
|
|
687
|
+
endpoint: url,
|
|
688
|
+
statusCode: 0,
|
|
689
|
+
responseTimeMs: Date.now() - start,
|
|
690
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
691
|
+
agentId: options.agentId,
|
|
692
|
+
payment: outboundPayment,
|
|
693
|
+
metadata: {
|
|
694
|
+
error: error instanceof Error ? error.message : String(error)
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
client.track(event2);
|
|
698
|
+
throw error;
|
|
699
|
+
}
|
|
700
|
+
const responseTimeMs = Date.now() - start;
|
|
701
|
+
const responsePayment = extractPaymentFromResponse(response.headers);
|
|
702
|
+
const is402 = response.status === 402;
|
|
703
|
+
const hasPayment = !!(outboundPayment || responsePayment);
|
|
704
|
+
if (onlyPayments && !hasPayment && !is402) {
|
|
705
|
+
return response;
|
|
706
|
+
}
|
|
707
|
+
const payment = responsePayment || outboundPayment;
|
|
708
|
+
const event = {
|
|
709
|
+
type: "request",
|
|
710
|
+
method,
|
|
711
|
+
path: pathname,
|
|
712
|
+
endpoint: url,
|
|
713
|
+
statusCode: response.status,
|
|
714
|
+
responseTimeMs,
|
|
715
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
716
|
+
agentId: options.agentId,
|
|
717
|
+
payment: payment || void 0,
|
|
718
|
+
metadata: is402 ? { paymentRequired: true } : void 0
|
|
719
|
+
};
|
|
720
|
+
client.track(event);
|
|
721
|
+
return response;
|
|
722
|
+
};
|
|
723
|
+
wrappedFetch.client = client;
|
|
724
|
+
return wrappedFetch;
|
|
725
|
+
}
|
|
407
726
|
// Annotate the CommonJS export names for ESM import in node:
|
|
408
727
|
0 && (module.exports = {
|
|
409
728
|
Analytix402,
|
|
410
729
|
analytix402,
|
|
411
730
|
createAnalytix402Client,
|
|
412
|
-
|
|
731
|
+
createAnalytix402Fetch,
|
|
732
|
+
createClient,
|
|
733
|
+
withAnalytixAnthropic,
|
|
734
|
+
withAnalytixOpenAI
|
|
413
735
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -1,236 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
function createClient(config) {
|
|
15
|
-
if (!config.apiKey) {
|
|
16
|
-
throw new Error("Analytix402: apiKey is required");
|
|
17
|
-
}
|
|
18
|
-
if (!config.apiKey.startsWith("ax_live_") && !config.apiKey.startsWith("ax_test_")) {
|
|
19
|
-
console.warn("Analytix402: API key should start with ax_live_ or ax_test_");
|
|
20
|
-
}
|
|
21
|
-
const cfg = {
|
|
22
|
-
...DEFAULT_CONFIG,
|
|
23
|
-
...config
|
|
24
|
-
};
|
|
25
|
-
const agentId = cfg.agentId;
|
|
26
|
-
const queue = [];
|
|
27
|
-
let flushTimer = null;
|
|
28
|
-
let isFlushing = false;
|
|
29
|
-
let isShutdown = false;
|
|
30
|
-
const log = (...args) => {
|
|
31
|
-
if (cfg.debug) {
|
|
32
|
-
console.log("[Analytix402]", ...args);
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
const warn = (...args) => {
|
|
36
|
-
console.warn("[Analytix402]", ...args);
|
|
37
|
-
};
|
|
38
|
-
function enqueue(event) {
|
|
39
|
-
if (isShutdown) {
|
|
40
|
-
warn("Client is shutdown, event dropped");
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
if (queue.length >= cfg.maxQueueSize) {
|
|
44
|
-
warn(`Queue full (${cfg.maxQueueSize}), dropping oldest event`);
|
|
45
|
-
queue.shift();
|
|
46
|
-
}
|
|
47
|
-
queue.push({
|
|
48
|
-
event,
|
|
49
|
-
attempts: 0,
|
|
50
|
-
addedAt: Date.now()
|
|
51
|
-
});
|
|
52
|
-
log(`Event queued (${queue.length} in queue)`);
|
|
53
|
-
if (queue.length >= cfg.batchSize) {
|
|
54
|
-
log("Batch size reached, flushing");
|
|
55
|
-
flush();
|
|
56
|
-
} else if (!flushTimer) {
|
|
57
|
-
flushTimer = setTimeout(() => {
|
|
58
|
-
flushTimer = null;
|
|
59
|
-
flush();
|
|
60
|
-
}, cfg.flushInterval);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function track(event) {
|
|
64
|
-
if (agentId && !event.agentId) {
|
|
65
|
-
event.agentId = agentId;
|
|
66
|
-
}
|
|
67
|
-
enqueue(event);
|
|
68
|
-
}
|
|
69
|
-
function heartbeat(status = "healthy", metadata) {
|
|
70
|
-
if (!agentId) {
|
|
71
|
-
warn("heartbeat() requires agentId in config");
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
const event = {
|
|
75
|
-
type: "heartbeat",
|
|
76
|
-
agentId,
|
|
77
|
-
status,
|
|
78
|
-
metadata,
|
|
79
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
80
|
-
};
|
|
81
|
-
enqueue(event);
|
|
82
|
-
}
|
|
83
|
-
function reportOutcome(taskId, success, options) {
|
|
84
|
-
const event = {
|
|
85
|
-
type: "task_outcome",
|
|
86
|
-
agentId,
|
|
87
|
-
taskId,
|
|
88
|
-
success,
|
|
89
|
-
durationMs: options?.durationMs,
|
|
90
|
-
cost: options?.cost,
|
|
91
|
-
metadata: options?.metadata,
|
|
92
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
93
|
-
};
|
|
94
|
-
enqueue(event);
|
|
95
|
-
}
|
|
96
|
-
function startTask(taskId) {
|
|
97
|
-
const id = taskId || `task_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
98
|
-
const startTime = Date.now();
|
|
99
|
-
return {
|
|
100
|
-
taskId: id,
|
|
101
|
-
end(success, metadata) {
|
|
102
|
-
const durationMs = Date.now() - startTime;
|
|
103
|
-
reportOutcome(id, success, { durationMs, metadata });
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
function trackLLM(usage) {
|
|
108
|
-
const event = {
|
|
109
|
-
type: "llm_usage",
|
|
110
|
-
agentId,
|
|
111
|
-
taskId: usage.taskId,
|
|
112
|
-
model: usage.model,
|
|
113
|
-
provider: usage.provider,
|
|
114
|
-
inputTokens: usage.inputTokens,
|
|
115
|
-
outputTokens: usage.outputTokens,
|
|
116
|
-
totalTokens: usage.inputTokens + usage.outputTokens,
|
|
117
|
-
costUsd: usage.costUsd,
|
|
118
|
-
durationMs: usage.durationMs,
|
|
119
|
-
metadata: usage.metadata,
|
|
120
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
121
|
-
};
|
|
122
|
-
enqueue(event);
|
|
123
|
-
}
|
|
124
|
-
async function sendBatch(events) {
|
|
125
|
-
if (events.length === 0) return true;
|
|
126
|
-
const payload = {
|
|
127
|
-
events,
|
|
128
|
-
sdk: {
|
|
129
|
-
name: SDK_NAME,
|
|
130
|
-
version: SDK_VERSION
|
|
131
|
-
},
|
|
132
|
-
sentAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
133
|
-
};
|
|
134
|
-
try {
|
|
135
|
-
const controller = new AbortController();
|
|
136
|
-
const timeoutId = setTimeout(() => controller.abort(), cfg.timeout);
|
|
137
|
-
const response = await fetch(`${cfg.baseUrl}/api/ingest/batch`, {
|
|
138
|
-
method: "POST",
|
|
139
|
-
headers: {
|
|
140
|
-
"Content-Type": "application/json",
|
|
141
|
-
"X-API-Key": cfg.apiKey,
|
|
142
|
-
"User-Agent": `${SDK_NAME}/${SDK_VERSION}`
|
|
143
|
-
},
|
|
144
|
-
body: JSON.stringify(payload),
|
|
145
|
-
signal: controller.signal
|
|
146
|
-
});
|
|
147
|
-
clearTimeout(timeoutId);
|
|
148
|
-
if (!response.ok) {
|
|
149
|
-
const text = await response.text().catch(() => "Unknown error");
|
|
150
|
-
throw new Error(`HTTP ${response.status}: ${text}`);
|
|
151
|
-
}
|
|
152
|
-
log(`Sent ${events.length} events successfully`);
|
|
153
|
-
return true;
|
|
154
|
-
} catch (error) {
|
|
155
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
156
|
-
warn("Request timed out");
|
|
157
|
-
} else {
|
|
158
|
-
warn("Failed to send events:", error);
|
|
159
|
-
}
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
async function flush() {
|
|
164
|
-
if (isFlushing || queue.length === 0) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
isFlushing = true;
|
|
168
|
-
if (flushTimer) {
|
|
169
|
-
clearTimeout(flushTimer);
|
|
170
|
-
flushTimer = null;
|
|
171
|
-
}
|
|
172
|
-
const batch = queue.splice(0, cfg.batchSize);
|
|
173
|
-
const events = batch.map((q) => q.event);
|
|
174
|
-
log(`Flushing ${events.length} events`);
|
|
175
|
-
const success = await sendBatch(events);
|
|
176
|
-
if (!success) {
|
|
177
|
-
const retriable = batch.filter((q) => q.attempts < cfg.maxRetries);
|
|
178
|
-
if (retriable.length > 0) {
|
|
179
|
-
log(`Re-queuing ${retriable.length} events for retry`);
|
|
180
|
-
for (const item of retriable.reverse()) {
|
|
181
|
-
item.attempts++;
|
|
182
|
-
queue.unshift(item);
|
|
183
|
-
}
|
|
184
|
-
const backoff = Math.min(1e3 * Math.pow(2, retriable[0].attempts), 3e4);
|
|
185
|
-
log(`Retry scheduled in ${backoff}ms`);
|
|
186
|
-
flushTimer = setTimeout(() => {
|
|
187
|
-
flushTimer = null;
|
|
188
|
-
flush();
|
|
189
|
-
}, backoff);
|
|
190
|
-
} else {
|
|
191
|
-
warn(`Dropped ${batch.length} events after ${cfg.maxRetries} retries`);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
isFlushing = false;
|
|
195
|
-
if (queue.length > 0 && !flushTimer) {
|
|
196
|
-
flushTimer = setTimeout(() => {
|
|
197
|
-
flushTimer = null;
|
|
198
|
-
flush();
|
|
199
|
-
}, cfg.flushInterval);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
async function shutdown() {
|
|
203
|
-
log("Shutting down...");
|
|
204
|
-
isShutdown = true;
|
|
205
|
-
if (flushTimer) {
|
|
206
|
-
clearTimeout(flushTimer);
|
|
207
|
-
flushTimer = null;
|
|
208
|
-
}
|
|
209
|
-
if (queue.length > 0) {
|
|
210
|
-
log(`Flushing ${queue.length} remaining events`);
|
|
211
|
-
isFlushing = false;
|
|
212
|
-
await flush();
|
|
213
|
-
}
|
|
214
|
-
log("Shutdown complete");
|
|
215
|
-
}
|
|
216
|
-
if (typeof process !== "undefined") {
|
|
217
|
-
const handleExit = () => {
|
|
218
|
-
shutdown().catch(console.error);
|
|
219
|
-
};
|
|
220
|
-
process.on("beforeExit", handleExit);
|
|
221
|
-
process.on("SIGINT", handleExit);
|
|
222
|
-
process.on("SIGTERM", handleExit);
|
|
223
|
-
}
|
|
224
|
-
return {
|
|
225
|
-
track,
|
|
226
|
-
flush,
|
|
227
|
-
shutdown,
|
|
228
|
-
heartbeat,
|
|
229
|
-
reportOutcome,
|
|
230
|
-
startTask,
|
|
231
|
-
trackLLM
|
|
232
|
-
};
|
|
233
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
withAnalytix as withAnalytix2
|
|
3
|
+
} from "./chunk-PQO3A7RN.mjs";
|
|
4
|
+
import {
|
|
5
|
+
createAnalytix402Fetch
|
|
6
|
+
} from "./chunk-KSO5EEA2.mjs";
|
|
7
|
+
import {
|
|
8
|
+
withAnalytix
|
|
9
|
+
} from "./chunk-MIPQLCB6.mjs";
|
|
10
|
+
import {
|
|
11
|
+
createClient
|
|
12
|
+
} from "./chunk-GV5RDHUW.mjs";
|
|
234
13
|
|
|
235
14
|
// src/middleware.ts
|
|
236
15
|
var X402_HEADERS = {
|
|
@@ -379,5 +158,8 @@ export {
|
|
|
379
158
|
Analytix402,
|
|
380
159
|
analytix402,
|
|
381
160
|
createAnalytix402Client,
|
|
382
|
-
|
|
161
|
+
createAnalytix402Fetch,
|
|
162
|
+
createClient,
|
|
163
|
+
withAnalytix2 as withAnalytixAnthropic,
|
|
164
|
+
withAnalytix as withAnalytixOpenAI
|
|
383
165
|
};
|