@agent-native/otel 0.0.1-bootstrap.0 → 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/README.md CHANGED
@@ -1,3 +1,36 @@
1
1
  # @agent-native/otel
2
2
 
3
- Placeholder version used to create the package on npm. Install the latest version instead.
3
+ OpenTelemetry SDK wiring for Agent-Native apps. It builds a meter provider and a tracer provider with OTLP exporters and registers them with `@agent-native/core` through `registerObservabilityProvider()`.
4
+
5
+ It does nothing unless an OTLP endpoint is set, so apps can depend on it unconditionally. Apps scaffolded from first-party templates already include it; to add it to another app:
6
+
7
+ ```sh
8
+ pnpm add @agent-native/otel
9
+ ```
10
+
11
+ Then start it from a server plugin:
12
+
13
+ ```ts
14
+ // server/plugins/otel.ts
15
+ import { defineNitroPlugin } from "@agent-native/core/server";
16
+ import { startAgentNativeOtel } from "@agent-native/otel";
17
+
18
+ export default defineNitroPlugin(() => {
19
+ startAgentNativeOtel();
20
+ });
21
+ ```
22
+
23
+ Configuration uses the standard OpenTelemetry variables:
24
+
25
+ | Variable | Effect |
26
+ | ---------------------------------------------------------- | ------------------------------------------------------------------ |
27
+ | `OTEL_EXPORTER_OTLP_ENDPOINT` | Turns both signals on; `/v1/metrics` and `/v1/traces` are appended |
28
+ | `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` / `_TRACES_ENDPOINT` | Turns on that signal only, at this exact URL |
29
+ | `OTEL_EXPORTER_OTLP_HEADERS` | Headers sent with every export, e.g. `Authorization=Bearer%20…` |
30
+ | `OTEL_SERVICE_NAME` | `service.name` |
31
+ | `OTEL_RESOURCE_ATTRIBUTES` | Extra resource attributes, e.g. `deployment.environment.name` |
32
+ | `OTEL_TRACES_SAMPLER` / `_ARG` | Trace sampling, e.g. `parentbased_traceidratio` / `0.01` |
33
+ | `OTEL_METRICS_EXPORTER` / `OTEL_TRACES_EXPORTER` | `otlp` (default) or `none`; any other value fails startup |
34
+ | `OTEL_EXPORTER_OTLP_PROTOCOL` (and per-signal overrides) | `http/protobuf` only; any other value fails startup |
35
+
36
+ Metrics use cumulative temporality with a random `service.instance.id` per process, so each process is its own series. On serverless runtimes (Netlify, AWS Lambda, Vercel, Cloudflare), as `@agent-native/core` detects them, core force-flushes both providers on every response, capped at 2 seconds; elsewhere metrics export every 60 seconds.
@@ -0,0 +1,21 @@
1
+ export interface AgentNativeOtelHandle {
2
+ readonly flushOnResponse: boolean;
3
+ shutdown(): Promise<void>;
4
+ }
5
+ /**
6
+ * Start the OpenTelemetry SDK and register it with `@agent-native/core`.
7
+ *
8
+ * A no-op returning `undefined` unless an OTLP endpoint is set, either the
9
+ * shared `OTEL_EXPORTER_OTLP_ENDPOINT` or a signal's own
10
+ * `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` / `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`;
11
+ * only signals with an endpoint start. Everything is read from `process.env`,
12
+ * as the SDK itself does: exporters read the standard `OTEL_EXPORTER_OTLP_*`
13
+ * variables, the resource reads `OTEL_SERVICE_NAME` and
14
+ * `OTEL_RESOURCE_ATTRIBUTES`, and the trace sampler reads
15
+ * `OTEL_TRACES_SAMPLER` / `OTEL_TRACES_SAMPLER_ARG`. Set
16
+ * `OTEL_METRICS_EXPORTER=none` or `OTEL_TRACES_EXPORTER=none` to turn off one
17
+ * signal; any exporter other than `otlp` or `none` throws. Calling it again
18
+ * returns the running instance.
19
+ */
20
+ export declare function startAgentNativeOtel(): AgentNativeOtelHandle | undefined;
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgCA,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAgED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,IAAI,qBAAqB,GAAG,SAAS,CAyDxE"}
package/dist/index.js ADDED
@@ -0,0 +1,131 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { isServerlessRuntime, registerObservabilityProvider, } from "@agent-native/core/server";
3
+ import { context } from "@opentelemetry/api";
4
+ import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
5
+ import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-proto";
6
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
7
+ import { defaultResource, detectResources, envDetector, resourceFromAttributes, } from "@opentelemetry/resources";
8
+ import { AggregationTemporality, MeterProvider, PeriodicExportingMetricReader, } from "@opentelemetry/sdk-metrics";
9
+ import { BasicTracerProvider, BatchSpanProcessor, } from "@opentelemetry/sdk-trace-base";
10
+ const METRIC_EXPORT_INTERVAL_MS = 60_000;
11
+ let started;
12
+ function envValue(key) {
13
+ return process.env[key]?.trim() || undefined;
14
+ }
15
+ // A signal exports when an OTLP endpoint applies to it: its own
16
+ // OTEL_EXPORTER_OTLP_<SIGNAL>_ENDPOINT or the shared base endpoint. An
17
+ // exporter this package cannot build fails startup, endpoint or not, instead of
18
+ // being ignored or quietly shipping the signal to the collector over OTLP.
19
+ function signalEnabled(signal) {
20
+ const exporterKey = `OTEL_${signal}_EXPORTER`;
21
+ const exporter = envValue(exporterKey)?.toLowerCase() ?? "otlp";
22
+ if (exporter === "none")
23
+ return false;
24
+ if (exporter !== "otlp") {
25
+ throw new Error(`${exporterKey}="${exporter}" is not supported by @agent-native/otel. Set it to "otlp" or "none".`);
26
+ }
27
+ // The exporters are the http/protobuf ones, so any other transport would
28
+ // start cleanly and then send the wrong wire format to the collector.
29
+ for (const protocolKey of [
30
+ `OTEL_EXPORTER_OTLP_${signal}_PROTOCOL`,
31
+ "OTEL_EXPORTER_OTLP_PROTOCOL",
32
+ ]) {
33
+ const protocol = envValue(protocolKey)?.toLowerCase();
34
+ if (protocol === undefined)
35
+ continue;
36
+ if (protocol !== "http/protobuf") {
37
+ throw new Error(`${protocolKey}="${protocol}" is not supported by @agent-native/otel. Set it to "http/protobuf" or leave it unset.`);
38
+ }
39
+ break;
40
+ }
41
+ return Boolean(envValue(`OTEL_EXPORTER_OTLP_${signal}_ENDPOINT`) ??
42
+ envValue("OTEL_EXPORTER_OTLP_ENDPOINT"));
43
+ }
44
+ // Cumulative counters restart with every process, so each process must be its
45
+ // own series. OTEL_RESOURCE_ATTRIBUTES can still override the id.
46
+ function buildResource() {
47
+ return defaultResource()
48
+ .merge(resourceFromAttributes({ "service.instance.id": randomUUID() }))
49
+ .merge(detectResources({ detectors: [envDetector] }));
50
+ }
51
+ function withoutForceFlush(provider) {
52
+ return new Proxy(provider, {
53
+ get(target, property, receiver) {
54
+ if (property === "forceFlush")
55
+ return undefined;
56
+ const value = Reflect.get(target, property, receiver);
57
+ return typeof value === "function" ? value.bind(target) : value;
58
+ },
59
+ });
60
+ }
61
+ /**
62
+ * Start the OpenTelemetry SDK and register it with `@agent-native/core`.
63
+ *
64
+ * A no-op returning `undefined` unless an OTLP endpoint is set, either the
65
+ * shared `OTEL_EXPORTER_OTLP_ENDPOINT` or a signal's own
66
+ * `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` / `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`;
67
+ * only signals with an endpoint start. Everything is read from `process.env`,
68
+ * as the SDK itself does: exporters read the standard `OTEL_EXPORTER_OTLP_*`
69
+ * variables, the resource reads `OTEL_SERVICE_NAME` and
70
+ * `OTEL_RESOURCE_ATTRIBUTES`, and the trace sampler reads
71
+ * `OTEL_TRACES_SAMPLER` / `OTEL_TRACES_SAMPLER_ARG`. Set
72
+ * `OTEL_METRICS_EXPORTER=none` or `OTEL_TRACES_EXPORTER=none` to turn off one
73
+ * signal; any exporter other than `otlp` or `none` throws. Calling it again
74
+ * returns the running instance.
75
+ */
76
+ export function startAgentNativeOtel() {
77
+ if (started)
78
+ return started;
79
+ const metricsEnabled = signalEnabled("METRICS");
80
+ const tracesEnabled = signalEnabled("TRACES");
81
+ if (!metricsEnabled && !tracesEnabled)
82
+ return undefined;
83
+ const resource = buildResource();
84
+ // Serverless functions freeze between invocations, so a periodic reader
85
+ // never fires and core must flush on every response. A long-running server
86
+ // exports on the timer instead of once per request.
87
+ const flushOnResponse = isServerlessRuntime();
88
+ const provider = {};
89
+ const shutdowns = [];
90
+ if (metricsEnabled) {
91
+ const meterProvider = new MeterProvider({
92
+ resource,
93
+ readers: [
94
+ new PeriodicExportingMetricReader({
95
+ exporter: new OTLPMetricExporter({
96
+ temporalityPreference: AggregationTemporality.CUMULATIVE,
97
+ }),
98
+ exportIntervalMillis: METRIC_EXPORT_INTERVAL_MS,
99
+ }),
100
+ ],
101
+ });
102
+ provider.meterProvider = flushOnResponse
103
+ ? meterProvider
104
+ : withoutForceFlush(meterProvider);
105
+ shutdowns.push(() => meterProvider.shutdown());
106
+ }
107
+ if (tracesEnabled) {
108
+ context.setGlobalContextManager(new AsyncLocalStorageContextManager().enable());
109
+ const tracerProvider = new BasicTracerProvider({
110
+ resource,
111
+ spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter())],
112
+ });
113
+ provider.tracerProvider = flushOnResponse
114
+ ? tracerProvider
115
+ : withoutForceFlush(tracerProvider);
116
+ shutdowns.push(() => tracerProvider.shutdown());
117
+ }
118
+ const unregister = registerObservabilityProvider(provider);
119
+ const handle = {
120
+ flushOnResponse,
121
+ async shutdown() {
122
+ unregister();
123
+ if (started === handle)
124
+ started = undefined;
125
+ await Promise.all(shutdowns.map((shutdown) => shutdown()));
126
+ },
127
+ };
128
+ started = handle;
129
+ return handle;
130
+ }
131
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EACL,mBAAmB,EACnB,6BAA6B,GAI9B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,+BAA+B,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAC;AAC7E,OAAO,EAEL,eAAe,EACf,eAAe,EACf,WAAW,EACX,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,sBAAsB,EACtB,aAAa,EACb,6BAA6B,GAC9B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,yBAAyB,GAAG,MAAM,CAAC;AAOzC,IAAI,OAA0C,CAAC;AAI/C,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;AAC/C,CAAC;AAED,gEAAgE;AAChE,uEAAuE;AACvE,gFAAgF;AAChF,2EAA2E;AAC3E,SAAS,aAAa,CAAC,MAAc;IACnC,MAAM,WAAW,GAAG,QAAQ,MAAM,WAAW,CAAC;IAC9C,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,IAAI,MAAM,CAAC;IAChE,IAAI,QAAQ,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,GAAG,WAAW,KAAK,QAAQ,uEAAuE,CACnG,CAAC;IACJ,CAAC;IACD,yEAAyE;IACzE,sEAAsE;IACtE,KAAK,MAAM,WAAW,IAAI;QACxB,sBAAsB,MAAM,WAAW;QACvC,6BAA6B;KAC9B,EAAE,CAAC;QACF,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;QACtD,IAAI,QAAQ,KAAK,SAAS;YAAE,SAAS;QACrC,IAAI,QAAQ,KAAK,eAAe,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CACb,GAAG,WAAW,KAAK,QAAQ,wFAAwF,CACpH,CAAC;QACJ,CAAC;QACD,MAAM;IACR,CAAC;IACD,OAAO,OAAO,CACZ,QAAQ,CAAC,sBAAsB,MAAM,WAAW,CAAC;QACjD,QAAQ,CAAC,6BAA6B,CAAC,CACxC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,kEAAkE;AAClE,SAAS,aAAa;IACpB,OAAO,eAAe,EAAE;SACrB,KAAK,CAAC,sBAAsB,CAAC,EAAE,qBAAqB,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;SACtE,KAAK,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,iBAAiB,CAExB,QAAW;IACX,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE;QACzB,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ;YAC5B,IAAI,QAAQ,KAAK,YAAY;gBAAE,OAAO,SAAS,CAAC;YAChD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACtD,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAClE,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,oBAAoB;IAClC,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,MAAM,cAAc,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAChD,MAAM,aAAa,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC9C,IAAI,CAAC,cAAc,IAAI,CAAC,aAAa;QAAE,OAAO,SAAS,CAAC;IAExD,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;IACjC,wEAAwE;IACxE,2EAA2E;IAC3E,oDAAoD;IACpD,MAAM,eAAe,GAAG,mBAAmB,EAAE,CAAC;IAC9C,MAAM,QAAQ,GAA0B,EAAE,CAAC;IAC3C,MAAM,SAAS,GAA+B,EAAE,CAAC;IAEjD,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC;YACtC,QAAQ;YACR,OAAO,EAAE;gBACP,IAAI,6BAA6B,CAAC;oBAChC,QAAQ,EAAE,IAAI,kBAAkB,CAAC;wBAC/B,qBAAqB,EAAE,sBAAsB,CAAC,UAAU;qBACzD,CAAC;oBACF,oBAAoB,EAAE,yBAAyB;iBAChD,CAAC;aACH;SACF,CAAC,CAAC;QACH,QAAQ,CAAC,aAAa,GAAG,eAAe;YACtC,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;QACrC,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,CAAC,uBAAuB,CAC7B,IAAI,+BAA+B,EAAE,CAAC,MAAM,EAAE,CAC/C,CAAC;QACF,MAAM,cAAc,GAAG,IAAI,mBAAmB,CAAC;YAC7C,QAAQ;YACR,cAAc,EAAE,CAAC,IAAI,kBAAkB,CAAC,IAAI,iBAAiB,EAAE,CAAC,CAAC;SAClE,CAAC,CAAC;QACH,QAAQ,CAAC,cAAc,GAAG,eAAe;YACvC,CAAC,CAAC,cAAc;YAChB,CAAC,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;QACtC,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,UAAU,GAAG,6BAA6B,CAAC,QAAQ,CAAC,CAAC;IAC3D,MAAM,MAAM,GAA0B;QACpC,eAAe;QACf,KAAK,CAAC,QAAQ;YACZ,UAAU,EAAE,CAAC;YACb,IAAI,OAAO,KAAK,MAAM;gBAAE,OAAO,GAAG,SAAS,CAAC;YAC5C,MAAM,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC7D,CAAC;KACF,CAAC;IACF,OAAO,GAAG,MAAM,CAAC;IACjB,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,55 @@
1
1
  {
2
2
  "name": "@agent-native/otel",
3
- "version": "0.0.1-bootstrap.0",
4
-
5
- "description": "Placeholder to create the package; real releases are published by CI."
6
- }
3
+ "version": "0.1.0",
4
+ "description": "OpenTelemetry SDK wiring for Agent-Native apps: exports core's metrics and traces over OTLP when OTEL_EXPORTER_OTLP_ENDPOINT is set.",
5
+ "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/BuilderIO/agent-native/issues"
8
+ },
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/BuilderIO/agent-native",
13
+ "directory": "packages/otel"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "type": "module",
20
+ "sideEffects": false,
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js"
25
+ }
26
+ },
27
+ "publishConfig": {
28
+ "access": "public",
29
+ "provenance": true
30
+ },
31
+ "dependencies": {
32
+ "@opentelemetry/api": "^1.9.1",
33
+ "@opentelemetry/context-async-hooks": "^2.11.0",
34
+ "@opentelemetry/exporter-metrics-otlp-proto": "^0.222.0",
35
+ "@opentelemetry/exporter-trace-otlp-proto": "^0.222.0",
36
+ "@opentelemetry/resources": "^2.11.0",
37
+ "@opentelemetry/sdk-metrics": "^2.11.0",
38
+ "@opentelemetry/sdk-trace-base": "^2.11.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^24.2.1",
42
+ "typescript-7": "npm:typescript@^7.0.2",
43
+ "vitest": "^4.1.5",
44
+ "@agent-native/core": "0.203.0"
45
+ },
46
+ "peerDependencies": {
47
+ "@agent-native/core": ">=0.200.0"
48
+ },
49
+ "scripts": {
50
+ "build": "tsc",
51
+ "dev": "tsc --watch",
52
+ "test": "vitest --run src",
53
+ "typecheck": "tsc --noEmit"
54
+ }
55
+ }