






















@@ -4,7 +4,6 @@ import { spawn, type ChildProcess } from "node:child_process";
44import { existsSync } from "node:fs";
55import { mkdir, writeFile } from "node:fs/promises";
66import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
7-import { createRequire } from "node:module";
87import path from "node:path";
98109type OtlpAnyValue = {
@@ -36,26 +35,6 @@ type OtlpResourceSpans = {
3635scopeSpans?: OtlpScopeSpans[];
3736};
383739-type OtlpTraceRequest = {
40-resourceSpans?: OtlpResourceSpans[];
41-};
42-43-type OtlpRoot = {
44-opentelemetry: {
45-proto: {
46-collector: {
47-trace: {
48-v1: {
49-ExportTraceServiceRequest: {
50-decode(input: Uint8Array): OtlpTraceRequest;
51-};
52-};
53-};
54-};
55-};
56-};
57-};
58-5938type CliOptions = {
6039outputDir: string;
6140providerMode: string;
@@ -96,35 +75,6 @@ const DISALLOWED_ATTRIBUTE_KEYS = new Set([
9675"openclaw.toolCallId",
9776]);
987799-let traceRequestDecoder:
100-| OtlpRoot["opentelemetry"]["proto"]["collector"]["trace"]["v1"]["ExportTraceServiceRequest"]
101-| undefined;
102-103-function requireOtlpRoot(): OtlpRoot {
104-const candidates = [
105-path.join(process.cwd(), "dist", "extensions", "diagnostics-otel", "package.json"),
106-path.join(process.cwd(), "extensions", "diagnostics-otel", "package.json"),
107-import.meta.url,
108-];
109-const failures: string[] = [];
110-for (const candidate of candidates) {
111-try {
112-return createRequire(candidate)(
113-"@opentelemetry/otlp-transformer/build/src/generated/root.js",
114-) as OtlpRoot;
115-} catch (error) {
116-failures.push(`${candidate}: ${error instanceof Error ? error.message : String(error)}`);
117-}
118-}
119-throw new Error(`failed to load OTLP transformer decoder:\n${failures.join("\n")}`);
120-}
121-122-function getTraceRequestDecoder() {
123-traceRequestDecoder ??=
124-requireOtlpRoot().opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest;
125-return traceRequestDecoder;
126-}
127-12878function usage(): string {
12979return `Usage: pnpm qa:otel:smoke [--output-dir <path>] [--provider-mode <mode>] [--scenario <id>] [--model <ref>] [--alt-model <ref>]
13080@@ -223,11 +173,205 @@ function spanAttributes(span: OtlpSpan): Record<string, string | number | boolea
223173return attributes;
224174}
225175176+class ProtoReader {
177+private offset = 0;
178+179+constructor(private readonly buffer: Uint8Array) {}
180+181+done(): boolean {
182+return this.offset >= this.buffer.length;
183+}
184+185+tag() {
186+const raw = this.varint();
187+return { field: raw >>> 3, wire: raw & 0x7 };
188+}
189+190+varint(): number {
191+let result = 0;
192+let shift = 0;
193+while (this.offset < this.buffer.length) {
194+const byte = this.buffer[this.offset++];
195+result += (byte & 0x7f) * 2 ** shift;
196+if ((byte & 0x80) === 0) {
197+return result;
198+}
199+shift += 7;
200+}
201+throw new Error("truncated protobuf varint");
202+}
203+204+bytes(): Uint8Array {
205+const length = this.varint();
206+const end = this.offset + length;
207+if (end > this.buffer.length) {
208+throw new Error("truncated protobuf bytes");
209+}
210+const value = this.buffer.subarray(this.offset, end);
211+this.offset = end;
212+return value;
213+}
214+215+string(): string {
216+return new TextDecoder().decode(this.bytes());
217+}
218+219+fixed64(): number {
220+const end = this.offset + 8;
221+if (end > this.buffer.length) {
222+throw new Error("truncated protobuf fixed64");
223+}
224+const view = new DataView(this.buffer.buffer, this.buffer.byteOffset + this.offset, 8);
225+this.offset = end;
226+return view.getFloat64(0, true);
227+}
228+229+skip(wire: number) {
230+if (wire === 0) {
231+this.varint();
232+} else if (wire === 1) {
233+this.offset += 8;
234+} else if (wire === 2) {
235+this.bytes();
236+} else if (wire === 5) {
237+this.offset += 4;
238+} else {
239+throw new Error(`unsupported protobuf wire type ${wire}`);
240+}
241+}
242+}
243+244+function decodeAnyValue(message: Uint8Array): OtlpAnyValue {
245+const reader = new ProtoReader(message);
246+const value: OtlpAnyValue = {};
247+while (!reader.done()) {
248+const { field, wire } = reader.tag();
249+if (field === 1 && wire === 2) {
250+value.stringValue = reader.string();
251+} else if (field === 2 && wire === 0) {
252+value.boolValue = reader.varint() !== 0;
253+} else if (field === 3 && wire === 0) {
254+value.intValue = reader.varint();
255+} else if (field === 4 && wire === 1) {
256+value.doubleValue = reader.fixed64();
257+} else if (field === 5 && wire === 2) {
258+value.arrayValue = decodeArrayValue(reader.bytes());
259+} else if (field === 6 && wire === 2) {
260+value.kvlistValue = decodeKeyValueList(reader.bytes());
261+} else if (field === 7 && wire === 2) {
262+value.bytesValue = reader.bytes();
263+} else {
264+reader.skip(wire);
265+}
266+}
267+return value;
268+}
269+270+function decodeArrayValue(message: Uint8Array): { values?: OtlpAnyValue[] } {
271+const reader = new ProtoReader(message);
272+const values: OtlpAnyValue[] = [];
273+while (!reader.done()) {
274+const { field, wire } = reader.tag();
275+if (field === 1 && wire === 2) {
276+values.push(decodeAnyValue(reader.bytes()));
277+} else {
278+ reader.skip(wire);
279+}
280+}
281+return { values };
282+}
283+284+function decodeKeyValue(message: Uint8Array): OtlpKeyValue {
285+const reader = new ProtoReader(message);
286+const entry: OtlpKeyValue = {};
287+while (!reader.done()) {
288+const { field, wire } = reader.tag();
289+if (field === 1 && wire === 2) {
290+entry.key = reader.string();
291+} else if (field === 2 && wire === 2) {
292+entry.value = decodeAnyValue(reader.bytes());
293+} else {
294+reader.skip(wire);
295+}
296+}
297+return entry;
298+}
299+300+function decodeKeyValueList(message: Uint8Array): { values?: OtlpKeyValue[] } {
301+const reader = new ProtoReader(message);
302+const values: OtlpKeyValue[] = [];
303+while (!reader.done()) {
304+const { field, wire } = reader.tag();
305+if (field === 1 && wire === 2) {
306+values.push(decodeKeyValue(reader.bytes()));
307+} else {
308+reader.skip(wire);
309+}
310+}
311+return { values };
312+}
313+314+function decodeSpan(message: Uint8Array): OtlpSpan {
315+const reader = new ProtoReader(message);
316+const span: OtlpSpan = {};
317+while (!reader.done()) {
318+const { field, wire } = reader.tag();
319+if (field === 4 && wire === 2) {
320+span.parentSpanId = reader.bytes();
321+} else if (field === 5 && wire === 2) {
322+span.name = reader.string();
323+} else if (field === 9 && wire === 2) {
324+span.attributes ??= [];
325+span.attributes.push(decodeKeyValue(reader.bytes()));
326+} else {
327+reader.skip(wire);
328+}
329+}
330+return span;
331+}
332+333+function decodeScopeSpans(message: Uint8Array): OtlpScopeSpans {
334+const reader = new ProtoReader(message);
335+const spans: OtlpSpan[] = [];
336+while (!reader.done()) {
337+const { field, wire } = reader.tag();
338+if (field === 2 && wire === 2) {
339+spans.push(decodeSpan(reader.bytes()));
340+} else {
341+reader.skip(wire);
342+}
343+}
344+return { spans };
345+}
346+347+function decodeResourceSpans(message: Uint8Array): OtlpResourceSpans {
348+const reader = new ProtoReader(message);
349+const scopeSpans: OtlpScopeSpans[] = [];
350+while (!reader.done()) {
351+const { field, wire } = reader.tag();
352+if (field === 2 && wire === 2) {
353+scopeSpans.push(decodeScopeSpans(reader.bytes()));
354+} else {
355+reader.skip(wire);
356+}
357+}
358+return { scopeSpans };
359+}
360+226361function decodeTraceRequest(body: Buffer): CapturedSpan[] {
227-const decoded = getTraceRequestDecoder().decode(body);
362+const reader = new ProtoReader(body);
363+const resourceSpans: OtlpResourceSpans[] = [];
364+while (!reader.done()) {
365+const { field, wire } = reader.tag();
366+if (field === 1 && wire === 2) {
367+resourceSpans.push(decodeResourceSpans(reader.bytes()));
368+} else {
369+reader.skip(wire);
370+}
371+}
228372const spans: CapturedSpan[] = [];
229-for (const resourceSpans of decoded.resourceSpans ?? []) {
230-for (const scopeSpans of resourceSpans.scopeSpans ?? []) {
373+for (const resource of resourceSpans) {
374+for (const scopeSpans of resource.scopeSpans ?? []) {
231375for (const span of scopeSpans.spans ?? []) {
232376const name = span.name?.trim();
233377if (!name) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。