@vymalo/opencode-otel 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +29 -17
- package/dist/config.js +210 -197
- package/dist/config.js.map +1 -1
- package/dist/deferred.d.ts +22 -0
- package/dist/deferred.js +29 -0
- package/dist/deferred.js.map +1 -0
- package/dist/export-logging.d.ts +28 -0
- package/dist/export-logging.js +74 -0
- package/dist/export-logging.js.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/instruments.d.ts +22 -22
- package/dist/instruments.js +95 -94
- package/dist/instruments.js.map +1 -1
- package/dist/lib.d.ts +5 -1
- package/dist/lib.js +6 -1
- package/dist/lib.js.map +1 -1
- package/dist/logging.d.ts +6 -6
- package/dist/logging.js +59 -62
- package/dist/logging.js.map +1 -1
- package/dist/opencode.d.ts +16 -14
- package/dist/opencode.js +165 -122
- package/dist/opencode.js.map +1 -1
- package/dist/propagation.d.ts +13 -13
- package/dist/propagation.js +50 -48
- package/dist/propagation.js.map +1 -1
- package/dist/providers.d.ts +46 -35
- package/dist/providers.js +217 -158
- package/dist/providers.js.map +1 -1
- package/dist/recorder.d.ts +150 -69
- package/dist/recorder.js +788 -539
- package/dist/recorder.js.map +1 -1
- package/dist/token-source.d.ts +59 -0
- package/dist/token-source.js +125 -0
- package/dist/token-source.js.map +1 -0
- package/dist/types.d.ts +92 -64
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/vcs.d.ts +63 -0
- package/dist/vcs.js +188 -0
- package/dist/vcs.js.map +1 -0
- package/package.json +3 -3
package/dist/config.d.ts
CHANGED
|
@@ -3,25 +3,37 @@ export declare const SIGNALS: readonly SignalName[];
|
|
|
3
3
|
/** Env source, injectable so tests never touch `process.env`. */
|
|
4
4
|
export type EnvSource = Record<string, string | undefined>;
|
|
5
5
|
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
* Parse the OTel `key=value,key2=value2` list format used by both
|
|
7
|
+
* `OTEL_EXPORTER_OTLP_HEADERS` and `OTEL_RESOURCE_ATTRIBUTES`. Values are
|
|
8
|
+
* percent-decoded per spec; an undecodable value is kept verbatim rather than
|
|
9
|
+
* dropping the whole pair.
|
|
10
|
+
*/
|
|
11
11
|
export declare function parseKeyValueList(raw: string | undefined): Record<string, string>;
|
|
12
12
|
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
* Normalise a command to argv. An array is taken verbatim — that is the form to
|
|
14
|
+
* use when a path contains spaces. A string is split on whitespace, with **no
|
|
15
|
+
* shell**: no quoting, no substitution, no injection surface.
|
|
16
|
+
*
|
|
17
|
+
* The split is decided by the type of the value actually supplied, not by the
|
|
18
|
+
* type of some other candidate. Keying it on the wrong source silently produced
|
|
19
|
+
* a single argv element like `"governance-auth token"` whenever an environment
|
|
20
|
+
* override sat on top of an array in config — which `execFile` then failed to
|
|
21
|
+
* spawn, so every export went out unauthenticated.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseCommand(raw: unknown): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Resolve plugin options against the environment.
|
|
26
|
+
*
|
|
27
|
+
* **Precedence: environment overrides plugin options.** Options are the base
|
|
28
|
+
* layer so a `.well-known/opencode` document can ship a working default for a
|
|
29
|
+
* whole organization; `OTEL_*` variables override per machine so a developer
|
|
30
|
+
* can point at a local collector without editing served config. See
|
|
31
|
+
* `plans/otel.md`.
|
|
32
|
+
*/
|
|
21
33
|
export declare function resolveOtelConfig(raw: unknown, env?: EnvSource): ResolvedOtelConfig;
|
|
22
34
|
/**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
* Resolve the OTLP URL for one signal. A per-signal endpoint is used verbatim
|
|
36
|
+
* (spec: it already includes the path); the base endpoint gets the signal path
|
|
37
|
+
* appended.
|
|
38
|
+
*/
|
|
27
39
|
export declare function signalUrl(config: ResolvedOtelConfig, signal: SignalName): string | undefined;
|
package/dist/config.js
CHANGED
|
@@ -1,224 +1,237 @@
|
|
|
1
|
-
|
|
1
|
+
import { DEFAULT_REFRESH_MS } from "./token-source.js";
|
|
2
|
+
export const SIGNALS = [
|
|
3
|
+
"traces",
|
|
4
|
+
"metrics",
|
|
5
|
+
"logs"
|
|
6
|
+
];
|
|
2
7
|
const DEFAULTS = {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
serviceName: "opencode",
|
|
9
|
+
metricExportIntervalMs: 6e4,
|
|
10
|
+
logExportIntervalMs: 5e3,
|
|
11
|
+
traceExportIntervalMs: 5e3,
|
|
12
|
+
metricTemporality: "delta"
|
|
8
13
|
};
|
|
9
14
|
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
* The `OTEL_*` variable names read per signal. The first entry of each tuple is
|
|
16
|
+
* the OTel-spec name; the second (where present) is the Claude-Code-compatible
|
|
17
|
+
* alias, accepted so an operator can move a working env block across tools.
|
|
18
|
+
*/
|
|
14
19
|
const SIGNAL_ENV = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
traces: {
|
|
21
|
+
exporter: "OTEL_TRACES_EXPORTER",
|
|
22
|
+
endpoint: "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
|
|
23
|
+
interval: ["OTEL_BSP_SCHEDULE_DELAY", "OTEL_TRACES_EXPORT_INTERVAL"]
|
|
24
|
+
},
|
|
25
|
+
metrics: {
|
|
26
|
+
exporter: "OTEL_METRICS_EXPORTER",
|
|
27
|
+
endpoint: "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT",
|
|
28
|
+
interval: ["OTEL_METRIC_EXPORT_INTERVAL"]
|
|
29
|
+
},
|
|
30
|
+
logs: {
|
|
31
|
+
exporter: "OTEL_LOGS_EXPORTER",
|
|
32
|
+
endpoint: "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT",
|
|
33
|
+
interval: ["OTEL_BLRP_SCHEDULE_DELAY", "OTEL_LOGS_EXPORT_INTERVAL"]
|
|
34
|
+
}
|
|
30
35
|
};
|
|
31
36
|
function parseBool(value) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
if (value === undefined) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
if (/^(1|true|yes|on)$/i.test(value)) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
if (/^(0|false|no|off)$/i.test(value)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
42
47
|
}
|
|
43
48
|
function parsePositiveInt(value) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
if (value === undefined) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const n = Number.parseInt(value.trim(), 10);
|
|
53
|
+
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
49
54
|
}
|
|
50
55
|
/**
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
* Parse the OTel `key=value,key2=value2` list format used by both
|
|
57
|
+
* `OTEL_EXPORTER_OTLP_HEADERS` and `OTEL_RESOURCE_ATTRIBUTES`. Values are
|
|
58
|
+
* percent-decoded per spec; an undecodable value is kept verbatim rather than
|
|
59
|
+
* dropping the whole pair.
|
|
60
|
+
*/
|
|
56
61
|
export function parseKeyValueList(raw) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return out;
|
|
62
|
+
if (!raw) {
|
|
63
|
+
return {};
|
|
64
|
+
}
|
|
65
|
+
const out = {};
|
|
66
|
+
for (const pair of raw.split(",")) {
|
|
67
|
+
const eq = pair.indexOf("=");
|
|
68
|
+
if (eq <= 0) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const key = pair.slice(0, eq).trim();
|
|
72
|
+
const rawValue = pair.slice(eq + 1).trim();
|
|
73
|
+
if (!key) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
out[key] = decodeURIComponent(rawValue);
|
|
78
|
+
} catch {
|
|
79
|
+
out[key] = rawValue;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
79
83
|
}
|
|
80
84
|
function parseExporter(value) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
if (value === undefined) {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
// The spec allows a comma-separated list; we honour the first recognised
|
|
89
|
+
// entry rather than fanning out to multiple exporters.
|
|
90
|
+
for (const candidate of value.split(",").map((v) => v.trim().toLowerCase())) {
|
|
91
|
+
if (candidate === "otlp" || candidate === "console" || candidate === "none") {
|
|
92
|
+
return candidate;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return undefined;
|
|
92
96
|
}
|
|
93
97
|
function firstDefined(env, keys) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
for (const key of keys) {
|
|
99
|
+
const value = env[key];
|
|
100
|
+
if (value !== undefined && value !== "") {
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return undefined;
|
|
101
105
|
}
|
|
102
106
|
function stringRecord(raw) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return out;
|
|
107
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
108
|
+
return {};
|
|
109
|
+
}
|
|
110
|
+
const out = {};
|
|
111
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
112
|
+
if (typeof value === "string") {
|
|
113
|
+
out[key] = value;
|
|
114
|
+
} else if (typeof value === "number" || typeof value === "boolean") {
|
|
115
|
+
out[key] = String(value);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
116
119
|
}
|
|
117
120
|
function stringList(raw) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
.filter((v) => v !== "");
|
|
126
|
-
}
|
|
127
|
-
return [];
|
|
121
|
+
if (Array.isArray(raw)) {
|
|
122
|
+
return raw.filter((v) => typeof v === "string" && v.trim() !== "");
|
|
123
|
+
}
|
|
124
|
+
if (typeof raw === "string") {
|
|
125
|
+
return raw.split(",").map((v) => v.trim()).filter((v) => v !== "");
|
|
126
|
+
}
|
|
127
|
+
return [];
|
|
128
128
|
}
|
|
129
129
|
/**
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
130
|
+
* Normalise a command to argv. An array is taken verbatim — that is the form to
|
|
131
|
+
* use when a path contains spaces. A string is split on whitespace, with **no
|
|
132
|
+
* shell**: no quoting, no substitution, no injection surface.
|
|
133
|
+
*
|
|
134
|
+
* The split is decided by the type of the value actually supplied, not by the
|
|
135
|
+
* type of some other candidate. Keying it on the wrong source silently produced
|
|
136
|
+
* a single argv element like `"governance-auth token"` whenever an environment
|
|
137
|
+
* override sat on top of an array in config — which `execFile` then failed to
|
|
138
|
+
* spawn, so every export went out unauthenticated.
|
|
139
|
+
*/
|
|
140
|
+
export function parseCommand(raw) {
|
|
141
|
+
if (Array.isArray(raw)) {
|
|
142
|
+
return raw.filter((part) => typeof part === "string" && part.trim() !== "");
|
|
143
|
+
}
|
|
144
|
+
if (typeof raw === "string") {
|
|
145
|
+
return raw.split(/\s+/).filter((part) => part !== "");
|
|
146
|
+
}
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Resolve plugin options against the environment.
|
|
151
|
+
*
|
|
152
|
+
* **Precedence: environment overrides plugin options.** Options are the base
|
|
153
|
+
* layer so a `.well-known/opencode` document can ship a working default for a
|
|
154
|
+
* whole organization; `OTEL_*` variables override per machine so a developer
|
|
155
|
+
* can point at a local collector without editing served config. See
|
|
156
|
+
* `plans/otel.md`.
|
|
157
|
+
*/
|
|
138
158
|
export function resolveOtelConfig(raw, env = process.env) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
? opts.traceExportIntervalMs
|
|
199
|
-
: DEFAULTS.traceExportIntervalMs),
|
|
200
|
-
metricTemporality: temporality,
|
|
201
|
-
includeSessionId: parseBool(env.OTEL_METRICS_INCLUDE_SESSION_ID) ?? opts.includeSessionId === true,
|
|
202
|
-
filteredTools,
|
|
203
|
-
propagateTraceContext: parseBool(env.OPENCODE_OTEL_PROPAGATE_TRACE_CONTEXT) ??
|
|
204
|
-
(opts.propagateTraceContext !== false && exporters.traces !== "none")
|
|
205
|
-
};
|
|
159
|
+
const opts = raw ?? {};
|
|
160
|
+
const enabled = parseBool(env.OPENCODE_OTEL_ENABLED) ?? opts.enabled !== false;
|
|
161
|
+
const endpoint = env.OTEL_EXPORTER_OTLP_ENDPOINT || opts.endpoint || undefined;
|
|
162
|
+
const endpoints = {};
|
|
163
|
+
for (const signal of SIGNALS) {
|
|
164
|
+
const value = env[SIGNAL_ENV[signal].endpoint] || opts.endpoints?.[signal];
|
|
165
|
+
if (value) {
|
|
166
|
+
endpoints[signal] = value;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Env headers merge over option headers key-by-key rather than replacing the
|
|
170
|
+
// map, so a machine can override just its auth token without restating the
|
|
171
|
+
// whole set the served config provided.
|
|
172
|
+
const headers = {
|
|
173
|
+
...stringRecord(opts.headers),
|
|
174
|
+
...parseKeyValueList(env.OTEL_EXPORTER_OTLP_HEADERS)
|
|
175
|
+
};
|
|
176
|
+
// An endpoint anywhere is what makes OTLP the implicit default; with none
|
|
177
|
+
// configured the plugin stays completely inert.
|
|
178
|
+
const hasEndpoint = Boolean(endpoint) || SIGNALS.some((s) => Boolean(endpoints[s]));
|
|
179
|
+
const fallback = hasEndpoint ? "otlp" : "none";
|
|
180
|
+
const exporters = {};
|
|
181
|
+
for (const signal of SIGNALS) {
|
|
182
|
+
exporters[signal] = parseExporter(env[SIGNAL_ENV[signal].exporter]) ?? opts.exporters?.[signal] ?? fallback;
|
|
183
|
+
}
|
|
184
|
+
const resourceAttributes = {
|
|
185
|
+
...stringRecord(opts.resourceAttributes),
|
|
186
|
+
...parseKeyValueList(env.OTEL_RESOURCE_ATTRIBUTES)
|
|
187
|
+
};
|
|
188
|
+
const temporality = env.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE?.trim().toLowerCase() === "cumulative" ? "cumulative" : env.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE?.trim().toLowerCase() === "delta" ? "delta" : opts.metricTemporality ?? DEFAULTS.metricTemporality;
|
|
189
|
+
const filteredTools = new Set(env.OTEL_OPENCODE_FILTERED_TOOLS !== undefined ? stringList(env.OTEL_OPENCODE_FILTERED_TOOLS) : stringList(opts.filteredTools));
|
|
190
|
+
// Resolve the source first, then normalise based on *its* own type — see
|
|
191
|
+
// `parseCommand` for why keying the split on anything else is a trap.
|
|
192
|
+
const tokenCommand = parseCommand(env.OPENCODE_OTEL_TOKEN_COMMAND ?? opts.tokenCommand);
|
|
193
|
+
const enabledSignals = SIGNALS.filter((s) => exporters[s] !== "none");
|
|
194
|
+
return {
|
|
195
|
+
enabled,
|
|
196
|
+
active: enabled && enabledSignals.length > 0,
|
|
197
|
+
endpoint,
|
|
198
|
+
endpoints,
|
|
199
|
+
headers,
|
|
200
|
+
tokenCommand,
|
|
201
|
+
tokenHeader: env.OPENCODE_OTEL_TOKEN_HEADER || opts.tokenHeader || "Authorization",
|
|
202
|
+
tokenPrefix: env.OPENCODE_OTEL_TOKEN_PREFIX ?? opts.tokenPrefix ?? "Bearer ",
|
|
203
|
+
tokenRefreshMs: parsePositiveInt(env.OPENCODE_OTEL_TOKEN_REFRESH_MS) ?? (opts.tokenRefreshMs && opts.tokenRefreshMs > 0 ? opts.tokenRefreshMs : DEFAULT_REFRESH_MS),
|
|
204
|
+
tokenTimeoutMs: parsePositiveInt(env.OPENCODE_OTEL_TOKEN_TIMEOUT_MS) ?? (opts.tokenTimeoutMs && opts.tokenTimeoutMs > 0 ? opts.tokenTimeoutMs : 1e4),
|
|
205
|
+
exporters,
|
|
206
|
+
serviceName: env.OTEL_SERVICE_NAME || opts.serviceName || DEFAULTS.serviceName,
|
|
207
|
+
environment: env.OPENCODE_OTEL_ENVIRONMENT || opts.environment || undefined,
|
|
208
|
+
resourceAttributes,
|
|
209
|
+
collectVcs: parseBool(env.OPENCODE_OTEL_COLLECT_VCS) ?? opts.collectVcs !== false,
|
|
210
|
+
metricExportIntervalMs: parsePositiveInt(firstDefined(env, SIGNAL_ENV.metrics.interval)) ?? (opts.metricExportIntervalMs && opts.metricExportIntervalMs > 0 ? opts.metricExportIntervalMs : DEFAULTS.metricExportIntervalMs),
|
|
211
|
+
logExportIntervalMs: parsePositiveInt(firstDefined(env, SIGNAL_ENV.logs.interval)) ?? (opts.logExportIntervalMs && opts.logExportIntervalMs > 0 ? opts.logExportIntervalMs : DEFAULTS.logExportIntervalMs),
|
|
212
|
+
traceExportIntervalMs: parsePositiveInt(firstDefined(env, SIGNAL_ENV.traces.interval)) ?? (opts.traceExportIntervalMs && opts.traceExportIntervalMs > 0 ? opts.traceExportIntervalMs : DEFAULTS.traceExportIntervalMs),
|
|
213
|
+
metricTemporality: temporality,
|
|
214
|
+
includeSessionId: parseBool(env.OTEL_METRICS_INCLUDE_SESSION_ID) ?? opts.includeSessionId === true,
|
|
215
|
+
filteredTools,
|
|
216
|
+
propagateTraceContext: parseBool(env.OPENCODE_OTEL_PROPAGATE_TRACE_CONTEXT) ?? (opts.propagateTraceContext !== false && exporters.traces !== "none")
|
|
217
|
+
};
|
|
206
218
|
}
|
|
207
219
|
/**
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
220
|
+
* Resolve the OTLP URL for one signal. A per-signal endpoint is used verbatim
|
|
221
|
+
* (spec: it already includes the path); the base endpoint gets the signal path
|
|
222
|
+
* appended.
|
|
223
|
+
*/
|
|
212
224
|
export function signalUrl(config, signal) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
225
|
+
const explicit = config.endpoints[signal];
|
|
226
|
+
if (explicit) {
|
|
227
|
+
return explicit;
|
|
228
|
+
}
|
|
229
|
+
if (!config.endpoint) {
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
const base = config.endpoint.replace(/\/+$/, "");
|
|
233
|
+
const path = signal === "traces" ? "v1/traces" : signal === "metrics" ? "v1/metrics" : "v1/logs";
|
|
234
|
+
return `${base}/${path}`;
|
|
223
235
|
}
|
|
236
|
+
|
|
224
237
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"
|
|
1
|
+
{"mappings":"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","names":[],"sources":["../src/config.ts"],"version":3,"file":"config.js","sourceRoot":""}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A resource attribute whose value only becomes known after the plugin has
|
|
3
|
+
* already started — OpenCode reports the host version and the git branch as
|
|
4
|
+
* *events*, not as plugin input, but an OTel `Resource` is fixed at provider
|
|
5
|
+
* construction.
|
|
6
|
+
*
|
|
7
|
+
* The OTel resource API accepts promise-valued attributes and exporters await
|
|
8
|
+
* them before the first export, so a deferred value is the supported way to
|
|
9
|
+
* bridge that gap. The timeout is not optional: a promise that never settles
|
|
10
|
+
* would block every export forever, so an attribute that has not arrived in
|
|
11
|
+
* time resolves to `undefined` and is dropped from the resource.
|
|
12
|
+
*/
|
|
13
|
+
export interface DeferredAttribute {
|
|
14
|
+
/** Hand this to `resourceFromAttributes`. */
|
|
15
|
+
readonly value: Promise<string | undefined>;
|
|
16
|
+
/** Settle it with the real value. Later calls are ignored. */
|
|
17
|
+
settle(value: string | undefined): void;
|
|
18
|
+
/** Give up and settle as absent. Idempotent. */
|
|
19
|
+
abandon(): void;
|
|
20
|
+
}
|
|
21
|
+
export declare const DEFAULT_DEFERRED_TIMEOUT_MS = 2e3;
|
|
22
|
+
export declare function deferredAttribute(timeoutMs?: number, schedule?: typeof setTimeout): DeferredAttribute;
|
package/dist/deferred.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const DEFAULT_DEFERRED_TIMEOUT_MS = 2e3;
|
|
2
|
+
export function deferredAttribute(timeoutMs = DEFAULT_DEFERRED_TIMEOUT_MS, schedule = setTimeout) {
|
|
3
|
+
let resolve = () => {};
|
|
4
|
+
let settled = false;
|
|
5
|
+
const value = new Promise((r) => {
|
|
6
|
+
resolve = r;
|
|
7
|
+
});
|
|
8
|
+
const finish = (next) => {
|
|
9
|
+
if (settled) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
settled = true;
|
|
13
|
+
resolve(next);
|
|
14
|
+
};
|
|
15
|
+
const timer = schedule(() => finish(undefined), timeoutMs);
|
|
16
|
+
// Never hold a short CLI invocation open just to wait for an attribute.
|
|
17
|
+
timer.unref?.();
|
|
18
|
+
return {
|
|
19
|
+
value,
|
|
20
|
+
settle: (next) => {
|
|
21
|
+
if (next !== undefined && next !== "") {
|
|
22
|
+
finish(next);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
abandon: () => finish(undefined)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=deferred.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"mappings":"AAqBA,OAAO,MAAM,8BAA8B;AAE3C,OAAO,SAAS,kBACd,YAAoB,6BACpB,WAA8B,YACX;CACnB,IAAI,gBAAqD,CAAC;CAC1D,IAAI,UAAU;CACd,MAAM,QAAQ,IAAI,SAA6B,MAAM;EACnD,UAAU;CACZ,CAAC;CAED,MAAM,UAAU,SAAmC;EACjD,IAAI,SAAS;GACX;EACF;EACA,UAAU;EACV,QAAQ,IAAI;CACd;CAEA,MAAM,QAAQ,eAAe,OAAO,SAAS,GAAG,SAAS;;CAEzD,AAAC,MAAiC,QAAQ;CAE1C,OAAO;EACL;EACA,SAAS,SAAS;GAChB,IAAI,SAAS,aAAa,SAAS,IAAI;IACrC,OAAO,IAAI;GACb;EACF;EACA,eAAe,OAAO,SAAS;CACjC;AACF","names":[],"sources":["../src/deferred.ts"],"version":3,"file":"deferred.js","sourceRoot":""}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type ExportResult } from "@opentelemetry/core";
|
|
2
|
+
import type { Logger } from "./logging.js";
|
|
3
|
+
import type { SignalName } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* The shape all three OTel exporters share: `export(items, resultCallback)`.
|
|
6
|
+
* Typed structurally rather than against `SpanExporter | PushMetricExporter |
|
|
7
|
+
* LogRecordExporter`, whose item types are unrelated.
|
|
8
|
+
*/
|
|
9
|
+
export interface ExporterLike {
|
|
10
|
+
export(items: never, resultCallback: (result: ExportResult) => void): void;
|
|
11
|
+
shutdown(): Promise<void>;
|
|
12
|
+
forceFlush?(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Wrap an exporter so a failed export reaches the host log stream.
|
|
16
|
+
*
|
|
17
|
+
* Without this, a rejected OTLP request — an expired credential, a wrong
|
|
18
|
+
* audience, an unreachable collector — is reported only through the OTel SDK's
|
|
19
|
+
* own `diag` channel, which nothing here subscribes to. The visible symptom is
|
|
20
|
+
* that telemetry silently stops while the session looks entirely healthy.
|
|
21
|
+
*
|
|
22
|
+
* Deliberately not `diag.setLogger`: that is process-global and would hijack
|
|
23
|
+
* the channel for the host and every other plugin. Decorating our own exporters
|
|
24
|
+
* observes exactly the failures we caused and nothing else.
|
|
25
|
+
*/
|
|
26
|
+
export declare function withFailureLogging<T extends ExporterLike>(exporter: T, signal: SignalName, logger: Logger, options?: {
|
|
27
|
+
onFailure?: () => void;
|
|
28
|
+
}): T;
|