@nightowlsdev/telemetry-core 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Night Owls contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # @nightowlsdev/telemetry-core
2
+
3
+ The shared **SwarmSpan → OpenTelemetry replay** for the Night Owls telemetry family. When a
4
+ swarm run finishes, `@nightowlsdev/core`'s engine hands a batch of pre-recorded `SwarmSpan`s
5
+ (one `run`, one `generation` per model call, one `tool` per tool call) to a
6
+ `TelemetryExporter`. This package replays those spans through a `BasicTracerProvider` as
7
+ real OpenTelemetry spans: it derives a valid 32-hex trace id from the run id, nests the
8
+ children under the run span, and maps generations to `gen_ai.*` semantic conventions. The
9
+ two backend adapters (`@nightowlsdev/telemetry-otel`, `@nightowlsdev/telemetry-langfuse`) are thin
10
+ shells over this — they differ only in the `SpanProcessor` they pass in.
11
+
12
+ It never imports `@mastra/*` and re-exports no Mastra types (the engine wall, CONTRACTS §1).
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pnpm add @nightowlsdev/telemetry-core @nightowlsdev/core
18
+ ```
19
+
20
+ OpenTelemetry (`@opentelemetry/api`, `@opentelemetry/sdk-trace-base`,
21
+ `@opentelemetry/resources`, `@opentelemetry/semantic-conventions`) ships as a dependency.
22
+
23
+ ## What it does
24
+
25
+ - **`deriveTraceId(runId)`** — a UUID run id becomes its dash-stripped 32-lowercase-hex form;
26
+ any other run id is sha256-hashed to 32 hex. Deterministic, so a run always maps to the
27
+ same trace.
28
+ - **`createSpanReplayer({ exporter | processor, serviceName?, resourceAttributes? })`** —
29
+ builds a long-lived provider. `replay(spans)` groups spans by run id, forces the derived
30
+ trace id via a synthetic parent `SpanContext`, opens the `run` span first and parents the
31
+ `generation`/`tool` children under it (`trace.setSpan`), replays the original epoch-ms
32
+ timings, then **awaits `provider.forceFlush()`** so nothing is lost in a short-lived /
33
+ serverless invocation.
34
+ - **`replayerExporter(opts)`** — wraps a replayer as a `TelemetryExporter` so an adapter can
35
+ return it directly from its factory.
36
+ - **gen_ai mapping** — generation spans carry `gen_ai.request.model`,
37
+ `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens` (from
38
+ `@opentelemetry/semantic-conventions/incubating`) plus `gen_ai.usage.cost_usd`, so both
39
+ OTLP backends and Langfuse's default `gen_ai.*` filter pick them up.
40
+
41
+ ## Usage (building an adapter)
42
+
43
+ ```ts
44
+ import { replayerExporter } from "@nightowlsdev/telemetry-core";
45
+ import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
46
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
47
+
48
+ export function myTelemetry(opts) {
49
+ const processor = new BatchSpanProcessor(new OTLPTraceExporter({ url: opts.url }));
50
+ return replayerExporter({ processor, serviceName: "nightowls" });
51
+ }
52
+ ```
53
+
54
+ You usually don't depend on this package directly — use `@nightowlsdev/telemetry-otel` or
55
+ `@nightowlsdev/telemetry-langfuse`, then wire it into `defineSwarm({ telemetry })`.
56
+
57
+ ## Multi-backend: send to OTLP **and** Langfuse at once
58
+
59
+ `@nightowlsdev/core`'s `compositeTelemetry` fans one span batch out to many exporters, isolating
60
+ each one (`Promise.allSettled` — a throwing exporter never blocks the others or the run):
61
+
62
+ ```ts
63
+ import { defineSwarm, compositeTelemetry } from "@nightowlsdev/core";
64
+ import { otelTelemetry } from "@nightowlsdev/telemetry-otel";
65
+ import { langfuseTelemetry } from "@nightowlsdev/telemetry-langfuse";
66
+
67
+ const swarm = defineSwarm({
68
+ storage, agents, models: { allow: ["openai/gpt-5.5"] }, modelFactory,
69
+ cost: { maxSteps: 8, maxCostUsd: 1 },
70
+ telemetry: compositeTelemetry([
71
+ otelTelemetry({ url, headers }),
72
+ langfuseTelemetry({ publicKey, secretKey }),
73
+ ]),
74
+ });
75
+ ```
76
+
77
+ `defineSwarm({ telemetry })` also accepts a bare array (`telemetry: [otelTelemetry(...),
78
+ langfuseTelemetry(...)]`) — it's composed the same way. `customTelemetry(fn)` wraps any
79
+ `(spans) => void | Promise<void>` into an exporter.
80
+
81
+ ## Engine wall
82
+
83
+ The built `dist/index.d.ts` contains zero `@mastra` references (enforced by
84
+ `test/wall.test.ts`). It consumes only `@nightowlsdev/core`'s `SwarmSpan`/`TelemetryExporter`.
package/dist/index.cjs ADDED
@@ -0,0 +1,146 @@
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
+ KIND_MAP: () => KIND_MAP,
24
+ createSpanReplayer: () => createSpanReplayer,
25
+ deriveRootSpanId: () => deriveRootSpanId,
26
+ deriveTraceId: () => deriveTraceId,
27
+ replayerExporter: () => replayerExporter,
28
+ toOtelAttributes: () => toOtelAttributes
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/replay.ts
33
+ var import_api2 = require("@opentelemetry/api");
34
+ var import_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
35
+ var import_resources = require("@opentelemetry/resources");
36
+
37
+ // src/trace-id.ts
38
+ var import_node_crypto = require("crypto");
39
+ var HEX32 = /^[0-9a-f]{32}$/;
40
+ function deriveTraceId(runId) {
41
+ const stripped = runId.replace(/-/g, "").toLowerCase();
42
+ if (HEX32.test(stripped)) return stripped;
43
+ return (0, import_node_crypto.createHash)("sha256").update(runId).digest("hex").slice(0, 32);
44
+ }
45
+ function deriveRootSpanId(runId) {
46
+ return (0, import_node_crypto.createHash)("sha256").update(`root:${runId}`).digest("hex").slice(0, 16);
47
+ }
48
+
49
+ // src/attributes.ts
50
+ var import_api = require("@opentelemetry/api");
51
+ var import_incubating = require("@opentelemetry/semantic-conventions/incubating");
52
+ var KIND_MAP = {
53
+ run: import_api.SpanKind.INTERNAL,
54
+ delegation: import_api.SpanKind.INTERNAL,
55
+ generation: import_api.SpanKind.INTERNAL,
56
+ tool: import_api.SpanKind.CLIENT,
57
+ event: import_api.SpanKind.INTERNAL,
58
+ recall: import_api.SpanKind.INTERNAL
59
+ // R4: a memory recall is an internal read (like run/event)
60
+ };
61
+ function coerce(v) {
62
+ if (v == null) return void 0;
63
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") return v;
64
+ return JSON.stringify(v);
65
+ }
66
+ function toOtelAttributes(span) {
67
+ const out = { "nightowls.span.kind": span.kind };
68
+ for (const [k, v] of Object.entries(span.attributes)) {
69
+ const c = coerce(v);
70
+ if (c !== void 0) out[k] = c;
71
+ }
72
+ if (span.kind === "generation") {
73
+ const a = span.attributes;
74
+ if (typeof a.modelId === "string") out[import_incubating.ATTR_GEN_AI_REQUEST_MODEL] = a.modelId;
75
+ if (typeof a.inputTokens === "number") out[import_incubating.ATTR_GEN_AI_USAGE_INPUT_TOKENS] = a.inputTokens;
76
+ if (typeof a.outputTokens === "number") out[import_incubating.ATTR_GEN_AI_USAGE_OUTPUT_TOKENS] = a.outputTokens;
77
+ if (typeof a.costUsd === "number") out["gen_ai.usage.cost_usd"] = a.costUsd;
78
+ }
79
+ return out;
80
+ }
81
+
82
+ // src/replay.ts
83
+ function createSpanReplayer(opts) {
84
+ const processor = opts.processor ?? new import_sdk_trace_base.SimpleSpanProcessor(
85
+ opts.exporter ?? (() => {
86
+ throw new Error("createSpanReplayer needs `exporter` or `processor`");
87
+ })()
88
+ );
89
+ const provider = new import_sdk_trace_base.BasicTracerProvider({
90
+ resource: (0, import_resources.resourceFromAttributes)({
91
+ "service.name": opts.serviceName ?? "nightowls",
92
+ ...opts.resourceAttributes
93
+ }),
94
+ spanProcessors: [processor]
95
+ });
96
+ const tracer = provider.getTracer("@nightowlsdev/telemetry");
97
+ return {
98
+ async replay(spans) {
99
+ if (spans.length === 0) return;
100
+ const groups = /* @__PURE__ */ new Map();
101
+ for (const s of spans) {
102
+ const g = groups.get(s.traceId);
103
+ if (g) g.push(s);
104
+ else groups.set(s.traceId, [s]);
105
+ }
106
+ for (const [runId, group] of groups) {
107
+ const traceId = deriveTraceId(runId);
108
+ const rootSc = {
109
+ traceId,
110
+ spanId: deriveRootSpanId(runId),
111
+ traceFlags: import_api2.TraceFlags.SAMPLED,
112
+ isRemote: false
113
+ };
114
+ const rootCtx = import_api2.trace.setSpanContext(import_api2.context.active(), rootSc);
115
+ const ordered = [...group].sort(
116
+ (a, b) => a.kind === "run" ? -1 : b.kind === "run" ? 1 : a.startedAt - b.startedAt
117
+ );
118
+ let runSpan;
119
+ for (const sw of ordered) {
120
+ const parentCtx = sw.kind === "run" || !runSpan ? rootCtx : import_api2.trace.setSpan(import_api2.context.active(), runSpan);
121
+ const span = tracer.startSpan(sw.name, { kind: KIND_MAP[sw.kind], startTime: sw.startedAt }, parentCtx);
122
+ span.setAttributes(toOtelAttributes(sw));
123
+ span.end(sw.endedAt ?? sw.startedAt);
124
+ if (sw.kind === "run") runSpan = span;
125
+ }
126
+ }
127
+ await provider.forceFlush();
128
+ },
129
+ async shutdown() {
130
+ await provider.shutdown();
131
+ }
132
+ };
133
+ }
134
+ function replayerExporter(opts) {
135
+ const replayer = createSpanReplayer(opts);
136
+ return { export: (spans) => replayer.replay(spans) };
137
+ }
138
+ // Annotate the CommonJS export names for ESM import in node:
139
+ 0 && (module.exports = {
140
+ KIND_MAP,
141
+ createSpanReplayer,
142
+ deriveRootSpanId,
143
+ deriveTraceId,
144
+ replayerExporter,
145
+ toOtelAttributes
146
+ });
@@ -0,0 +1,30 @@
1
+ import { SpanExporter, SpanProcessor } from '@opentelemetry/sdk-trace-base';
2
+ import { SwarmSpan, TelemetryExporter } from '@nightowlsdev/core';
3
+ import { SpanKind, Attributes } from '@opentelemetry/api';
4
+
5
+ interface SpanReplayerOpts {
6
+ /** Provide a SpanExporter (telemetry-otel: OTLP) OR a full SpanProcessor (telemetry-langfuse). */
7
+ exporter?: SpanExporter;
8
+ processor?: SpanProcessor;
9
+ serviceName?: string;
10
+ resourceAttributes?: Record<string, string>;
11
+ }
12
+ interface SpanReplayer {
13
+ replay(spans: SwarmSpan[]): Promise<void>;
14
+ shutdown(): Promise<void>;
15
+ }
16
+ /** Build a long-lived provider; `replay` re-plays a batch of pre-recorded SwarmSpans as real OTel spans. */
17
+ declare function createSpanReplayer(opts: SpanReplayerOpts): SpanReplayer;
18
+ /** Convenience: wrap a replayer as a TelemetryExporter (so adapters export directly). */
19
+ declare function replayerExporter(opts: SpanReplayerOpts): TelemetryExporter;
20
+
21
+ /** Derive a valid 32-lowercase-hex OTel trace id from any runId (UUID → strip dashes; else sha256). */
22
+ declare function deriveTraceId(runId: string): string;
23
+ /** Derive a stable 16-hex span id for the synthetic trace root (so the run span has a parent to anchor the traceId). */
24
+ declare function deriveRootSpanId(runId: string): string;
25
+
26
+ declare const KIND_MAP: Record<SwarmSpan["kind"], SpanKind>;
27
+ /** Map a SwarmSpan's attributes to OTel attributes, lifting generation usage/model/cost to gen_ai.* semconv. */
28
+ declare function toOtelAttributes(span: SwarmSpan): Attributes;
29
+
30
+ export { KIND_MAP, type SpanReplayer, type SpanReplayerOpts, createSpanReplayer, deriveRootSpanId, deriveTraceId, replayerExporter, toOtelAttributes };
@@ -0,0 +1,30 @@
1
+ import { SpanExporter, SpanProcessor } from '@opentelemetry/sdk-trace-base';
2
+ import { SwarmSpan, TelemetryExporter } from '@nightowlsdev/core';
3
+ import { SpanKind, Attributes } from '@opentelemetry/api';
4
+
5
+ interface SpanReplayerOpts {
6
+ /** Provide a SpanExporter (telemetry-otel: OTLP) OR a full SpanProcessor (telemetry-langfuse). */
7
+ exporter?: SpanExporter;
8
+ processor?: SpanProcessor;
9
+ serviceName?: string;
10
+ resourceAttributes?: Record<string, string>;
11
+ }
12
+ interface SpanReplayer {
13
+ replay(spans: SwarmSpan[]): Promise<void>;
14
+ shutdown(): Promise<void>;
15
+ }
16
+ /** Build a long-lived provider; `replay` re-plays a batch of pre-recorded SwarmSpans as real OTel spans. */
17
+ declare function createSpanReplayer(opts: SpanReplayerOpts): SpanReplayer;
18
+ /** Convenience: wrap a replayer as a TelemetryExporter (so adapters export directly). */
19
+ declare function replayerExporter(opts: SpanReplayerOpts): TelemetryExporter;
20
+
21
+ /** Derive a valid 32-lowercase-hex OTel trace id from any runId (UUID → strip dashes; else sha256). */
22
+ declare function deriveTraceId(runId: string): string;
23
+ /** Derive a stable 16-hex span id for the synthetic trace root (so the run span has a parent to anchor the traceId). */
24
+ declare function deriveRootSpanId(runId: string): string;
25
+
26
+ declare const KIND_MAP: Record<SwarmSpan["kind"], SpanKind>;
27
+ /** Map a SwarmSpan's attributes to OTel attributes, lifting generation usage/model/cost to gen_ai.* semconv. */
28
+ declare function toOtelAttributes(span: SwarmSpan): Attributes;
29
+
30
+ export { KIND_MAP, type SpanReplayer, type SpanReplayerOpts, createSpanReplayer, deriveRootSpanId, deriveTraceId, replayerExporter, toOtelAttributes };
package/dist/index.js ADDED
@@ -0,0 +1,121 @@
1
+ // src/replay.ts
2
+ import { context, trace, TraceFlags } from "@opentelemetry/api";
3
+ import {
4
+ BasicTracerProvider,
5
+ SimpleSpanProcessor
6
+ } from "@opentelemetry/sdk-trace-base";
7
+ import { resourceFromAttributes } from "@opentelemetry/resources";
8
+
9
+ // src/trace-id.ts
10
+ import { createHash } from "crypto";
11
+ var HEX32 = /^[0-9a-f]{32}$/;
12
+ function deriveTraceId(runId) {
13
+ const stripped = runId.replace(/-/g, "").toLowerCase();
14
+ if (HEX32.test(stripped)) return stripped;
15
+ return createHash("sha256").update(runId).digest("hex").slice(0, 32);
16
+ }
17
+ function deriveRootSpanId(runId) {
18
+ return createHash("sha256").update(`root:${runId}`).digest("hex").slice(0, 16);
19
+ }
20
+
21
+ // src/attributes.ts
22
+ import { SpanKind } from "@opentelemetry/api";
23
+ import {
24
+ ATTR_GEN_AI_REQUEST_MODEL,
25
+ ATTR_GEN_AI_USAGE_INPUT_TOKENS,
26
+ ATTR_GEN_AI_USAGE_OUTPUT_TOKENS
27
+ } from "@opentelemetry/semantic-conventions/incubating";
28
+ var KIND_MAP = {
29
+ run: SpanKind.INTERNAL,
30
+ delegation: SpanKind.INTERNAL,
31
+ generation: SpanKind.INTERNAL,
32
+ tool: SpanKind.CLIENT,
33
+ event: SpanKind.INTERNAL,
34
+ recall: SpanKind.INTERNAL
35
+ // R4: a memory recall is an internal read (like run/event)
36
+ };
37
+ function coerce(v) {
38
+ if (v == null) return void 0;
39
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") return v;
40
+ return JSON.stringify(v);
41
+ }
42
+ function toOtelAttributes(span) {
43
+ const out = { "nightowls.span.kind": span.kind };
44
+ for (const [k, v] of Object.entries(span.attributes)) {
45
+ const c = coerce(v);
46
+ if (c !== void 0) out[k] = c;
47
+ }
48
+ if (span.kind === "generation") {
49
+ const a = span.attributes;
50
+ if (typeof a.modelId === "string") out[ATTR_GEN_AI_REQUEST_MODEL] = a.modelId;
51
+ if (typeof a.inputTokens === "number") out[ATTR_GEN_AI_USAGE_INPUT_TOKENS] = a.inputTokens;
52
+ if (typeof a.outputTokens === "number") out[ATTR_GEN_AI_USAGE_OUTPUT_TOKENS] = a.outputTokens;
53
+ if (typeof a.costUsd === "number") out["gen_ai.usage.cost_usd"] = a.costUsd;
54
+ }
55
+ return out;
56
+ }
57
+
58
+ // src/replay.ts
59
+ function createSpanReplayer(opts) {
60
+ const processor = opts.processor ?? new SimpleSpanProcessor(
61
+ opts.exporter ?? (() => {
62
+ throw new Error("createSpanReplayer needs `exporter` or `processor`");
63
+ })()
64
+ );
65
+ const provider = new BasicTracerProvider({
66
+ resource: resourceFromAttributes({
67
+ "service.name": opts.serviceName ?? "nightowls",
68
+ ...opts.resourceAttributes
69
+ }),
70
+ spanProcessors: [processor]
71
+ });
72
+ const tracer = provider.getTracer("@nightowlsdev/telemetry");
73
+ return {
74
+ async replay(spans) {
75
+ if (spans.length === 0) return;
76
+ const groups = /* @__PURE__ */ new Map();
77
+ for (const s of spans) {
78
+ const g = groups.get(s.traceId);
79
+ if (g) g.push(s);
80
+ else groups.set(s.traceId, [s]);
81
+ }
82
+ for (const [runId, group] of groups) {
83
+ const traceId = deriveTraceId(runId);
84
+ const rootSc = {
85
+ traceId,
86
+ spanId: deriveRootSpanId(runId),
87
+ traceFlags: TraceFlags.SAMPLED,
88
+ isRemote: false
89
+ };
90
+ const rootCtx = trace.setSpanContext(context.active(), rootSc);
91
+ const ordered = [...group].sort(
92
+ (a, b) => a.kind === "run" ? -1 : b.kind === "run" ? 1 : a.startedAt - b.startedAt
93
+ );
94
+ let runSpan;
95
+ for (const sw of ordered) {
96
+ const parentCtx = sw.kind === "run" || !runSpan ? rootCtx : trace.setSpan(context.active(), runSpan);
97
+ const span = tracer.startSpan(sw.name, { kind: KIND_MAP[sw.kind], startTime: sw.startedAt }, parentCtx);
98
+ span.setAttributes(toOtelAttributes(sw));
99
+ span.end(sw.endedAt ?? sw.startedAt);
100
+ if (sw.kind === "run") runSpan = span;
101
+ }
102
+ }
103
+ await provider.forceFlush();
104
+ },
105
+ async shutdown() {
106
+ await provider.shutdown();
107
+ }
108
+ };
109
+ }
110
+ function replayerExporter(opts) {
111
+ const replayer = createSpanReplayer(opts);
112
+ return { export: (spans) => replayer.replay(spans) };
113
+ }
114
+ export {
115
+ KIND_MAP,
116
+ createSpanReplayer,
117
+ deriveRootSpanId,
118
+ deriveTraceId,
119
+ replayerExporter,
120
+ toOtelAttributes
121
+ };
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@nightowlsdev/telemetry-core",
3
+ "version": "0.1.2",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/cueplusplus/corale.git",
12
+ "directory": "packages/telemetry-core"
13
+ },
14
+ "homepage": "https://github.com/cueplusplus/corale#readme",
15
+ "sideEffects": false,
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "require": "./dist/index.cjs"
21
+ }
22
+ },
23
+ "main": "./dist/index.cjs",
24
+ "module": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "dependencies": {
30
+ "@opentelemetry/api": "^1.9.0",
31
+ "@opentelemetry/core": "^2.0.1",
32
+ "@opentelemetry/sdk-trace-base": "^2.0.1",
33
+ "@opentelemetry/resources": "^2.0.1",
34
+ "@opentelemetry/semantic-conventions": "^1.36.0"
35
+ },
36
+ "peerDependencies": {
37
+ "@nightowlsdev/core": "0.3.0"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^24.12.4",
41
+ "tsup": "8.5.1",
42
+ "typescript": "6.0.3",
43
+ "vitest": "^3.2.0",
44
+ "@nightowlsdev/core": "0.3.0",
45
+ "@nightowlsdev/tsconfig": "0.0.0",
46
+ "@nightowlsdev/eslint-config": "0.0.0"
47
+ },
48
+ "scripts": {
49
+ "build": "tsup",
50
+ "typecheck": "tsc --noEmit",
51
+ "test": "vitest run",
52
+ "lint": "eslint src"
53
+ }
54
+ }