@vymalo/opencode-otel 0.12.0 → 0.14.1
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 +20 -1
- 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/opencode.d.ts
CHANGED
|
@@ -3,20 +3,22 @@ import { type EnvSource } from "./config.js";
|
|
|
3
3
|
import { type Logger } from "./logging.js";
|
|
4
4
|
import { type ExporterFactories } from "./providers.js";
|
|
5
5
|
export interface OtelPluginFactoryOptions {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
logger?: Logger;
|
|
7
|
+
/** Injected environment; defaults to `process.env`. */
|
|
8
|
+
env?: EnvSource;
|
|
9
|
+
/** Substitute exporters (tests use in-memory ones). */
|
|
10
|
+
exporters?: ExporterFactories;
|
|
11
|
+
/** Injectable clock; defaults to `Date.now`. */
|
|
12
|
+
now?: () => number;
|
|
13
|
+
/** Skip `beforeExit`/`SIGINT`/`SIGTERM` registration (tests). */
|
|
14
|
+
registerProcessHandlers?: boolean;
|
|
15
|
+
/** Override the resolved host metadata (hostname, version) for tests. */
|
|
16
|
+
hostInfo?: {
|
|
17
|
+
hostname?: string;
|
|
18
|
+
version?: string;
|
|
19
|
+
};
|
|
20
|
+
/** How long a deferred resource attribute waits for its event. */
|
|
21
|
+
deferredTimeoutMs?: number;
|
|
20
22
|
}
|
|
21
23
|
export declare function createOtelPlugin(factoryOptions?: OtelPluginFactoryOptions): Plugin;
|
|
22
24
|
export declare const OpencodeOtelPlugin: Plugin;
|
package/dist/opencode.js
CHANGED
|
@@ -1,141 +1,184 @@
|
|
|
1
1
|
import { hostname } from "node:os";
|
|
2
2
|
import { resolveOtelConfig } from "./config.js";
|
|
3
|
+
import { deferredAttribute } from "./deferred.js";
|
|
3
4
|
import { createJsonConsoleLogger, DEFAULT_LOG_LEVEL, fromOpenCodeLogLevel, LOG_LEVEL_PRIORITY } from "./logging.js";
|
|
4
5
|
import { installTracePropagation } from "./propagation.js";
|
|
5
6
|
import { buildResource, createProviders, describeError } from "./providers.js";
|
|
6
7
|
import { TelemetryRecorder } from "./recorder.js";
|
|
8
|
+
import { readVcsInfo } from "./vcs.js";
|
|
7
9
|
const PLUGIN_SERVICE_NAME = "opencode-otel-plugin";
|
|
8
10
|
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
* Pipe plugin logs through OpenCode's `client.app.log` so they show up in the
|
|
12
|
+
* host's structured log stream, with the JSON console as a reliable fallback.
|
|
13
|
+
* Mirrors the rest of the suite.
|
|
14
|
+
*
|
|
15
|
+
* Note this is the plugin's *own* diagnostic logging — entirely separate from
|
|
16
|
+
* the OTLP logs signal it exports.
|
|
17
|
+
*/
|
|
16
18
|
function createOpenCodeLogger(client, getMinLevel) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
19
|
+
const fallback = createJsonConsoleLogger("debug");
|
|
20
|
+
const consoleAll = /^(1|true|yes|on)$/i.test(process.env.VYMALO_PLUGIN_CONSOLE_LOG ?? "");
|
|
21
|
+
const write = (level, event, fields) => {
|
|
22
|
+
if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[getMinLevel()]) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (consoleAll || level === "warn" || level === "error") {
|
|
26
|
+
fallback[level](event, fields);
|
|
27
|
+
}
|
|
28
|
+
const hostLevel = level === "trace" ? "debug" : level;
|
|
29
|
+
void client.app.log({ body: {
|
|
30
|
+
service: PLUGIN_SERVICE_NAME,
|
|
31
|
+
level: hostLevel,
|
|
32
|
+
message: event,
|
|
33
|
+
extra: fields
|
|
34
|
+
} }).catch(() => {
|
|
35
|
+
/* best-effort */
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
trace: (event, fields) => write("trace", event, fields),
|
|
40
|
+
debug: (event, fields) => write("debug", event, fields),
|
|
41
|
+
info: (event, fields) => write("info", event, fields),
|
|
42
|
+
warn: (event, fields) => write("warn", event, fields),
|
|
43
|
+
error: (event, fields) => write("error", event, fields)
|
|
44
|
+
};
|
|
42
45
|
}
|
|
43
46
|
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
function registerExitHandlers(providers, logger) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
47
|
+
* Drain buffered telemetry on process exit. The plugin API has no dispose hook,
|
|
48
|
+
* so without this a short CLI invocation loses everything still in a batch
|
|
49
|
+
* processor. Handlers are registered once and never keep the loop alive.
|
|
50
|
+
*/
|
|
51
|
+
function registerExitHandlers(providers, logger, deferred) {
|
|
52
|
+
let done = false;
|
|
53
|
+
const drain = () => {
|
|
54
|
+
if (done) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
done = true;
|
|
58
|
+
// Settle any still-pending resource attribute first. Exporters await those
|
|
59
|
+
// promises, and their timers are `unref`'d — so on `beforeExit` the timer
|
|
60
|
+
// may never fire and the shutdown would hang, losing everything buffered.
|
|
61
|
+
for (const attribute of deferred) {
|
|
62
|
+
attribute.abandon();
|
|
63
|
+
}
|
|
64
|
+
void providers.shutdown().catch((error) => {
|
|
65
|
+
logger.warn("otel_shutdown_failed", { error: describeError(error) });
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
process.once("beforeExit", drain);
|
|
69
|
+
process.once("SIGINT", drain);
|
|
70
|
+
process.once("SIGTERM", drain);
|
|
62
71
|
}
|
|
63
72
|
export function createOtelPlugin(factoryOptions = {}) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
73
|
+
return async (input, pluginOptions) => {
|
|
74
|
+
let currentLogLevel = DEFAULT_LOG_LEVEL;
|
|
75
|
+
const logger = factoryOptions.logger ?? createOpenCodeLogger(input.client, () => currentLogLevel);
|
|
76
|
+
const config = resolveOtelConfig(pluginOptions, factoryOptions.env ?? process.env);
|
|
77
|
+
if (!config.active) {
|
|
78
|
+
// No endpoint and no explicit exporter means the plugin was installed but
|
|
79
|
+
// never configured — cost nothing rather than half-initializing.
|
|
80
|
+
logger.info("otel_plugin_inactive", {
|
|
81
|
+
enabled: config.enabled,
|
|
82
|
+
reason: config.enabled ? "no_exporter_configured" : "disabled"
|
|
83
|
+
});
|
|
84
|
+
return { config: async (hostConfig) => {
|
|
85
|
+
currentLogLevel = fromOpenCodeLogLevel(hostConfig.logLevel) ?? DEFAULT_LOG_LEVEL;
|
|
86
|
+
} };
|
|
87
|
+
}
|
|
88
|
+
// `service.version` and the git branch only ever reach a plugin as events
|
|
89
|
+
// (`installation.updated` / `vcs.branch.updated`), which arrive after the
|
|
90
|
+
// resource is built. Deferred attributes bridge that, with a bounded wait
|
|
91
|
+
// so a host that never emits them cannot stall the first export.
|
|
92
|
+
const version = deferredAttribute(factoryOptions.deferredTimeoutMs);
|
|
93
|
+
const branch = deferredAttribute(factoryOptions.deferredTimeoutMs);
|
|
94
|
+
if (factoryOptions.hostInfo?.version) {
|
|
95
|
+
version.settle(factoryOptions.hostInfo.version);
|
|
96
|
+
}
|
|
97
|
+
// Read the checkout straight off disk. This is both richer and more
|
|
98
|
+
// reliable than waiting for `vcs.branch.updated`: it arrives before the
|
|
99
|
+
// first export instead of racing the deferral window, and it carries the
|
|
100
|
+
// remote and revision, which no event reports at all. The event stays as a
|
|
101
|
+
// fallback — `settle` keeps the first value, so whichever lands first wins.
|
|
102
|
+
const vcs = config.collectVcs ? await readVcsInfo(input.worktree ?? input.directory).catch(() => ({})) : {};
|
|
103
|
+
if (vcs.ref) {
|
|
104
|
+
branch.settle(vcs.ref);
|
|
105
|
+
}
|
|
106
|
+
const resource = buildResource(config, {
|
|
107
|
+
version: version.value,
|
|
108
|
+
hostname: factoryOptions.hostInfo?.hostname ?? safeHostname(),
|
|
109
|
+
projectName: input.project?.id,
|
|
110
|
+
directory: input.directory,
|
|
111
|
+
worktree: input.worktree,
|
|
112
|
+
branch: branch.value,
|
|
113
|
+
vcs
|
|
114
|
+
});
|
|
115
|
+
const providers = createProviders(config, resource, logger, factoryOptions.exporters);
|
|
116
|
+
const recorder = new TelemetryRecorder({
|
|
117
|
+
providers,
|
|
118
|
+
config,
|
|
119
|
+
logger,
|
|
120
|
+
now: factoryOptions.now,
|
|
121
|
+
resourceSinks: {
|
|
122
|
+
version: (value) => version.settle(value),
|
|
123
|
+
branch: (value) => branch.settle(value)
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
if (factoryOptions.registerProcessHandlers !== false) {
|
|
127
|
+
registerExitHandlers(providers, logger, [version, branch]);
|
|
128
|
+
}
|
|
129
|
+
logger.info("otel_plugin_enabled", {
|
|
130
|
+
serviceName: config.serviceName,
|
|
131
|
+
exporters: config.exporters,
|
|
132
|
+
endpoint: config.endpoint,
|
|
133
|
+
includeSessionId: config.includeSessionId,
|
|
134
|
+
filteredTools: [...config.filteredTools]
|
|
135
|
+
});
|
|
136
|
+
return {
|
|
137
|
+
config: async (hostConfig) => {
|
|
138
|
+
currentLogLevel = fromOpenCodeLogLevel(hostConfig.logLevel) ?? DEFAULT_LOG_LEVEL;
|
|
139
|
+
if (config.propagateTraceContext) {
|
|
140
|
+
const wrapped = installTracePropagation(hostConfig, {
|
|
141
|
+
getContext: () => recorder.currentChatContext(),
|
|
142
|
+
logger
|
|
143
|
+
});
|
|
144
|
+
logger.debug("otel_trace_propagation_ready", { providerCount: wrapped });
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
event: async ({ event }) => {
|
|
148
|
+
recorder.onEvent(event);
|
|
149
|
+
},
|
|
150
|
+
"chat.message": async (chatInput, chatOutput) => {
|
|
151
|
+
recorder.onChatMessage(chatInput, chatOutput);
|
|
152
|
+
},
|
|
153
|
+
"tool.execute.before": async (toolInput) => {
|
|
154
|
+
recorder.onToolBefore(toolInput);
|
|
155
|
+
},
|
|
156
|
+
"tool.execute.after": async (toolInput, toolOutput) => {
|
|
157
|
+
recorder.onToolAfter(toolInput, toolOutput);
|
|
158
|
+
},
|
|
159
|
+
"chat.params": async (paramsInput, paramsOutput) => {
|
|
160
|
+
recorder.onChatParams(paramsInput, paramsOutput);
|
|
161
|
+
},
|
|
162
|
+
"permission.ask": async (permissionInput, permissionOutput) => {
|
|
163
|
+
recorder.onPermissionAsk(permissionInput, permissionOutput);
|
|
164
|
+
},
|
|
165
|
+
"experimental.text.complete": async (textInput, textOutput) => {
|
|
166
|
+
recorder.onTextComplete(textInput, textOutput);
|
|
167
|
+
},
|
|
168
|
+
"experimental.compaction.autocontinue": async (compactionInput, compactionOutput) => {
|
|
169
|
+
recorder.onCompactionAutocontinue(compactionInput, compactionOutput);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
};
|
|
130
173
|
}
|
|
131
174
|
function safeHostname() {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
175
|
+
try {
|
|
176
|
+
return hostname() || undefined;
|
|
177
|
+
} catch {
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
138
180
|
}
|
|
139
181
|
export const OpencodeOtelPlugin = createOtelPlugin();
|
|
140
182
|
export default OpencodeOtelPlugin;
|
|
183
|
+
|
|
141
184
|
//# sourceMappingURL=opencode.js.map
|
package/dist/opencode.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"
|
|
1
|
+
{"mappings":"AAAA,SAAS,gBAAgB;AAIzB,SAAyB,yBAAyB;AAClD,SAAiC,yBAAyB;AAC1D,SACE,yBACA,mBACA,sBAEA,0BAGK;AACP,SAAS,+BAA4D;AACrE,SACE,eACA,iBACA,qBAGK;AACP,SAAS,yBAAyB;AAClC,SAAS,mBAAiC;AAE1C,MAAM,sBAAsB;;;;;;;;;AA4B5B,SAAS,qBAAqB,QAA+B,aAAqC;CAChG,MAAM,WAAW,wBAAwB,OAAO;CAChD,MAAM,aAAa,qBAAqB,KAAK,QAAQ,IAAI,6BAA6B,EAAE;CAExF,MAAM,SAAS,OAAiB,OAAe,WAAuB;EACpE,IAAI,mBAAmB,SAAS,mBAAmB,YAAY,IAAI;GACjE;EACF;EACA,IAAI,cAAc,UAAU,UAAU,UAAU,SAAS;GACvD,SAAS,MAAM,CAAC,OAAO,MAAM;EAC/B;EACA,MAAM,YAAY,UAAU,UAAU,UAAU;EAChD,KAAK,OAAO,IACT,IAAI,EACH,MAAM;GAAE,SAAS;GAAqB,OAAO;GAAW,SAAS;GAAO,OAAO;EAAO,EACxF,CAAC,CAAC,CACD,YAAY;;EAEb,CAAC;CACL;CAEA,OAAO;EACL,QAAQ,OAAO,WAAW,MAAM,SAAS,OAAO,MAAM;EACtD,QAAQ,OAAO,WAAW,MAAM,SAAS,OAAO,MAAM;EACtD,OAAO,OAAO,WAAW,MAAM,QAAQ,OAAO,MAAM;EACpD,OAAO,OAAO,WAAW,MAAM,QAAQ,OAAO,MAAM;EACpD,QAAQ,OAAO,WAAW,MAAM,SAAS,OAAO,MAAM;CACxD;AACF;;;;;;AAOA,SAAS,qBACP,WACA,QACA,UACM;CACN,IAAI,OAAO;CACX,MAAM,cAAc;EAClB,IAAI,MAAM;GACR;EACF;EACA,OAAO;;;;EAIP,KAAK,MAAM,aAAa,UAAU;GAChC,UAAU,QAAQ;EACpB;EACA,KAAK,UAAU,SAAS,CAAC,CAAC,OAAO,UAAU;GACzC,OAAO,KAAK,wBAAwB,EAAE,OAAO,cAAc,KAAK,EAAE,CAAC;EACrE,CAAC;CACH;CACA,QAAQ,KAAK,cAAc,KAAK;CAChC,QAAQ,KAAK,UAAU,KAAK;CAC5B,QAAQ,KAAK,WAAW,KAAK;AAC/B;AAEA,OAAO,SAAS,iBAAiB,iBAA2C,CAAC,GAAW;CACtF,OAAO,OAAO,OAAoB,kBAAkC;EAClE,IAAI,kBAA4B;EAChC,MAAM,SACJ,eAAe,UAAU,qBAAqB,MAAM,cAAc,eAAe;EAEnF,MAAM,SAAS,kBAAkB,eAAe,eAAe,OAAO,QAAQ,GAAG;EAEjF,IAAI,CAAC,OAAO,QAAQ;;;GAGlB,OAAO,KAAK,wBAAwB;IAClC,SAAS,OAAO;IAChB,QAAQ,OAAO,UAAU,2BAA2B;GACtD,CAAC;GACD,OAAO,EACL,QAAQ,OAAO,eAA+B;IAC5C,kBAAkB,qBAAqB,WAAW,QAAQ,KAAK;GACjE,EACF;EACF;;;;;EAMA,MAAM,UAAU,kBAAkB,eAAe,iBAAiB;EAClE,MAAM,SAAS,kBAAkB,eAAe,iBAAiB;EACjE,IAAI,eAAe,UAAU,SAAS;GACpC,QAAQ,OAAO,eAAe,SAAS,OAAO;EAChD;;;;;;EAOA,MAAM,MAAe,OAAO,aACxB,MAAM,YAAY,MAAM,YAAY,MAAM,SAAS,CAAC,CAAC,aAAa,CAAC,EAAE,IACrE,CAAC;EACL,IAAI,IAAI,KAAK;GACX,OAAO,OAAO,IAAI,GAAG;EACvB;EAEA,MAAM,WAAW,cAAc,QAAQ;GACrC,SAAS,QAAQ;GACjB,UAAU,eAAe,UAAU,YAAY,aAAa;GAC5D,aAAa,MAAM,SAAS;GAC5B,WAAW,MAAM;GACjB,UAAU,MAAM;GAChB,QAAQ,OAAO;GACf;EACF,CAAC;EAED,MAAM,YAAY,gBAAgB,QAAQ,UAAU,QAAQ,eAAe,SAAS;EACpF,MAAM,WAAW,IAAI,kBAAkB;GACrC;GACA;GACA;GACA,KAAK,eAAe;GACpB,eAAe;IACb,UAAU,UAAU,QAAQ,OAAO,KAAK;IACxC,SAAS,UAAU,OAAO,OAAO,KAAK;GACxC;EACF,CAAC;EAED,IAAI,eAAe,4BAA4B,OAAO;GACpD,qBAAqB,WAAW,QAAQ,CAAC,SAAS,MAAM,CAAC;EAC3D;EAEA,OAAO,KAAK,uBAAuB;GACjC,aAAa,OAAO;GACpB,WAAW,OAAO;GAClB,UAAU,OAAO;GACjB,kBAAkB,OAAO;GACzB,eAAe,CAAC,GAAG,OAAO,aAAa;EACzC,CAAC;EAED,OAAO;GACL,QAAQ,OAAO,eAA+B;IAC5C,kBAAkB,qBAAqB,WAAW,QAAQ,KAAK;IAC/D,IAAI,OAAO,uBAAuB;KAChC,MAAM,UAAU,wBAAwB,YAAsC;MAC5E,kBAAkB,SAAS,mBAAmB;MAC9C;KACF,CAAC;KACD,OAAO,MAAM,gCAAgC,EAAE,eAAe,QAAQ,CAAC;IACzE;GACF;GACA,OAAO,OAAO,EAAE,YAAY;IAC1B,SAAS,QAAQ,KAAK;GACxB;GACA,gBAAgB,OAAO,WAAW,eAAe;IAC/C,SAAS,cAAc,WAAW,UAAU;GAC9C;GACA,uBAAuB,OAAO,cAAc;IAC1C,SAAS,aAAa,SAAS;GACjC;GACA,sBAAsB,OAAO,WAAW,eAAe;IACrD,SAAS,YAAY,WAAW,UAAU;GAC5C;GACA,eAAe,OAAO,aAAa,iBAAiB;IAClD,SAAS,aAAa,aAAa,YAAY;GACjD;GACA,kBAAkB,OAAO,iBAAiB,qBAAqB;IAC7D,SAAS,gBAAgB,iBAAiB,gBAAgB;GAC5D;GACA,8BAA8B,OAAO,WAAW,eAAe;IAC7D,SAAS,eAAe,WAAW,UAAU;GAC/C;GACA,wCAAwC,OAAO,iBAAiB,qBAAqB;IACnF,SAAS,yBAAyB,iBAAiB,gBAAgB;GACrE;EACF;CACF;AACF;AAEA,SAAS,eAAmC;CAC1C,IAAI;EACF,OAAO,SAAS,KAAK;CACvB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,OAAO,MAAM,qBAA6B,iBAAiB;AAE3D,eAAe","names":[],"sources":["../src/opencode.ts"],"version":3,"file":"opencode.js","sourceRoot":""}
|
package/dist/propagation.d.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import type { Context } from "@opentelemetry/api";
|
|
2
2
|
import type { Logger } from "./logging.js";
|
|
3
3
|
export interface ProviderConfigLike {
|
|
4
|
-
|
|
4
|
+
options?: Record<string, unknown>;
|
|
5
5
|
}
|
|
6
6
|
export interface PropagationConfigInput {
|
|
7
|
-
|
|
7
|
+
provider?: Record<string, ProviderConfigLike | undefined>;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
* Wrap every provider's `options.fetch` so outgoing model requests carry W3C
|
|
11
|
+
* trace context, joining an OpenCode session and its gateway-side spans into
|
|
12
|
+
* one trace.
|
|
13
|
+
*
|
|
14
|
+
* This is the same interception seam `@vymalo/opencode-ratelimit` uses — and it
|
|
15
|
+
* composes the same way: the existing fetch is captured at install time and
|
|
16
|
+
* delegated to, so stacking the two plugins in either order works.
|
|
17
|
+
*/
|
|
18
18
|
export declare function installTracePropagation(input: PropagationConfigInput, deps: {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
getContext: () => Context | undefined;
|
|
20
|
+
logger: Logger;
|
|
21
|
+
fetchImpl?: typeof fetch;
|
|
22
22
|
}): number;
|
package/dist/propagation.js
CHANGED
|
@@ -2,54 +2,56 @@ import { defaultTextMapSetter } from "@opentelemetry/api";
|
|
|
2
2
|
import { W3CTraceContextPropagator } from "@opentelemetry/core";
|
|
3
3
|
const propagator = new W3CTraceContextPropagator();
|
|
4
4
|
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
* Wrap every provider's `options.fetch` so outgoing model requests carry W3C
|
|
6
|
+
* trace context, joining an OpenCode session and its gateway-side spans into
|
|
7
|
+
* one trace.
|
|
8
|
+
*
|
|
9
|
+
* This is the same interception seam `@vymalo/opencode-ratelimit` uses — and it
|
|
10
|
+
* composes the same way: the existing fetch is captured at install time and
|
|
11
|
+
* delegated to, so stacking the two plugins in either order works.
|
|
12
|
+
*/
|
|
13
13
|
export function installTracePropagation(input, deps) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
14
|
+
const providers = input.provider;
|
|
15
|
+
if (!providers) {
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
let wrapped = 0;
|
|
19
|
+
for (const [providerId, providerConfig] of Object.entries(providers)) {
|
|
20
|
+
if (!providerConfig) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const options = providerConfig.options ??= {};
|
|
24
|
+
const delegate = typeof options.fetch === "function" ? options.fetch : deps.fetchImpl ?? globalThis.fetch;
|
|
25
|
+
if (typeof delegate !== "function") {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
options.fetch = async (input_, init) => {
|
|
29
|
+
const context = deps.getContext();
|
|
30
|
+
if (!context) {
|
|
31
|
+
return delegate(input_, init);
|
|
32
|
+
}
|
|
33
|
+
const carrier = {};
|
|
34
|
+
propagator.inject(context, carrier, defaultTextMapSetter);
|
|
35
|
+
if (Object.keys(carrier).length === 0) {
|
|
36
|
+
return delegate(input_, init);
|
|
37
|
+
}
|
|
38
|
+
const headers = new Headers(init?.headers ?? {});
|
|
39
|
+
// Never clobber an upstream traceparent — if something already set one,
|
|
40
|
+
// it knows more about the request than we do.
|
|
41
|
+
for (const [key, value] of Object.entries(carrier)) {
|
|
42
|
+
if (!headers.has(key)) {
|
|
43
|
+
headers.set(key, value);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return delegate(input_, {
|
|
47
|
+
...init,
|
|
48
|
+
headers
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
wrapped += 1;
|
|
52
|
+
deps.logger.trace("otel_trace_propagation_installed", { providerId });
|
|
53
|
+
}
|
|
54
|
+
return wrapped;
|
|
54
55
|
}
|
|
56
|
+
|
|
55
57
|
//# sourceMappingURL=propagation.js.map
|
package/dist/propagation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"
|
|
1
|
+
{"mappings":"AACA,SAAS,4BAA4B;AACrC,SAAS,iCAAiC;AAY1C,MAAM,aAAa,IAAI,0BAA0B;;;;;;;;;;AAWjD,OAAO,SAAS,wBACd,OACA,MACQ;CACR,MAAM,YAAY,MAAM;CACxB,IAAI,CAAC,WAAW;EACd,OAAO;CACT;CAEA,IAAI,UAAU;CACd,KAAK,MAAM,CAAC,YAAY,mBAAmB,OAAO,QAAQ,SAAS,GAAG;EACpE,IAAI,CAAC,gBAAgB;GACnB;EACF;EACA,MAAM,UAAW,eAAe,YAAY,CAAC;EAC7C,MAAM,WACJ,OAAO,QAAQ,UAAU,aACpB,QAAQ,QACR,KAAK,aAAa,WAAW;EACpC,IAAI,OAAO,aAAa,YAAY;GAClC;EACF;EAEA,QAAQ,QAAQ,OACd,QACA,SACsB;GACtB,MAAM,UAAU,KAAK,WAAW;GAChC,IAAI,CAAC,SAAS;IACZ,OAAO,SAAS,QAAQ,IAAI;GAC9B;GACA,MAAM,UAAkC,CAAC;GACzC,WAAW,OAAO,SAAS,SAAS,oBAAoB;GACxD,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,WAAW,GAAG;IACrC,OAAO,SAAS,QAAQ,IAAI;GAC9B;GACA,MAAM,UAAU,IAAI,QAAQ,MAAM,WAAW,CAAC,CAAC;;;GAG/C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,GAAG;IAClD,IAAI,CAAC,QAAQ,IAAI,GAAG,GAAG;KACrB,QAAQ,IAAI,KAAK,KAAK;IACxB;GACF;GACA,OAAO,SAAS,QAAQ;IAAE,GAAG;IAAM;GAAQ,CAAC;EAC9C;EACA,WAAW;EACX,KAAK,OAAO,MAAM,oCAAoC,EAAE,WAAW,CAAC;CACtE;CAEA,OAAO;AACT","names":[],"sources":["../src/propagation.ts"],"version":3,"file":"propagation.js","sourceRoot":""}
|