@tokenjam/sdk 0.2.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/README.md +178 -0
- package/dist/client.d.ts +55 -0
- package/dist/client.js +197 -0
- package/dist/client.js.map +1 -0
- package/dist/client.test.d.ts +1 -0
- package/dist/client.test.js +202 -0
- package/dist/client.test.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/semconv.d.ts +69 -0
- package/dist/semconv.js +79 -0
- package/dist/semconv.js.map +1 -0
- package/dist/semconv.test.d.ts +1 -0
- package/dist/semconv.test.js +32 -0
- package/dist/semconv.test.js.map +1 -0
- package/dist/span-builder.d.ts +29 -0
- package/dist/span-builder.js +135 -0
- package/dist/span-builder.js.map +1 -0
- package/dist/span-builder.test.d.ts +1 -0
- package/dist/span-builder.test.js +104 -0
- package/dist/span-builder.test.js.map +1 -0
- package/dist/types.d.ts +93 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# @tokenjam/sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for [TokenJam](https://github.com/Metabuilder-Labs/tokenjam) — local-first, OTel-native observability for AI agents.
|
|
4
|
+
|
|
5
|
+
Communicates with a running `ocw serve` instance via HTTP. No in-process OTel pipeline — spans are built with `SpanBuilder` and sent by `TjClient`.
|
|
6
|
+
|
|
7
|
+
> **Note:** Provider auto-instrumentation (the `patch_anthropic()`, `patch_openai()`, etc. convenience wrappers from the Python SDK) does not exist in this package. Every LLM call and tool call must be manually instrumented using `SpanBuilder`.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @tokenjam/sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires Node.js >= 18. Start the OCW server before sending spans:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install tokenjam
|
|
19
|
+
ocw serve
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { TjClient, SpanBuilder, SpanStatus } from "@tokenjam/sdk";
|
|
26
|
+
|
|
27
|
+
const client = new TjClient({
|
|
28
|
+
ingestSecret: "your-ingest-secret", // from ocw.toml security.ingest_secret
|
|
29
|
+
serviceName: "my-agent", // shown as agent ID in ocw status
|
|
30
|
+
}).start();
|
|
31
|
+
|
|
32
|
+
// Record an LLM call
|
|
33
|
+
const span = new SpanBuilder("gen_ai.llm.call")
|
|
34
|
+
.agentId("my-agent")
|
|
35
|
+
.agentName("My Agent")
|
|
36
|
+
.provider("anthropic")
|
|
37
|
+
.model("claude-sonnet-4-6")
|
|
38
|
+
.inputTokens(512)
|
|
39
|
+
.outputTokens(128)
|
|
40
|
+
.cacheReadTokens(256)
|
|
41
|
+
.cacheCreateTokens(64)
|
|
42
|
+
.conversationId("conv-abc123")
|
|
43
|
+
.startTime(new Date().toISOString())
|
|
44
|
+
.durationMs(1200)
|
|
45
|
+
.build();
|
|
46
|
+
|
|
47
|
+
await client.send(span);
|
|
48
|
+
await client.shutdown();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## TjClient
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
new TjClient(options: TjClientOptions)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
| Option | Type | Default | Description |
|
|
58
|
+
|---|---|---|---|
|
|
59
|
+
| `ingestSecret` | `string` | required | Bearer token from `security.ingest_secret` in `ocw.toml` |
|
|
60
|
+
| `baseUrl` | `string` | `http://127.0.0.1:7391` | OCW server base URL |
|
|
61
|
+
| `serviceName` | `string` | `"ocw-ts-sdk"` | Reported as `service.name` in OTLP resource attributes; used as fallback agent ID |
|
|
62
|
+
| `batchSize` | `number` | `50` | Max spans buffered before auto-flush |
|
|
63
|
+
| `flushIntervalMs` | `number` | `5000` | Interval between automatic flushes (ms) |
|
|
64
|
+
| `maxRetries` | `number` | `3` | Retry attempts on network errors and 5xx responses; 4xx errors are not retried |
|
|
65
|
+
|
|
66
|
+
### Methods
|
|
67
|
+
|
|
68
|
+
| Method | Description |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `client.start()` | Start the automatic flush timer. Returns `this`. |
|
|
71
|
+
| `client.send(span)` | Buffer a span; auto-flushes when `batchSize` is reached. |
|
|
72
|
+
| `client.flush()` | Immediately send all buffered spans. Returns `IngestResult | null`. |
|
|
73
|
+
| `client.shutdown()` | Flush remaining spans and stop the timer. Call before process exit. |
|
|
74
|
+
|
|
75
|
+
## SpanBuilder
|
|
76
|
+
|
|
77
|
+
Fluent builder for constructing spans with GenAI semantic conventions.
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
new SpanBuilder(name: string)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Agent identity
|
|
84
|
+
|
|
85
|
+
| Method | Attribute set |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `.agentId(id)` | `gen_ai.agent.id` + `span.agentId` |
|
|
88
|
+
| `.agentName(name)` | `gen_ai.agent.name` |
|
|
89
|
+
| `.agentVersion(version)` | `gen_ai.agent.version` |
|
|
90
|
+
| `.sessionId(id)` | `gen_ai.session.id` |
|
|
91
|
+
| `.conversationId(id)` | `gen_ai.conversation.id` |
|
|
92
|
+
|
|
93
|
+
### LLM call attributes
|
|
94
|
+
|
|
95
|
+
| Method | Attribute set |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `.provider(name)` | `gen_ai.provider.name` |
|
|
98
|
+
| `.model(name)` | `gen_ai.request.model` |
|
|
99
|
+
| `.inputTokens(n)` | `gen_ai.usage.input_tokens` |
|
|
100
|
+
| `.outputTokens(n)` | `gen_ai.usage.output_tokens` |
|
|
101
|
+
| `.cacheReadTokens(n)` | `gen_ai.usage.cache_read_tokens` |
|
|
102
|
+
| `.cacheCreateTokens(n)` | `gen_ai.usage.cache_creation_tokens` |
|
|
103
|
+
|
|
104
|
+
### Tool call attributes
|
|
105
|
+
|
|
106
|
+
| Method | Attribute set |
|
|
107
|
+
|---|---|
|
|
108
|
+
| `.toolName(name)` | `gen_ai.tool.name` |
|
|
109
|
+
| `.toolInput(input)` | `gen_ai.tool.input` |
|
|
110
|
+
| `.toolOutput(output)` | `gen_ai.tool.output` |
|
|
111
|
+
|
|
112
|
+
### Span metadata
|
|
113
|
+
|
|
114
|
+
| Method | Description |
|
|
115
|
+
|---|---|
|
|
116
|
+
| `.traceId(id)` | Override auto-generated trace ID |
|
|
117
|
+
| `.spanId(id)` | Override auto-generated span ID |
|
|
118
|
+
| `.parentSpanId(id)` | Set parent span for trace hierarchy |
|
|
119
|
+
| `.kind(SpanKind)` | Span kind (default: `CLIENT`) |
|
|
120
|
+
| `.status(SpanStatus, message?)` | Status code (default: `OK`) |
|
|
121
|
+
| `.startTime(iso)` | Start time as ISO 8601 string |
|
|
122
|
+
| `.endTime(iso)` | End time as ISO 8601 string |
|
|
123
|
+
| `.durationMs(ms)` | Duration; used to compute `endTime` if not set |
|
|
124
|
+
| `.attribute(key, value)` | Set any arbitrary attribute |
|
|
125
|
+
| `.build()` | Returns the completed `Span` object |
|
|
126
|
+
|
|
127
|
+
## Semantic convention constants
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { GenAIAttributes, TjAttributes, ClaudeCodeEvents } from "@tokenjam/sdk";
|
|
131
|
+
|
|
132
|
+
// GenAI attribute name strings
|
|
133
|
+
GenAIAttributes.AGENT_ID // "gen_ai.agent.id"
|
|
134
|
+
GenAIAttributes.REQUEST_MODEL // "gen_ai.request.model"
|
|
135
|
+
GenAIAttributes.CACHE_CREATE_TOKENS // "gen_ai.usage.cache_creation_tokens"
|
|
136
|
+
// ...
|
|
137
|
+
|
|
138
|
+
// OCW-specific attribute name strings
|
|
139
|
+
TjAttributes.COST_USD // "ocw.cost_usd"
|
|
140
|
+
TjAttributes.SANDBOX_EVENT // "ocw.sandbox.event"
|
|
141
|
+
// ...
|
|
142
|
+
|
|
143
|
+
// Claude Code OTel log event names and attribute constants
|
|
144
|
+
ClaudeCodeEvents.API_REQUEST // "claude_code.api_request"
|
|
145
|
+
ClaudeCodeEvents.TOOL_RESULT // "claude_code.tool_result"
|
|
146
|
+
ClaudeCodeEvents.COST_USD // "cost_usd"
|
|
147
|
+
ClaudeCodeEvents.INPUT_TOKENS // "input_tokens"
|
|
148
|
+
// ...
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Use `ClaudeCodeEvents` when writing agents that consume Claude Code's own OTel log output (e.g. via the `ocw` MCP server or a log subscriber).
|
|
152
|
+
|
|
153
|
+
## SpanKind and SpanStatus
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { SpanKind, SpanStatus } from "@tokenjam/sdk";
|
|
157
|
+
|
|
158
|
+
SpanKind.CLIENT // default for LLM calls
|
|
159
|
+
SpanKind.SERVER
|
|
160
|
+
SpanKind.INTERNAL
|
|
161
|
+
SpanKind.PRODUCER
|
|
162
|
+
SpanKind.CONSUMER
|
|
163
|
+
|
|
164
|
+
SpanStatus.OK // default
|
|
165
|
+
SpanStatus.ERROR
|
|
166
|
+
SpanStatus.UNSET
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## What this SDK does NOT provide
|
|
170
|
+
|
|
171
|
+
Unlike the Python SDK (`pip install tokenjam`), this package does **not** include:
|
|
172
|
+
|
|
173
|
+
- **Session management** (`@watch()` decorator / `AgentSession` context manager) — you must manually build and send `invoke_agent` session spans.
|
|
174
|
+
- **Provider auto-instrumentation** — no `patchAnthropic()`, `patchOpenAI()`, etc. Every LLM call requires an explicit `SpanBuilder`.
|
|
175
|
+
- **Framework patches** — no LangChain JS, OpenAI Agents SDK, or Vercel AI SDK integration.
|
|
176
|
+
- **In-process OTel pipeline** — all telemetry goes over HTTP to `ocw serve`.
|
|
177
|
+
|
|
178
|
+
See the [Python SDK docs](https://github.com/Metabuilder-Labs/tokenjam#python-sdk) for the full-featured in-process instrumentation path.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { IngestResult, Span } from "./types.js";
|
|
2
|
+
export interface TjClientOptions {
|
|
3
|
+
/** Base URL of the OCW server (default: http://127.0.0.1:7391) */
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
/** Ingest secret for authentication */
|
|
6
|
+
ingestSecret: string;
|
|
7
|
+
/** Maximum batch size before auto-flush (default: 50) */
|
|
8
|
+
batchSize?: number;
|
|
9
|
+
/** Flush interval in milliseconds (default: 5000) */
|
|
10
|
+
flushIntervalMs?: number;
|
|
11
|
+
/** Service name reported in OTLP resource attributes (default: "ocw-ts-sdk") */
|
|
12
|
+
serviceName?: string;
|
|
13
|
+
/** Maximum retry attempts on network errors or 5xx responses (default: 3) */
|
|
14
|
+
maxRetries?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare class TjClient {
|
|
17
|
+
private readonly baseUrl;
|
|
18
|
+
private readonly ingestSecret;
|
|
19
|
+
private readonly batchSize;
|
|
20
|
+
private readonly flushIntervalMs;
|
|
21
|
+
private readonly serviceName;
|
|
22
|
+
private readonly maxRetries;
|
|
23
|
+
private buffer;
|
|
24
|
+
private timer;
|
|
25
|
+
constructor(options: TjClientOptions);
|
|
26
|
+
/**
|
|
27
|
+
* Start the automatic flush timer.
|
|
28
|
+
* Call this once after creating the client.
|
|
29
|
+
*/
|
|
30
|
+
start(): this;
|
|
31
|
+
/**
|
|
32
|
+
* Add a span to the buffer. Auto-flushes when batchSize is reached.
|
|
33
|
+
*/
|
|
34
|
+
send(span: Span): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Flush all buffered spans to the server.
|
|
37
|
+
* Returns the ingest result, or null if the buffer was empty.
|
|
38
|
+
*/
|
|
39
|
+
flush(): Promise<IngestResult | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Flush remaining spans and stop the timer.
|
|
42
|
+
*/
|
|
43
|
+
shutdown(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Convert SDK spans to OTLP JSON batch format.
|
|
46
|
+
*/
|
|
47
|
+
private toBatch;
|
|
48
|
+
/**
|
|
49
|
+
* POST a span batch to the ingest endpoint.
|
|
50
|
+
* Retries up to maxRetries times on network errors or 5xx responses using
|
|
51
|
+
* exponential backoff (base 2s, matching Python HttpTransport behaviour).
|
|
52
|
+
* 4xx errors are not retried — they indicate auth or validation failures.
|
|
53
|
+
*/
|
|
54
|
+
private post;
|
|
55
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TjClient = void 0;
|
|
4
|
+
const types_js_1 = require("./types.js");
|
|
5
|
+
const SPAN_KIND_TO_OTLP = {
|
|
6
|
+
[types_js_1.SpanKind.INTERNAL]: 1,
|
|
7
|
+
[types_js_1.SpanKind.SERVER]: 2,
|
|
8
|
+
[types_js_1.SpanKind.CLIENT]: 3,
|
|
9
|
+
[types_js_1.SpanKind.PRODUCER]: 4,
|
|
10
|
+
[types_js_1.SpanKind.CONSUMER]: 5,
|
|
11
|
+
};
|
|
12
|
+
const STATUS_CODE_TO_OTLP = {
|
|
13
|
+
[types_js_1.SpanStatus.UNSET]: 0,
|
|
14
|
+
[types_js_1.SpanStatus.OK]: 1,
|
|
15
|
+
[types_js_1.SpanStatus.ERROR]: 2,
|
|
16
|
+
};
|
|
17
|
+
function isoToUnixNano(iso) {
|
|
18
|
+
const ms = new Date(iso).getTime();
|
|
19
|
+
// Represent as nanoseconds in a string (BigInt-safe)
|
|
20
|
+
return `${ms}000000`;
|
|
21
|
+
}
|
|
22
|
+
function toOtlpValue(value) {
|
|
23
|
+
if (typeof value === "string")
|
|
24
|
+
return { stringValue: value };
|
|
25
|
+
if (typeof value === "number") {
|
|
26
|
+
if (Number.isInteger(value))
|
|
27
|
+
return { intValue: String(value) };
|
|
28
|
+
return { doubleValue: value };
|
|
29
|
+
}
|
|
30
|
+
if (typeof value === "boolean")
|
|
31
|
+
return { boolValue: value };
|
|
32
|
+
if (Array.isArray(value)) {
|
|
33
|
+
return { arrayValue: { values: value.map(toOtlpValue) } };
|
|
34
|
+
}
|
|
35
|
+
return { stringValue: String(value) };
|
|
36
|
+
}
|
|
37
|
+
function spanToOtlp(span) {
|
|
38
|
+
const attributes = Object.entries(span.attributes).map(([key, value]) => ({
|
|
39
|
+
key,
|
|
40
|
+
value: toOtlpValue(value),
|
|
41
|
+
}));
|
|
42
|
+
const otlp = {
|
|
43
|
+
traceId: span.traceId,
|
|
44
|
+
spanId: span.spanId,
|
|
45
|
+
name: span.name,
|
|
46
|
+
kind: SPAN_KIND_TO_OTLP[span.kind] ?? 1,
|
|
47
|
+
startTimeUnixNano: isoToUnixNano(span.startTime),
|
|
48
|
+
status: {
|
|
49
|
+
code: STATUS_CODE_TO_OTLP[span.statusCode] ?? 0,
|
|
50
|
+
message: span.statusMessage,
|
|
51
|
+
},
|
|
52
|
+
attributes,
|
|
53
|
+
};
|
|
54
|
+
if (span.parentSpanId)
|
|
55
|
+
otlp.parentSpanId = span.parentSpanId;
|
|
56
|
+
if (span.endTime)
|
|
57
|
+
otlp.endTimeUnixNano = isoToUnixNano(span.endTime);
|
|
58
|
+
if (span.events?.length) {
|
|
59
|
+
otlp.events = span.events.map(e => ({
|
|
60
|
+
timeUnixNano: isoToUnixNano(e.time),
|
|
61
|
+
name: e.name,
|
|
62
|
+
attributes: Object.entries(e.attributes ?? {}).map(([key, value]) => ({ key, value: toOtlpValue(value) })),
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
return otlp;
|
|
66
|
+
}
|
|
67
|
+
class TjClient {
|
|
68
|
+
baseUrl;
|
|
69
|
+
ingestSecret;
|
|
70
|
+
batchSize;
|
|
71
|
+
flushIntervalMs;
|
|
72
|
+
serviceName;
|
|
73
|
+
maxRetries;
|
|
74
|
+
buffer = [];
|
|
75
|
+
timer = null;
|
|
76
|
+
constructor(options) {
|
|
77
|
+
this.baseUrl = (options.baseUrl ?? "http://127.0.0.1:7391").replace(/\/$/, "");
|
|
78
|
+
this.ingestSecret = options.ingestSecret;
|
|
79
|
+
this.batchSize = options.batchSize ?? 50;
|
|
80
|
+
this.flushIntervalMs = options.flushIntervalMs ?? 5000;
|
|
81
|
+
this.serviceName = options.serviceName ?? "ocw-ts-sdk";
|
|
82
|
+
this.maxRetries = options.maxRetries ?? 3;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Start the automatic flush timer.
|
|
86
|
+
* Call this once after creating the client.
|
|
87
|
+
*/
|
|
88
|
+
start() {
|
|
89
|
+
if (this.timer)
|
|
90
|
+
return this;
|
|
91
|
+
this.timer = setInterval(() => {
|
|
92
|
+
void this.flush();
|
|
93
|
+
}, this.flushIntervalMs);
|
|
94
|
+
// Allow process to exit even if timer is running
|
|
95
|
+
if (this.timer.unref)
|
|
96
|
+
this.timer.unref();
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Add a span to the buffer. Auto-flushes when batchSize is reached.
|
|
101
|
+
*/
|
|
102
|
+
async send(span) {
|
|
103
|
+
this.buffer.push(span);
|
|
104
|
+
if (this.buffer.length >= this.batchSize) {
|
|
105
|
+
await this.flush();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Flush all buffered spans to the server.
|
|
110
|
+
* Returns the ingest result, or null if the buffer was empty.
|
|
111
|
+
*/
|
|
112
|
+
async flush() {
|
|
113
|
+
if (this.buffer.length === 0)
|
|
114
|
+
return null;
|
|
115
|
+
const spans = this.buffer.splice(0);
|
|
116
|
+
const batch = this.toBatch(spans);
|
|
117
|
+
return this.post(batch);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Flush remaining spans and stop the timer.
|
|
121
|
+
*/
|
|
122
|
+
async shutdown() {
|
|
123
|
+
if (this.timer) {
|
|
124
|
+
clearInterval(this.timer);
|
|
125
|
+
this.timer = null;
|
|
126
|
+
}
|
|
127
|
+
await this.flush();
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Convert SDK spans to OTLP JSON batch format.
|
|
131
|
+
*/
|
|
132
|
+
toBatch(spans) {
|
|
133
|
+
return {
|
|
134
|
+
resourceSpans: [
|
|
135
|
+
{
|
|
136
|
+
resource: {
|
|
137
|
+
attributes: [
|
|
138
|
+
{
|
|
139
|
+
key: "service.name",
|
|
140
|
+
value: { stringValue: this.serviceName },
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
scopeSpans: [
|
|
145
|
+
{
|
|
146
|
+
spans: spans.map(spanToOtlp),
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* POST a span batch to the ingest endpoint.
|
|
155
|
+
* Retries up to maxRetries times on network errors or 5xx responses using
|
|
156
|
+
* exponential backoff (base 2s, matching Python HttpTransport behaviour).
|
|
157
|
+
* 4xx errors are not retried — they indicate auth or validation failures.
|
|
158
|
+
*/
|
|
159
|
+
async post(batch) {
|
|
160
|
+
const url = `${this.baseUrl}/api/v1/spans`;
|
|
161
|
+
const body = JSON.stringify(batch);
|
|
162
|
+
const headers = {
|
|
163
|
+
"Content-Type": "application/json",
|
|
164
|
+
Authorization: `Bearer ${this.ingestSecret}`,
|
|
165
|
+
};
|
|
166
|
+
let lastError = null;
|
|
167
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
168
|
+
if (attempt > 0) {
|
|
169
|
+
// Exponential backoff: 2s, 4s, 8s
|
|
170
|
+
await new Promise(resolve => setTimeout(resolve, 2000 * Math.pow(2, attempt - 1)));
|
|
171
|
+
}
|
|
172
|
+
let response;
|
|
173
|
+
try {
|
|
174
|
+
response = await fetch(url, { method: "POST", headers, body });
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
// Network-level error — retry
|
|
178
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (response.ok) {
|
|
182
|
+
return (await response.json());
|
|
183
|
+
}
|
|
184
|
+
const text = await response.text().catch(() => "");
|
|
185
|
+
const error = new Error(`OCW ingest failed: ${response.status} ${response.statusText} — ${text}`);
|
|
186
|
+
// 4xx: not retriable (auth/validation failure)
|
|
187
|
+
if (response.status >= 400 && response.status < 500) {
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
// 5xx: retriable
|
|
191
|
+
lastError = error;
|
|
192
|
+
}
|
|
193
|
+
throw lastError ?? new Error("OCW ingest failed after retries");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
exports.TjClient = TjClient;
|
|
197
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_test_1 = require("node:test");
|
|
7
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const node_http_1 = require("node:http");
|
|
9
|
+
const client_js_1 = require("./client.js");
|
|
10
|
+
const span_builder_js_1 = require("./span-builder.js");
|
|
11
|
+
/**
|
|
12
|
+
* Spin up a local HTTP server that captures requests, so we can test
|
|
13
|
+
* the client without a real OCW server.
|
|
14
|
+
*/
|
|
15
|
+
function createMockServer() {
|
|
16
|
+
const requests = [];
|
|
17
|
+
let responseStatus = 200;
|
|
18
|
+
let responseBody = { ingested: 1, rejected: 0, rejections: [] };
|
|
19
|
+
const server = (0, node_http_1.createServer)((req, res) => {
|
|
20
|
+
let body = "";
|
|
21
|
+
req.on("data", (chunk) => {
|
|
22
|
+
body += chunk.toString();
|
|
23
|
+
});
|
|
24
|
+
req.on("end", () => {
|
|
25
|
+
requests.push({
|
|
26
|
+
method: req.method ?? "",
|
|
27
|
+
url: req.url ?? "",
|
|
28
|
+
headers: req.headers,
|
|
29
|
+
body,
|
|
30
|
+
});
|
|
31
|
+
res.writeHead(responseStatus, { "Content-Type": "application/json" });
|
|
32
|
+
res.end(JSON.stringify(responseBody));
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
server,
|
|
37
|
+
port: () => {
|
|
38
|
+
const addr = server.address();
|
|
39
|
+
if (addr && typeof addr === "object")
|
|
40
|
+
return addr.port;
|
|
41
|
+
throw new Error("Server not started");
|
|
42
|
+
},
|
|
43
|
+
requests,
|
|
44
|
+
setResponse(status, body) {
|
|
45
|
+
responseStatus = status;
|
|
46
|
+
responseBody = body;
|
|
47
|
+
},
|
|
48
|
+
async start() {
|
|
49
|
+
await new Promise((resolve) => {
|
|
50
|
+
server.listen(0, "127.0.0.1", resolve);
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
async stop() {
|
|
54
|
+
await new Promise((resolve, reject) => {
|
|
55
|
+
server.close((err) => (err ? reject(err) : resolve()));
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
(0, node_test_1.describe)("TjClient", () => {
|
|
61
|
+
let mock;
|
|
62
|
+
(0, node_test_1.beforeEach)(async () => {
|
|
63
|
+
mock = createMockServer();
|
|
64
|
+
await mock.start();
|
|
65
|
+
});
|
|
66
|
+
(0, node_test_1.afterEach)(async () => {
|
|
67
|
+
await mock.stop();
|
|
68
|
+
});
|
|
69
|
+
(0, node_test_1.it)("sends a span with correct auth header", async () => {
|
|
70
|
+
const client = new client_js_1.TjClient({
|
|
71
|
+
baseUrl: `http://127.0.0.1:${mock.port()}`,
|
|
72
|
+
ingestSecret: "test-secret-123",
|
|
73
|
+
batchSize: 1,
|
|
74
|
+
});
|
|
75
|
+
const span = new span_builder_js_1.SpanBuilder("gen_ai.llm.call")
|
|
76
|
+
.agentId("test-agent")
|
|
77
|
+
.provider("anthropic")
|
|
78
|
+
.model("claude-haiku-4-5")
|
|
79
|
+
.inputTokens(1000)
|
|
80
|
+
.outputTokens(200)
|
|
81
|
+
.build();
|
|
82
|
+
await client.send(span);
|
|
83
|
+
strict_1.default.equal(mock.requests.length, 1);
|
|
84
|
+
const req = mock.requests[0];
|
|
85
|
+
strict_1.default.equal(req.method, "POST");
|
|
86
|
+
strict_1.default.equal(req.url, "/api/v1/spans");
|
|
87
|
+
strict_1.default.equal(req.headers["authorization"], "Bearer test-secret-123");
|
|
88
|
+
strict_1.default.equal(req.headers["content-type"], "application/json");
|
|
89
|
+
});
|
|
90
|
+
(0, node_test_1.it)("sends OTLP JSON format", async () => {
|
|
91
|
+
const client = new client_js_1.TjClient({
|
|
92
|
+
baseUrl: `http://127.0.0.1:${mock.port()}`,
|
|
93
|
+
ingestSecret: "secret",
|
|
94
|
+
batchSize: 1,
|
|
95
|
+
});
|
|
96
|
+
const span = new span_builder_js_1.SpanBuilder("gen_ai.llm.call")
|
|
97
|
+
.agentId("agent-1")
|
|
98
|
+
.inputTokens(500)
|
|
99
|
+
.build();
|
|
100
|
+
await client.send(span);
|
|
101
|
+
const body = JSON.parse(mock.requests[0].body);
|
|
102
|
+
strict_1.default.ok(body.resourceSpans);
|
|
103
|
+
strict_1.default.ok(Array.isArray(body.resourceSpans));
|
|
104
|
+
strict_1.default.equal(body.resourceSpans.length, 1);
|
|
105
|
+
const scopeSpans = body.resourceSpans[0].scopeSpans;
|
|
106
|
+
strict_1.default.ok(Array.isArray(scopeSpans));
|
|
107
|
+
strict_1.default.equal(scopeSpans[0].spans.length, 1);
|
|
108
|
+
const otlpSpan = scopeSpans[0].spans[0];
|
|
109
|
+
strict_1.default.equal(otlpSpan.name, "gen_ai.llm.call");
|
|
110
|
+
strict_1.default.ok(otlpSpan.traceId);
|
|
111
|
+
strict_1.default.ok(otlpSpan.spanId);
|
|
112
|
+
strict_1.default.equal(otlpSpan.kind, 3); // CLIENT
|
|
113
|
+
strict_1.default.equal(otlpSpan.status.code, 1); // OK
|
|
114
|
+
});
|
|
115
|
+
(0, node_test_1.it)("batches spans and flushes at batchSize", async () => {
|
|
116
|
+
mock.setResponse(200, { ingested: 3, rejected: 0, rejections: [] });
|
|
117
|
+
const client = new client_js_1.TjClient({
|
|
118
|
+
baseUrl: `http://127.0.0.1:${mock.port()}`,
|
|
119
|
+
ingestSecret: "secret",
|
|
120
|
+
batchSize: 3,
|
|
121
|
+
});
|
|
122
|
+
// Send 2 spans — should not flush yet
|
|
123
|
+
await client.send(new span_builder_js_1.SpanBuilder("span-1").build());
|
|
124
|
+
await client.send(new span_builder_js_1.SpanBuilder("span-2").build());
|
|
125
|
+
strict_1.default.equal(mock.requests.length, 0);
|
|
126
|
+
// Third span triggers flush
|
|
127
|
+
await client.send(new span_builder_js_1.SpanBuilder("span-3").build());
|
|
128
|
+
strict_1.default.equal(mock.requests.length, 1);
|
|
129
|
+
const body = JSON.parse(mock.requests[0].body);
|
|
130
|
+
const spans = body.resourceSpans[0].scopeSpans[0].spans;
|
|
131
|
+
strict_1.default.equal(spans.length, 3);
|
|
132
|
+
});
|
|
133
|
+
(0, node_test_1.it)("flush sends remaining spans", async () => {
|
|
134
|
+
const client = new client_js_1.TjClient({
|
|
135
|
+
baseUrl: `http://127.0.0.1:${mock.port()}`,
|
|
136
|
+
ingestSecret: "secret",
|
|
137
|
+
batchSize: 100, // won't auto-flush
|
|
138
|
+
});
|
|
139
|
+
await client.send(new span_builder_js_1.SpanBuilder("span-1").build());
|
|
140
|
+
await client.send(new span_builder_js_1.SpanBuilder("span-2").build());
|
|
141
|
+
strict_1.default.equal(mock.requests.length, 0);
|
|
142
|
+
const result = await client.flush();
|
|
143
|
+
strict_1.default.equal(mock.requests.length, 1);
|
|
144
|
+
strict_1.default.ok(result);
|
|
145
|
+
strict_1.default.equal(result.ingested, 1); // mock default
|
|
146
|
+
});
|
|
147
|
+
(0, node_test_1.it)("flush on empty buffer returns null", async () => {
|
|
148
|
+
const client = new client_js_1.TjClient({
|
|
149
|
+
baseUrl: `http://127.0.0.1:${mock.port()}`,
|
|
150
|
+
ingestSecret: "secret",
|
|
151
|
+
});
|
|
152
|
+
const result = await client.flush();
|
|
153
|
+
strict_1.default.equal(result, null);
|
|
154
|
+
strict_1.default.equal(mock.requests.length, 0);
|
|
155
|
+
});
|
|
156
|
+
(0, node_test_1.it)("shutdown flushes and stops timer", async () => {
|
|
157
|
+
const client = new client_js_1.TjClient({
|
|
158
|
+
baseUrl: `http://127.0.0.1:${mock.port()}`,
|
|
159
|
+
ingestSecret: "secret",
|
|
160
|
+
batchSize: 100,
|
|
161
|
+
flushIntervalMs: 60000, // won't auto-flush in test
|
|
162
|
+
});
|
|
163
|
+
client.start();
|
|
164
|
+
await client.send(new span_builder_js_1.SpanBuilder("span-1").build());
|
|
165
|
+
await client.shutdown();
|
|
166
|
+
strict_1.default.equal(mock.requests.length, 1);
|
|
167
|
+
});
|
|
168
|
+
(0, node_test_1.it)("throws on server error", async () => {
|
|
169
|
+
mock.setResponse(401, { detail: "Invalid ingest secret" });
|
|
170
|
+
const client = new client_js_1.TjClient({
|
|
171
|
+
baseUrl: `http://127.0.0.1:${mock.port()}`,
|
|
172
|
+
ingestSecret: "wrong-secret",
|
|
173
|
+
batchSize: 1,
|
|
174
|
+
});
|
|
175
|
+
await strict_1.default.rejects(() => client.send(new span_builder_js_1.SpanBuilder("span-1").build()), (err) => {
|
|
176
|
+
strict_1.default.ok(err.message.includes("401"));
|
|
177
|
+
return true;
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
(0, node_test_1.it)("converts span attributes to OTLP format", async () => {
|
|
181
|
+
const client = new client_js_1.TjClient({
|
|
182
|
+
baseUrl: `http://127.0.0.1:${mock.port()}`,
|
|
183
|
+
ingestSecret: "secret",
|
|
184
|
+
batchSize: 1,
|
|
185
|
+
});
|
|
186
|
+
const span = new span_builder_js_1.SpanBuilder("gen_ai.llm.call")
|
|
187
|
+
.attribute("string.attr", "hello")
|
|
188
|
+
.attribute("int.attr", 42)
|
|
189
|
+
.attribute("float.attr", 3.14)
|
|
190
|
+
.attribute("bool.attr", true)
|
|
191
|
+
.build();
|
|
192
|
+
await client.send(span);
|
|
193
|
+
const body = JSON.parse(mock.requests[0].body);
|
|
194
|
+
const attrs = body.resourceSpans[0].scopeSpans[0].spans[0].attributes;
|
|
195
|
+
const attrMap = new Map(attrs.map((a) => [a.key, a.value]));
|
|
196
|
+
strict_1.default.deepEqual(attrMap.get("string.attr"), { stringValue: "hello" });
|
|
197
|
+
strict_1.default.deepEqual(attrMap.get("int.attr"), { intValue: "42" });
|
|
198
|
+
strict_1.default.deepEqual(attrMap.get("float.attr"), { doubleValue: 3.14 });
|
|
199
|
+
strict_1.default.deepEqual(attrMap.get("bool.attr"), { boolValue: true });
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
//# sourceMappingURL=client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.test.js","sourceRoot":"","sources":["../src/client.test.ts"],"names":[],"mappings":";;;;;AAAA,yCAAgE;AAChE,gEAAwC;AACxC,yCAAoF;AACpF,2CAAuC;AACvC,uDAAgD;AAGhD;;;GAGG;AACH,SAAS,gBAAgB;IAQvB,MAAM,QAAQ,GAKT,EAAE,CAAC;IACR,IAAI,cAAc,GAAG,GAAG,CAAC;IACzB,IAAI,YAAY,GAAY,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IAEzE,MAAM,MAAM,GAAG,IAAA,wBAAY,EAAC,CAAC,GAAoB,EAAE,GAAmB,EAAE,EAAE;QACxE,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAC/B,IAAI,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC3B,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACjB,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;gBACxB,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,EAAE;gBAClB,OAAO,EAAE,GAAG,CAAC,OAAiC;gBAC9C,IAAI;aACL,CAAC,CAAC;YACH,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;YACtE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,MAAM;QACN,IAAI,EAAE,GAAG,EAAE;YACT,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YAC9B,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC,IAAI,CAAC;YACvD,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACxC,CAAC;QACD,QAAQ;QACR,WAAW,CAAC,MAAc,EAAE,IAAa;YACvC,cAAc,GAAG,MAAM,CAAC;YACxB,YAAY,GAAG,IAAI,CAAC;QACtB,CAAC;QACD,KAAK,CAAC,KAAK;YACT,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBAClC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;YACzC,CAAC,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,IAAI;YACR,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC1C,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACzD,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED,IAAA,oBAAQ,EAAC,UAAU,EAAE,GAAG,EAAE;IACxB,IAAI,IAAyC,CAAC;IAE9C,IAAA,sBAAU,EAAC,KAAK,IAAI,EAAE;QACpB,IAAI,GAAG,gBAAgB,EAAE,CAAC;QAC1B,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,IAAA,qBAAS,EAAC,KAAK,IAAI,EAAE;QACnB,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,IAAA,cAAE,EAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,MAAM,GAAG,IAAI,oBAAQ,CAAC;YAC1B,OAAO,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE,EAAE;YAC1C,YAAY,EAAE,iBAAiB;YAC/B,SAAS,EAAE,CAAC;SACb,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,IAAI,6BAAW,CAAC,iBAAiB,CAAC;aAC5C,OAAO,CAAC,YAAY,CAAC;aACrB,QAAQ,CAAC,WAAW,CAAC;aACrB,KAAK,CAAC,kBAAkB,CAAC;aACzB,WAAW,CAAC,IAAI,CAAC;aACjB,YAAY,CAAC,GAAG,CAAC;aACjB,KAAK,EAAE,CAAC;QAEX,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAExB,gBAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC7B,gBAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,gBAAM,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;QACvC,gBAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,wBAAwB,CAAC,CAAC;QACrE,gBAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,kBAAkB,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,IAAA,cAAE,EAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QACtC,MAAM,MAAM,GAAG,IAAI,oBAAQ,CAAC;YAC1B,OAAO,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE,EAAE;YAC1C,YAAY,EAAE,QAAQ;YACtB,SAAS,EAAE,CAAC;SACb,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,IAAI,6BAAW,CAAC,iBAAiB,CAAC;aAC5C,OAAO,CAAC,SAAS,CAAC;aAClB,WAAW,CAAC,GAAG,CAAC;aAChB,KAAK,EAAE,CAAC;QAEX,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAExB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/C,gBAAM,CAAC,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC9B,gBAAM,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;QAC7C,gBAAM,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAE3C,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;QACpD,gBAAM,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;QACrC,gBAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAE5C,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACxC,gBAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;QAC/C,gBAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC5B,gBAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC3B,gBAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;QACzC,gBAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAA,cAAE,EAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;QAEpE,MAAM,MAAM,GAAG,IAAI,oBAAQ,CAAC;YAC1B,OAAO,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE,EAAE;YAC1C,YAAY,EAAE,QAAQ;YACtB,SAAS,EAAE,CAAC;SACb,CAAC,CAAC;QAEH,sCAAsC;QACtC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,6BAAW,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACrD,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,6BAAW,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACrD,gBAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAEtC,4BAA4B;QAC5B,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,6BAAW,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACrD,gBAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAEtC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACxD,gBAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,IAAA,cAAE,EAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;QAC3C,MAAM,MAAM,GAAG,IAAI,oBAAQ,CAAC;YAC1B,OAAO,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE,EAAE;YAC1C,YAAY,EAAE,QAAQ;YACtB,SAAS,EAAE,GAAG,EAAE,mBAAmB;SACpC,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,6BAAW,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACrD,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,6BAAW,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACrD,gBAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAEtC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACpC,gBAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACtC,gBAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;QAClB,gBAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,eAAe;IACnD,CAAC,CAAC,CAAC;IAEH,IAAA,cAAE,EAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;QAClD,MAAM,MAAM,GAAG,IAAI,oBAAQ,CAAC;YAC1B,OAAO,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE,EAAE;YAC1C,YAAY,EAAE,QAAQ;SACvB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACpC,gBAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC3B,gBAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,IAAA,cAAE,EAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAChD,MAAM,MAAM,GAAG,IAAI,oBAAQ,CAAC;YAC1B,OAAO,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE,EAAE;YAC1C,YAAY,EAAE,QAAQ;YACtB,SAAS,EAAE,GAAG;YACd,eAAe,EAAE,KAAK,EAAE,2BAA2B;SACpD,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,EAAE,CAAC;QAEf,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,6BAAW,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACrD,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;QAExB,gBAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,IAAA,cAAE,EAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QACtC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAE3D,MAAM,MAAM,GAAG,IAAI,oBAAQ,CAAC;YAC1B,OAAO,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE,EAAE;YAC1C,YAAY,EAAE,cAAc;YAC5B,SAAS,EAAE,CAAC;SACb,CAAC,CAAC;QAEH,MAAM,gBAAM,CAAC,OAAO,CAClB,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,6BAAW,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC,EACpD,CAAC,GAAU,EAAE,EAAE;YACb,gBAAM,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;YACvC,OAAO,IAAI,CAAC;QACd,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,cAAE,EAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACvD,MAAM,MAAM,GAAG,IAAI,oBAAQ,CAAC;YAC1B,OAAO,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE,EAAE;YAC1C,YAAY,EAAE,QAAQ;YACtB,SAAS,EAAE,CAAC;SACb,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,IAAI,6BAAW,CAAC,iBAAiB,CAAC;aAC5C,SAAS,CAAC,aAAa,EAAE,OAAO,CAAC;aACjC,SAAS,CAAC,UAAU,EAAE,EAAE,CAAC;aACzB,SAAS,CAAC,YAAY,EAAE,IAAI,CAAC;aAC7B,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC;aAC5B,KAAK,EAAE,CAAC;QAEX,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAExB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;QACtE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAkC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAE7F,gBAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;QACvE,gBAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9D,gBAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QACnE,gBAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { TjClient, type TjClientOptions } from "./client.js";
|
|
2
|
+
export { type Span, type SpanBatch, type IngestResult, SpanKind, SpanStatus } from "./types.js";
|
|
3
|
+
export { SpanBuilder } from "./span-builder.js";
|
|
4
|
+
export { GenAIAttributes, TjAttributes, ClaudeCodeEvents } from "./semconv.js";
|