@ai-setting/roy-agent-core 1.4.11 → 1.4.12
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/index.js +23 -0
- package/dist/env/agent/index.js +3035 -0
- package/dist/env/commands/index.js +1685 -0
- package/dist/env/debug/formatters/index.js +639 -0
- package/dist/env/debug/index.js +2300 -0
- package/dist/env/hook/index.js +273 -0
- package/dist/env/index.js +12591 -0
- package/dist/env/llm/index.js +2736 -0
- package/dist/env/log-trace/index.js +1779 -0
- package/dist/env/mcp/index.js +2173 -0
- package/dist/env/mcp/tool/index.js +1149 -0
- package/dist/env/memory/built-in/index.js +225 -0
- package/dist/env/memory/index.js +2171 -0
- package/dist/env/memory/plugin/index.js +1263 -0
- package/dist/env/prompt/index.js +2107 -0
- package/dist/env/session/index.js +3594 -0
- package/dist/env/session/storage/index.js +2049 -0
- package/dist/env/skill/index.js +1635 -0
- package/dist/env/skill/tool/index.js +114 -0
- package/dist/env/task/delegate/index.js +1844 -0
- package/dist/env/task/hooks/index.js +67 -0
- package/dist/env/task/index.js +3578 -0
- package/dist/env/task/plugins/index.js +1626 -0
- package/dist/env/task/storage/index.js +1464 -0
- package/dist/env/task/tools/index.js +344 -0
- package/dist/env/task/tools/operation/index.js +270 -0
- package/dist/env/tool/built-in/index.js +1151 -0
- package/dist/env/tool/index.js +2284 -0
- package/dist/env/workflow/decorators/index.js +449 -0
- package/dist/env/workflow/engine/index.js +4391 -0
- package/dist/env/workflow/index.js +6214 -0
- package/dist/env/workflow/nodes/index.js +650 -0
- package/dist/env/workflow/service/index.js +262 -0
- package/dist/env/workflow/storage/index.js +1236 -0
- package/dist/env/workflow/tools/index.js +1081 -0
- package/dist/env/workflow/types/index.js +479 -0
- package/dist/env/workflow/utils/index.js +1631 -0
- package/dist/index.js +15006 -14265
- package/package.json +2 -2
|
@@ -0,0 +1,2300 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
function __accessProp(key) {
|
|
7
|
+
return this[key];
|
|
8
|
+
}
|
|
9
|
+
var __toCommonJS = (from) => {
|
|
10
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
11
|
+
if (entry)
|
|
12
|
+
return entry;
|
|
13
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (var key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(entry, key))
|
|
17
|
+
__defProp(entry, key, {
|
|
18
|
+
get: __accessProp.bind(from, key),
|
|
19
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
__moduleCache.set(from, entry);
|
|
23
|
+
return entry;
|
|
24
|
+
};
|
|
25
|
+
var __moduleCache;
|
|
26
|
+
var __returnValue = (v) => v;
|
|
27
|
+
function __exportSetter(name, newValue) {
|
|
28
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
29
|
+
}
|
|
30
|
+
var __export = (target, all) => {
|
|
31
|
+
for (var name in all)
|
|
32
|
+
__defProp(target, name, {
|
|
33
|
+
get: all[name],
|
|
34
|
+
enumerable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
set: __exportSetter.bind(all, name)
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
var __legacyDecorateClassTS = function(decorators, target, key, desc) {
|
|
40
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
41
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
42
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
43
|
+
else
|
|
44
|
+
for (var i = decorators.length - 1;i >= 0; i--)
|
|
45
|
+
if (d = decorators[i])
|
|
46
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
47
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
48
|
+
};
|
|
49
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
50
|
+
var __require = import.meta.require;
|
|
51
|
+
|
|
52
|
+
// packages/core/src/env/log-trace/span-storage.ts
|
|
53
|
+
var exports_span_storage = {};
|
|
54
|
+
__export(exports_span_storage, {
|
|
55
|
+
SQLiteSpanStorage: () => SQLiteSpanStorage
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
class SQLiteSpanStorage {
|
|
59
|
+
db = null;
|
|
60
|
+
dbPath;
|
|
61
|
+
initialized = false;
|
|
62
|
+
constructor(dbPath) {
|
|
63
|
+
if (!dbPath) {
|
|
64
|
+
throw new Error("SQLiteSpanStorage requires a valid dbPath parameter");
|
|
65
|
+
}
|
|
66
|
+
if (dbPath === ":memory:") {
|
|
67
|
+
throw new Error("SQLiteSpanStorage does not support :memory: mode. Use a file path instead.");
|
|
68
|
+
}
|
|
69
|
+
this.dbPath = dbPath;
|
|
70
|
+
}
|
|
71
|
+
async initialize() {
|
|
72
|
+
if (this.initialized)
|
|
73
|
+
return;
|
|
74
|
+
const { Database } = __require("bun:sqlite");
|
|
75
|
+
const fs2 = __require("fs");
|
|
76
|
+
const path2 = __require("path");
|
|
77
|
+
const dir = path2.dirname(this.dbPath);
|
|
78
|
+
if (!fs2.existsSync(dir)) {
|
|
79
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
80
|
+
}
|
|
81
|
+
this.db = new Database(this.dbPath);
|
|
82
|
+
this.db.run("PRAGMA journal_mode=WAL");
|
|
83
|
+
this.db.run("PRAGMA busy_timeout=5000");
|
|
84
|
+
this.db.run("PRAGMA synchronous=NORMAL");
|
|
85
|
+
this.db.run(`
|
|
86
|
+
CREATE TABLE IF NOT EXISTS span (
|
|
87
|
+
span_id TEXT PRIMARY KEY,
|
|
88
|
+
trace_id TEXT NOT NULL,
|
|
89
|
+
parent_span_id TEXT,
|
|
90
|
+
name TEXT NOT NULL,
|
|
91
|
+
kind TEXT NOT NULL,
|
|
92
|
+
status TEXT NOT NULL,
|
|
93
|
+
start_time INTEGER NOT NULL,
|
|
94
|
+
end_time INTEGER,
|
|
95
|
+
attributes TEXT,
|
|
96
|
+
result TEXT,
|
|
97
|
+
error TEXT,
|
|
98
|
+
time_created INTEGER NOT NULL
|
|
99
|
+
)
|
|
100
|
+
`);
|
|
101
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_span_trace ON span(trace_id)");
|
|
102
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_span_parent ON span(parent_span_id)");
|
|
103
|
+
this.initialized = true;
|
|
104
|
+
}
|
|
105
|
+
save(span) {
|
|
106
|
+
if (!this.db)
|
|
107
|
+
return;
|
|
108
|
+
const stmt = this.db.prepare(`
|
|
109
|
+
INSERT OR REPLACE INTO span
|
|
110
|
+
(span_id, trace_id, parent_span_id, name, kind, status, start_time, end_time, attributes, result, error, time_created)
|
|
111
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
112
|
+
`);
|
|
113
|
+
const resultValue = span.result !== undefined && span.result !== null ? typeof span.result === "string" ? span.result : JSON.stringify(span.result) : null;
|
|
114
|
+
stmt.run(span.spanId, span.traceId, span.parentSpanId || null, span.name, span.kind, span.status, span.startTime, span.endTime || null, JSON.stringify(span.attributes), resultValue, span.error || null, Date.now());
|
|
115
|
+
}
|
|
116
|
+
saveBatch(spans) {
|
|
117
|
+
for (const span of spans) {
|
|
118
|
+
this.save(span);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
findByTraceId(traceId) {
|
|
122
|
+
if (!this.db)
|
|
123
|
+
return [];
|
|
124
|
+
const rows = this.db.prepare("SELECT * FROM span WHERE trace_id = ?").all(traceId);
|
|
125
|
+
return this.buildTree(rows.map(this.rowToSpan));
|
|
126
|
+
}
|
|
127
|
+
findBySpanId(spanId) {
|
|
128
|
+
if (!this.db)
|
|
129
|
+
return;
|
|
130
|
+
const row = this.db.prepare("SELECT * FROM span WHERE span_id = ?").get(spanId);
|
|
131
|
+
return row ? this.rowToSpan(row) : undefined;
|
|
132
|
+
}
|
|
133
|
+
listTraces(limit = 10) {
|
|
134
|
+
if (!this.db)
|
|
135
|
+
return [];
|
|
136
|
+
const rows = this.db.prepare(`
|
|
137
|
+
SELECT trace_id,
|
|
138
|
+
MIN(start_time) as start_time,
|
|
139
|
+
MAX(end_time) as end_time,
|
|
140
|
+
COUNT(*) as span_count
|
|
141
|
+
FROM span
|
|
142
|
+
GROUP BY trace_id
|
|
143
|
+
ORDER BY start_time DESC
|
|
144
|
+
LIMIT ?
|
|
145
|
+
`).all(limit);
|
|
146
|
+
return rows.map((row) => ({
|
|
147
|
+
traceId: row.trace_id,
|
|
148
|
+
rootSpanName: "unknown",
|
|
149
|
+
startTime: row.start_time,
|
|
150
|
+
endTime: row.end_time,
|
|
151
|
+
duration: row.end_time - row.start_time,
|
|
152
|
+
spanCount: row.span_count,
|
|
153
|
+
status: "ok"
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
deleteByTraceId(traceId) {
|
|
157
|
+
if (this.db) {
|
|
158
|
+
this.db.prepare("DELETE FROM span WHERE trace_id = ?").run(traceId);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
close() {
|
|
162
|
+
if (this.db) {
|
|
163
|
+
this.db.close();
|
|
164
|
+
this.db = null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
rowToSpan(row) {
|
|
168
|
+
return {
|
|
169
|
+
traceId: row.trace_id,
|
|
170
|
+
spanId: row.span_id,
|
|
171
|
+
parentSpanId: row.parent_span_id,
|
|
172
|
+
name: row.name,
|
|
173
|
+
kind: row.kind,
|
|
174
|
+
status: row.status,
|
|
175
|
+
startTime: row.start_time,
|
|
176
|
+
endTime: row.end_time,
|
|
177
|
+
attributes: row.attributes ? JSON.parse(row.attributes) : {},
|
|
178
|
+
result: row.result || undefined,
|
|
179
|
+
error: row.error || undefined
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
buildTree(spans) {
|
|
183
|
+
const spanMap = new Map;
|
|
184
|
+
const roots = [];
|
|
185
|
+
for (const span of spans) {
|
|
186
|
+
spanMap.set(span.spanId, { ...span, children: [] });
|
|
187
|
+
}
|
|
188
|
+
for (const span of spanMap.values()) {
|
|
189
|
+
if (span.parentSpanId) {
|
|
190
|
+
const parent = spanMap.get(span.parentSpanId);
|
|
191
|
+
if (parent) {
|
|
192
|
+
parent.children.push(span);
|
|
193
|
+
} else {
|
|
194
|
+
roots.push(span);
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
roots.push(span);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return roots;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// packages/core/src/env/log-trace/opentelemetry/propagation.ts
|
|
205
|
+
function padLeft(str, length) {
|
|
206
|
+
return str.padStart(length, "0");
|
|
207
|
+
}
|
|
208
|
+
function isValidHex(str) {
|
|
209
|
+
return /^[0-9a-f]+$/i.test(str);
|
|
210
|
+
}
|
|
211
|
+
function serialize(context) {
|
|
212
|
+
const traceId = padLeft(context.traceId.toLowerCase(), TRACE_ID_LENGTH);
|
|
213
|
+
const spanId = padLeft(context.spanId.toLowerCase(), PARENT_SPAN_ID_LENGTH);
|
|
214
|
+
const parts = [
|
|
215
|
+
TRACE_CONTEXT_VERSION,
|
|
216
|
+
traceId,
|
|
217
|
+
spanId,
|
|
218
|
+
TRACE_FLAGS_SAMPLED
|
|
219
|
+
];
|
|
220
|
+
return parts.join("-");
|
|
221
|
+
}
|
|
222
|
+
function parse(traceparent) {
|
|
223
|
+
if (!traceparent || typeof traceparent !== "string") {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const trimmed = traceparent.trim();
|
|
227
|
+
const parts = trimmed.split("-");
|
|
228
|
+
if (parts.length !== 4) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const [version, traceId, senderSpanId, flags] = parts;
|
|
232
|
+
if (version !== TRACE_CONTEXT_VERSION) {
|
|
233
|
+
if (version > TRACE_CONTEXT_VERSION) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (traceId.length !== TRACE_ID_LENGTH || !isValidHex(traceId)) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (senderSpanId.length !== PARENT_SPAN_ID_LENGTH || !isValidHex(senderSpanId)) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (flags.length !== FLAGS_LENGTH || !isValidHex(flags)) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
traceId: traceId.toLowerCase(),
|
|
248
|
+
spanId: senderSpanId.toLowerCase()
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
var TRACE_CONTEXT_VERSION = "00", TRACE_FLAGS_SAMPLED = "01", TRACEPARENT_HEADER = "TRACEPARENT", TRACE_ID_LENGTH = 32, PARENT_SPAN_ID_LENGTH = 16, FLAGS_LENGTH = 2, propagation;
|
|
252
|
+
var init_propagation = __esm(() => {
|
|
253
|
+
propagation = {
|
|
254
|
+
inject(carrier, context) {
|
|
255
|
+
carrier[TRACEPARENT_HEADER] = serialize(context);
|
|
256
|
+
},
|
|
257
|
+
extract(carrier) {
|
|
258
|
+
const traceparent = carrier[TRACEPARENT_HEADER];
|
|
259
|
+
if (!traceparent) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
return parse(traceparent);
|
|
263
|
+
},
|
|
264
|
+
getTraceparentHeader() {
|
|
265
|
+
return TRACEPARENT_HEADER;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// packages/core/src/env/log-trace/types.ts
|
|
271
|
+
var SpanKind, SpanStatus;
|
|
272
|
+
var init_types = __esm(() => {
|
|
273
|
+
((SpanKind2) => {
|
|
274
|
+
SpanKind2["CLIENT"] = "client";
|
|
275
|
+
SpanKind2["SERVER"] = "server";
|
|
276
|
+
SpanKind2["INTERNAL"] = "internal";
|
|
277
|
+
})(SpanKind ||= {});
|
|
278
|
+
((SpanStatus2) => {
|
|
279
|
+
SpanStatus2["OK"] = "ok";
|
|
280
|
+
SpanStatus2["ERROR"] = "error";
|
|
281
|
+
})(SpanStatus ||= {});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// packages/core/src/env/log-trace/opentelemetry/tracer-provider.ts
|
|
285
|
+
var exports_tracer_provider = {};
|
|
286
|
+
__export(exports_tracer_provider, {
|
|
287
|
+
resetTracerProvider: () => resetTracerProvider,
|
|
288
|
+
getTracerProvider: () => getTracerProvider,
|
|
289
|
+
OTelTracerProvider: () => OTelTracerProvider
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
class OTelSpanImpl {
|
|
293
|
+
name;
|
|
294
|
+
kind;
|
|
295
|
+
spanContext;
|
|
296
|
+
attributes = {};
|
|
297
|
+
startTime;
|
|
298
|
+
endTime;
|
|
299
|
+
error;
|
|
300
|
+
storage;
|
|
301
|
+
parentSpanContext;
|
|
302
|
+
onEnd;
|
|
303
|
+
constructor(name, spanContext, storage, parentSpanContext, onEnd) {
|
|
304
|
+
this.name = name;
|
|
305
|
+
this.kind = "internal";
|
|
306
|
+
this.spanContext = spanContext;
|
|
307
|
+
this.parentSpanContext = parentSpanContext;
|
|
308
|
+
this.startTime = Date.now();
|
|
309
|
+
this.storage = storage;
|
|
310
|
+
this.onEnd = onEnd;
|
|
311
|
+
}
|
|
312
|
+
setAttribute(key, value) {
|
|
313
|
+
this.attributes[key] = value;
|
|
314
|
+
}
|
|
315
|
+
addEvent(name, attributes) {
|
|
316
|
+
const events = this.attributes._events || [];
|
|
317
|
+
events.push({ name, attributes });
|
|
318
|
+
this.attributes._events = events;
|
|
319
|
+
}
|
|
320
|
+
end(result, error) {
|
|
321
|
+
this.endTime = Date.now();
|
|
322
|
+
if (error) {
|
|
323
|
+
this.error = error.message;
|
|
324
|
+
}
|
|
325
|
+
this.storage.save({
|
|
326
|
+
traceId: this.spanContext.traceId,
|
|
327
|
+
spanId: this.spanContext.spanId,
|
|
328
|
+
parentSpanId: this.spanContext.parentSpanId,
|
|
329
|
+
name: this.name,
|
|
330
|
+
kind: this.kind,
|
|
331
|
+
status: error ? "error" /* ERROR */ : "ok" /* OK */,
|
|
332
|
+
startTime: this.startTime,
|
|
333
|
+
endTime: this.endTime,
|
|
334
|
+
attributes: this.attributes,
|
|
335
|
+
result,
|
|
336
|
+
error: this.error
|
|
337
|
+
});
|
|
338
|
+
this.onEnd?.();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
class OTelTracerImpl {
|
|
343
|
+
name;
|
|
344
|
+
version;
|
|
345
|
+
storage;
|
|
346
|
+
currentContext;
|
|
347
|
+
activeSpans = new Map;
|
|
348
|
+
onSpanEndCallback;
|
|
349
|
+
provider;
|
|
350
|
+
constructor(name, version, storage, provider) {
|
|
351
|
+
this.name = name;
|
|
352
|
+
this.version = version;
|
|
353
|
+
this.storage = storage;
|
|
354
|
+
this.provider = provider;
|
|
355
|
+
}
|
|
356
|
+
setOnSpanEndCallback(callback) {
|
|
357
|
+
this.onSpanEndCallback = callback;
|
|
358
|
+
}
|
|
359
|
+
getActiveSpanCount() {
|
|
360
|
+
return this.activeSpans.size;
|
|
361
|
+
}
|
|
362
|
+
startSpan(name, options) {
|
|
363
|
+
const parentFromOptions = options?.parent;
|
|
364
|
+
const hasExplicitParent = options && "parent" in options;
|
|
365
|
+
const globalContext = this.provider.getGlobalContext();
|
|
366
|
+
let effectiveParentContext;
|
|
367
|
+
if (hasExplicitParent) {
|
|
368
|
+
effectiveParentContext = parentFromOptions;
|
|
369
|
+
} else if (this.currentContext) {
|
|
370
|
+
effectiveParentContext = this.currentContext;
|
|
371
|
+
} else if (globalContext) {
|
|
372
|
+
effectiveParentContext = globalContext;
|
|
373
|
+
}
|
|
374
|
+
const traceId = effectiveParentContext?.traceId || this.generateTraceId();
|
|
375
|
+
const spanId = this.generateSpanId();
|
|
376
|
+
let parentSpanId;
|
|
377
|
+
if (hasExplicitParent && parentFromOptions) {
|
|
378
|
+
parentSpanId = parentFromOptions.parentSpanId || parentFromOptions.spanId;
|
|
379
|
+
} else if (this.currentContext) {
|
|
380
|
+
parentSpanId = this.currentContext.spanId;
|
|
381
|
+
} else if (globalContext) {
|
|
382
|
+
parentSpanId = globalContext.spanId;
|
|
383
|
+
}
|
|
384
|
+
const spanContext = {
|
|
385
|
+
traceId,
|
|
386
|
+
spanId,
|
|
387
|
+
parentSpanId
|
|
388
|
+
};
|
|
389
|
+
this.currentContext = {
|
|
390
|
+
traceId,
|
|
391
|
+
spanId,
|
|
392
|
+
parentSpanId
|
|
393
|
+
};
|
|
394
|
+
const span = new OTelSpanImpl(name, spanContext, this.storage, effectiveParentContext, () => this.handleSpanEnd(spanContext.spanId, effectiveParentContext?.spanId));
|
|
395
|
+
if (options?.attributes) {
|
|
396
|
+
for (const [key, value] of Object.entries(options.attributes)) {
|
|
397
|
+
span.setAttribute(key, value);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
this.activeSpans.set(spanId, span);
|
|
401
|
+
return span;
|
|
402
|
+
}
|
|
403
|
+
injectToEnv(env) {
|
|
404
|
+
if (this.currentContext) {
|
|
405
|
+
propagation.inject(env, this.currentContext);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
getCurrentContext() {
|
|
409
|
+
return this.currentContext;
|
|
410
|
+
}
|
|
411
|
+
setCurrentContext(context) {
|
|
412
|
+
this.currentContext = context;
|
|
413
|
+
}
|
|
414
|
+
endSpan(span) {
|
|
415
|
+
const spanContext = span.spanContext;
|
|
416
|
+
this.activeSpans.delete(spanContext.spanId);
|
|
417
|
+
const currentTraceId = this.currentContext?.traceId;
|
|
418
|
+
if (spanContext.parentSpanId) {
|
|
419
|
+
const parentSpan = this.activeSpans.get(spanContext.parentSpanId);
|
|
420
|
+
if (parentSpan) {
|
|
421
|
+
const parentContext = {
|
|
422
|
+
traceId: spanContext.traceId,
|
|
423
|
+
spanId: parentSpan.spanContext.spanId,
|
|
424
|
+
parentSpanId: parentSpan.spanContext.parentSpanId
|
|
425
|
+
};
|
|
426
|
+
this.currentContext = parentContext;
|
|
427
|
+
return parentContext;
|
|
428
|
+
}
|
|
429
|
+
if (currentTraceId) {
|
|
430
|
+
const parentContext = {
|
|
431
|
+
traceId: currentTraceId,
|
|
432
|
+
spanId: spanContext.parentSpanId,
|
|
433
|
+
parentSpanId: undefined
|
|
434
|
+
};
|
|
435
|
+
this.currentContext = parentContext;
|
|
436
|
+
return parentContext;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
this.currentContext = undefined;
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
generateTraceId() {
|
|
443
|
+
const timestamp = Date.now().toString(16).padStart(12, "0");
|
|
444
|
+
const random = Array.from({ length: 5 }, () => Math.floor(Math.random() * 4294967295).toString(16).padStart(8, "0")).join("");
|
|
445
|
+
return (timestamp + random).slice(0, 32).padStart(32, "0");
|
|
446
|
+
}
|
|
447
|
+
generateSpanId() {
|
|
448
|
+
return Math.floor(Math.random() * 18446744073709552000).toString(16).padStart(16, "0");
|
|
449
|
+
}
|
|
450
|
+
handleSpanEnd(spanId, parentSpanId) {
|
|
451
|
+
this.activeSpans.delete(spanId);
|
|
452
|
+
const currentTraceId = this.currentContext?.traceId;
|
|
453
|
+
if (parentSpanId) {
|
|
454
|
+
const parentSpan = this.activeSpans.get(parentSpanId);
|
|
455
|
+
if (parentSpan) {
|
|
456
|
+
this.currentContext = {
|
|
457
|
+
traceId: parentSpan.spanContext.traceId,
|
|
458
|
+
spanId: parentSpan.spanContext.spanId,
|
|
459
|
+
parentSpanId: parentSpan.spanContext.parentSpanId
|
|
460
|
+
};
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (currentTraceId) {
|
|
464
|
+
this.currentContext = {
|
|
465
|
+
traceId: currentTraceId,
|
|
466
|
+
spanId: parentSpanId,
|
|
467
|
+
parentSpanId: undefined
|
|
468
|
+
};
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
this.currentContext = undefined;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
class OTelTracerProvider {
|
|
477
|
+
tracers = new Map;
|
|
478
|
+
storage;
|
|
479
|
+
initialized = false;
|
|
480
|
+
globalContext;
|
|
481
|
+
constructor(storage, dbPath) {
|
|
482
|
+
this.storage = storage || new SQLiteSpanStorage(dbPath || getDefaultDbPath());
|
|
483
|
+
}
|
|
484
|
+
async initialize() {
|
|
485
|
+
if (this.initialized)
|
|
486
|
+
return;
|
|
487
|
+
await this.storage.initialize();
|
|
488
|
+
this.initialized = true;
|
|
489
|
+
}
|
|
490
|
+
isInitialized() {
|
|
491
|
+
return this.initialized;
|
|
492
|
+
}
|
|
493
|
+
getGlobalContext() {
|
|
494
|
+
return this.globalContext;
|
|
495
|
+
}
|
|
496
|
+
setGlobalContext(context) {
|
|
497
|
+
this.globalContext = context;
|
|
498
|
+
}
|
|
499
|
+
getTracer(name, version) {
|
|
500
|
+
const key = `${name}@${version || "0.0.0"}`;
|
|
501
|
+
let tracer = this.tracers.get(key);
|
|
502
|
+
if (!tracer) {
|
|
503
|
+
tracer = new OTelTracerImpl(name, version, this.storage, this);
|
|
504
|
+
this.tracers.set(key, tracer);
|
|
505
|
+
if (this.initialized) {
|
|
506
|
+
this.restoreFromEnv(tracer);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return tracer;
|
|
510
|
+
}
|
|
511
|
+
restoreFromEnv(tracer) {
|
|
512
|
+
const extracted = propagation.extract(process.env);
|
|
513
|
+
if (extracted) {
|
|
514
|
+
tracer.setCurrentContext({
|
|
515
|
+
traceId: extracted.traceId,
|
|
516
|
+
spanId: extracted.spanId,
|
|
517
|
+
parentSpanId: undefined
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
shutdown() {
|
|
522
|
+
for (const tracer of this.tracers.values()) {}
|
|
523
|
+
this.tracers.clear();
|
|
524
|
+
this.storage.close();
|
|
525
|
+
this.initialized = false;
|
|
526
|
+
this.globalContext = undefined;
|
|
527
|
+
delete process.env["TRACEPARENT"];
|
|
528
|
+
delete process.env["TRACE_ID"];
|
|
529
|
+
delete process.env["LOG_TRACE_REQUEST_ID"];
|
|
530
|
+
}
|
|
531
|
+
getStorage() {
|
|
532
|
+
return this.storage;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
function getTracerProvider() {
|
|
536
|
+
if (!providerInstance) {
|
|
537
|
+
providerInstance = new OTelTracerProvider;
|
|
538
|
+
}
|
|
539
|
+
return providerInstance;
|
|
540
|
+
}
|
|
541
|
+
function resetTracerProvider() {
|
|
542
|
+
if (providerInstance) {
|
|
543
|
+
providerInstance.shutdown();
|
|
544
|
+
providerInstance = null;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function getDefaultDbPath() {
|
|
548
|
+
const os = __require("os");
|
|
549
|
+
const path2 = __require("path");
|
|
550
|
+
const home = os.homedir();
|
|
551
|
+
const dataHome = process.env.XDG_DATA_HOME || path2.join(home, ".local", "share");
|
|
552
|
+
return path2.join(dataHome, "roy-agent", "traces.db");
|
|
553
|
+
}
|
|
554
|
+
var providerInstance = null;
|
|
555
|
+
var init_tracer_provider = __esm(() => {
|
|
556
|
+
init_propagation();
|
|
557
|
+
init_types();
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
// packages/core/src/env/hook/types.ts
|
|
561
|
+
function createHook(meta, fn) {
|
|
562
|
+
return {
|
|
563
|
+
...meta,
|
|
564
|
+
execute: fn
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
function createPriorityHook(name, fn, priority) {
|
|
568
|
+
return createHook({ name, priority }, fn);
|
|
569
|
+
}
|
|
570
|
+
// packages/core/src/env/hook/hook-manager.ts
|
|
571
|
+
class HookManager {
|
|
572
|
+
_hooks = new Map;
|
|
573
|
+
componentName;
|
|
574
|
+
componentVersion;
|
|
575
|
+
defaultPriority;
|
|
576
|
+
constructor(options = {}) {
|
|
577
|
+
this.componentName = options.componentName ?? "unknown";
|
|
578
|
+
this.componentVersion = options.componentVersion ?? "0.0.0";
|
|
579
|
+
this.defaultPriority = options.defaultPriority ?? 0;
|
|
580
|
+
}
|
|
581
|
+
register(hookPoint, hook) {
|
|
582
|
+
const hooks = this.getOrCreateHooks(hookPoint);
|
|
583
|
+
hooks.push(hook);
|
|
584
|
+
}
|
|
585
|
+
registerMany(hookPoint, hooks) {
|
|
586
|
+
const hookList = this.getOrCreateHooks(hookPoint);
|
|
587
|
+
hookList.push(...hooks);
|
|
588
|
+
}
|
|
589
|
+
unregister(hookPoint, name) {
|
|
590
|
+
const hooks = this._hooks.get(hookPoint);
|
|
591
|
+
if (!hooks)
|
|
592
|
+
return false;
|
|
593
|
+
const index = hooks.findIndex((h) => h.name === name);
|
|
594
|
+
if (index === -1)
|
|
595
|
+
return false;
|
|
596
|
+
hooks.splice(index, 1);
|
|
597
|
+
return true;
|
|
598
|
+
}
|
|
599
|
+
unregisterAll(hookPoint) {
|
|
600
|
+
this._hooks.delete(hookPoint);
|
|
601
|
+
}
|
|
602
|
+
async execute(hookPoint, data, metadata = {}) {
|
|
603
|
+
const hooks = this._hooks.get(hookPoint);
|
|
604
|
+
if (!hooks || hooks.length === 0)
|
|
605
|
+
return;
|
|
606
|
+
const sortedHooks = this.sortHooks(hooks);
|
|
607
|
+
const ctx = this.createContext(hookPoint, data, metadata, "before");
|
|
608
|
+
for (const hook of sortedHooks) {
|
|
609
|
+
await this.safeExecute(hook, ctx);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
async executeAndCollect(hookPoint, data, metadata = {}) {
|
|
613
|
+
const hooks = this._hooks.get(hookPoint);
|
|
614
|
+
if (!hooks || hooks.length === 0)
|
|
615
|
+
return [];
|
|
616
|
+
const sortedHooks = this.sortHooks(hooks);
|
|
617
|
+
const ctx = this.createContext(hookPoint, data, metadata, "before");
|
|
618
|
+
const results = [];
|
|
619
|
+
for (const hook of sortedHooks) {
|
|
620
|
+
const result = await this.safeExecuteAndReturn(hook, ctx);
|
|
621
|
+
if (result !== undefined) {
|
|
622
|
+
results.push(result);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return results;
|
|
626
|
+
}
|
|
627
|
+
count(hookPoint) {
|
|
628
|
+
return this._hooks.get(hookPoint)?.length ?? 0;
|
|
629
|
+
}
|
|
630
|
+
clear() {
|
|
631
|
+
this._hooks.clear();
|
|
632
|
+
}
|
|
633
|
+
getHookPoints() {
|
|
634
|
+
return Array.from(this._hooks.keys());
|
|
635
|
+
}
|
|
636
|
+
hasHooks(hookPoint) {
|
|
637
|
+
return this.count(hookPoint) > 0;
|
|
638
|
+
}
|
|
639
|
+
setComponentInfo(name, version) {
|
|
640
|
+
this.componentName = name;
|
|
641
|
+
this.componentVersion = version;
|
|
642
|
+
}
|
|
643
|
+
get hooks() {
|
|
644
|
+
return this._hooks;
|
|
645
|
+
}
|
|
646
|
+
async executeWithIntervention(hookPoint, data, metadata = {}) {
|
|
647
|
+
const hooks = this._hooks.get(hookPoint);
|
|
648
|
+
if (!hooks || hooks.length === 0) {
|
|
649
|
+
return { stopped: false, results: [] };
|
|
650
|
+
}
|
|
651
|
+
const sortedHooks = this.sortHooks(hooks);
|
|
652
|
+
const ctx = this.createContext(hookPoint, data, metadata, "before");
|
|
653
|
+
const results = [];
|
|
654
|
+
let stopped = false;
|
|
655
|
+
let action;
|
|
656
|
+
for (const hook of sortedHooks) {
|
|
657
|
+
if (stopped)
|
|
658
|
+
break;
|
|
659
|
+
const result = await this.safeExecuteAndReturn(hook, ctx);
|
|
660
|
+
results.push(result);
|
|
661
|
+
if (result && typeof result === "object" && "stopped" in result) {
|
|
662
|
+
const hookResult = result;
|
|
663
|
+
if (hookResult.stopped) {
|
|
664
|
+
stopped = true;
|
|
665
|
+
action = hookResult.action;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return { stopped, action, results };
|
|
670
|
+
}
|
|
671
|
+
getOrCreateHooks(hookPoint) {
|
|
672
|
+
let hooks = this._hooks.get(hookPoint);
|
|
673
|
+
if (!hooks) {
|
|
674
|
+
hooks = [];
|
|
675
|
+
this._hooks.set(hookPoint, hooks);
|
|
676
|
+
}
|
|
677
|
+
return hooks;
|
|
678
|
+
}
|
|
679
|
+
sortHooks(hooks) {
|
|
680
|
+
return [...hooks].sort((a, b) => {
|
|
681
|
+
const priorityA = a.priority ?? this.defaultPriority;
|
|
682
|
+
const priorityB = b.priority ?? this.defaultPriority;
|
|
683
|
+
return priorityA - priorityB;
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
createContext(hookPoint, data, metadata, phase) {
|
|
687
|
+
return {
|
|
688
|
+
component: {
|
|
689
|
+
name: this.componentName,
|
|
690
|
+
version: this.componentVersion
|
|
691
|
+
},
|
|
692
|
+
data,
|
|
693
|
+
metadata,
|
|
694
|
+
phase,
|
|
695
|
+
hookPoint
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
async safeExecute(hook, ctx) {
|
|
699
|
+
try {
|
|
700
|
+
await hook.execute(ctx);
|
|
701
|
+
} catch (error) {
|
|
702
|
+
console.error(`Hook "${hook.name}" failed:`, error);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
async safeExecuteAndReturn(hook, ctx) {
|
|
706
|
+
try {
|
|
707
|
+
return await hook.execute(ctx);
|
|
708
|
+
} catch (error) {
|
|
709
|
+
console.error(`Hook "${hook.name}" failed:`, error);
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
// packages/core/src/env/hook/global-hook-manager.ts
|
|
715
|
+
var globalHookManager = new HookManager;
|
|
716
|
+
var AgentHookPoints = {
|
|
717
|
+
BEFORE_START: "agent:before.start",
|
|
718
|
+
BEFORE_LLM: "agent:before.llm",
|
|
719
|
+
AFTER_LLM: "agent:after.llm",
|
|
720
|
+
BEFORE_TOOL: "agent:before.tool",
|
|
721
|
+
AFTER_TOOL: "agent:after.tool",
|
|
722
|
+
ON_ITERATION: "agent:on.iteration",
|
|
723
|
+
ON_THRESHOLD: "agent:on.threshold",
|
|
724
|
+
AFTER_COMPLETE: "agent:after.complete",
|
|
725
|
+
ON_ERROR: "agent:on.error"
|
|
726
|
+
};
|
|
727
|
+
var LLMHookPoints = {
|
|
728
|
+
BEFORE_INVOKE: "llm:before.invoke",
|
|
729
|
+
AFTER_INVOKE: "llm:after.invoke",
|
|
730
|
+
ON_STREAM: "llm:on.stream"
|
|
731
|
+
};
|
|
732
|
+
var ToolHookPoints = {
|
|
733
|
+
BEFORE_EXECUTE: "tool:before.execute",
|
|
734
|
+
AFTER_EXECUTE: "tool:after.execute",
|
|
735
|
+
BEFORE_REGISTER: "tool:before.register",
|
|
736
|
+
AFTER_REGISTER: "tool:after.register",
|
|
737
|
+
ON_ERROR: "tool:on.error"
|
|
738
|
+
};
|
|
739
|
+
var hookPointAliases = {
|
|
740
|
+
"before.tool": "tool:before.execute",
|
|
741
|
+
"after.tool": "tool:after.execute",
|
|
742
|
+
"llm.before-invoke": "llm:before.invoke",
|
|
743
|
+
"llm.after-invoke": "llm:after.invoke",
|
|
744
|
+
"llm.stream": "llm:on.stream"
|
|
745
|
+
};
|
|
746
|
+
function setupAliasHooks() {
|
|
747
|
+
const originalRegister = globalHookManager.register.bind(globalHookManager);
|
|
748
|
+
globalHookManager.register = function(hookPoint, hook) {
|
|
749
|
+
originalRegister(hookPoint, hook);
|
|
750
|
+
const alias = hookPointAliases[hookPoint];
|
|
751
|
+
if (alias) {
|
|
752
|
+
originalRegister(alias, hook);
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
setupAliasHooks();
|
|
757
|
+
async function executeAgentHook(hookPoint, data, metadata = {}) {
|
|
758
|
+
await globalHookManager.execute(hookPoint, data, metadata);
|
|
759
|
+
}
|
|
760
|
+
async function executeAgentHookWithIntervention(hookPoint, data, metadata = {}) {
|
|
761
|
+
return globalHookManager.executeWithIntervention(hookPoint, data, metadata);
|
|
762
|
+
}
|
|
763
|
+
async function executeLLMHook(hookPoint, data, metadata = {}) {
|
|
764
|
+
await globalHookManager.execute(hookPoint, data, metadata);
|
|
765
|
+
}
|
|
766
|
+
async function executeToolHook(hookPoint, data, metadata = {}) {
|
|
767
|
+
await globalHookManager.execute(hookPoint, data, metadata);
|
|
768
|
+
}
|
|
769
|
+
// packages/core/src/env/debug/formatters/trace-formatter.ts
|
|
770
|
+
class TraceFormatter {
|
|
771
|
+
formatEntry(entry, options = {}) {
|
|
772
|
+
const { pretty = false } = options;
|
|
773
|
+
const actionSymbol = entry.action === "enter" ? ">>>" : entry.action === "error" ? "!!!" : "<<<";
|
|
774
|
+
let output = `${actionSymbol} ${entry.function} ${entry.action}`;
|
|
775
|
+
if (entry.action === "enter" && entry.params) {
|
|
776
|
+
output += `
|
|
777
|
+
` + this.formatData(entry.params, options);
|
|
778
|
+
} else if (entry.action === "quit") {
|
|
779
|
+
if (entry.durationMs !== undefined) {
|
|
780
|
+
output += ` (${entry.durationMs}ms)`;
|
|
781
|
+
}
|
|
782
|
+
if (entry.result) {
|
|
783
|
+
output += `
|
|
784
|
+
` + this.formatData(entry.result, options);
|
|
785
|
+
}
|
|
786
|
+
} else if (entry.action === "error" && entry.error) {
|
|
787
|
+
output += `: ${entry.error}`;
|
|
788
|
+
}
|
|
789
|
+
if (pretty) {
|
|
790
|
+
output = this.indentMultilineContent(output);
|
|
791
|
+
}
|
|
792
|
+
return output;
|
|
793
|
+
}
|
|
794
|
+
indentMultilineContent(text, indent = " ") {
|
|
795
|
+
const lines = text.split(`
|
|
796
|
+
`);
|
|
797
|
+
if (lines.length <= 1)
|
|
798
|
+
return text;
|
|
799
|
+
return lines.map((line, i) => i === 0 ? line : `${indent}${line}`).join(`
|
|
800
|
+
`);
|
|
801
|
+
}
|
|
802
|
+
formatEntries(entries, options = {}) {
|
|
803
|
+
if (entries.length === 0) {
|
|
804
|
+
return "No trace entries";
|
|
805
|
+
}
|
|
806
|
+
const formatted = entries.map((entry) => this.formatEntry(entry, options));
|
|
807
|
+
if (options.pretty) {
|
|
808
|
+
return formatted.join(`
|
|
809
|
+
` + "-".repeat(80) + `
|
|
810
|
+
`);
|
|
811
|
+
}
|
|
812
|
+
return formatted.join(`
|
|
813
|
+
`);
|
|
814
|
+
}
|
|
815
|
+
formatData(data, options = {}) {
|
|
816
|
+
const { pretty = false, maxDepth = 10 } = options;
|
|
817
|
+
if (pretty) {
|
|
818
|
+
return this.formatPretty(data, 0, maxDepth);
|
|
819
|
+
}
|
|
820
|
+
return JSON.stringify(data);
|
|
821
|
+
}
|
|
822
|
+
formatPretty(data, indent = 0, maxDepth) {
|
|
823
|
+
const baseIndent = " ".repeat(indent);
|
|
824
|
+
const nextIndent = " ".repeat(indent + 1);
|
|
825
|
+
if (data === null || data === undefined) {
|
|
826
|
+
return String(data);
|
|
827
|
+
}
|
|
828
|
+
if (typeof data === "string") {
|
|
829
|
+
if (data.includes(`
|
|
830
|
+
`)) {
|
|
831
|
+
return data.split(`
|
|
832
|
+
`).map((line, i) => i === 0 ? line : `${nextIndent}${line}`).join(`
|
|
833
|
+
`);
|
|
834
|
+
}
|
|
835
|
+
return data;
|
|
836
|
+
}
|
|
837
|
+
if (typeof data === "number" || typeof data === "boolean") {
|
|
838
|
+
return String(data);
|
|
839
|
+
}
|
|
840
|
+
if (Array.isArray(data)) {
|
|
841
|
+
if (data.length === 0)
|
|
842
|
+
return "[]";
|
|
843
|
+
const items = data.map((item) => {
|
|
844
|
+
const formatted = this.formatPretty(item, indent + 1, maxDepth);
|
|
845
|
+
return formatted.split(`
|
|
846
|
+
`).map((line) => ` ${line}`).join(`
|
|
847
|
+
`);
|
|
848
|
+
});
|
|
849
|
+
return `[
|
|
850
|
+
` + items.join(`,
|
|
851
|
+
`) + `
|
|
852
|
+
` + baseIndent + "]";
|
|
853
|
+
}
|
|
854
|
+
if (typeof data === "object") {
|
|
855
|
+
const obj = data;
|
|
856
|
+
const keys = Object.keys(obj);
|
|
857
|
+
if (keys.length === 0)
|
|
858
|
+
return "{}";
|
|
859
|
+
if (indent >= maxDepth) {
|
|
860
|
+
return "{...}";
|
|
861
|
+
}
|
|
862
|
+
const entries = keys.map((key) => {
|
|
863
|
+
const value = obj[key];
|
|
864
|
+
let formatted;
|
|
865
|
+
if (typeof value === "object" && value !== null) {
|
|
866
|
+
formatted = this.formatPretty(value, indent + 1, maxDepth);
|
|
867
|
+
} else if (typeof value === "string" && value.includes(`
|
|
868
|
+
`)) {
|
|
869
|
+
formatted = value.split(`
|
|
870
|
+
`).map((line, i) => i === 0 ? line : `${nextIndent}${line}`).join(`
|
|
871
|
+
`);
|
|
872
|
+
} else {
|
|
873
|
+
formatted = JSON.stringify(value);
|
|
874
|
+
}
|
|
875
|
+
return ` ${key}: ` + formatted.split(`
|
|
876
|
+
`).map((line, i) => i === 0 ? line : nextIndent + line).join(`
|
|
877
|
+
`);
|
|
878
|
+
});
|
|
879
|
+
return `{
|
|
880
|
+
` + entries.join(`,
|
|
881
|
+
`) + `
|
|
882
|
+
` + baseIndent + "}";
|
|
883
|
+
}
|
|
884
|
+
return JSON.stringify(data);
|
|
885
|
+
}
|
|
886
|
+
toJSON(entries) {
|
|
887
|
+
return JSON.stringify(entries, null, 2);
|
|
888
|
+
}
|
|
889
|
+
toSummary(entries) {
|
|
890
|
+
if (entries.length === 0) {
|
|
891
|
+
return "No trace entries";
|
|
892
|
+
}
|
|
893
|
+
const functions = new Set(entries.map((e) => e.function));
|
|
894
|
+
const actions = entries.reduce((acc, e) => {
|
|
895
|
+
acc[e.action] = (acc[e.action] || 0) + 1;
|
|
896
|
+
return acc;
|
|
897
|
+
}, {});
|
|
898
|
+
let summary = `Trace Summary (${entries.length} entries)
|
|
899
|
+
`;
|
|
900
|
+
summary += `Functions: ${[...functions].join(", ")}
|
|
901
|
+
`;
|
|
902
|
+
summary += `Actions: ${Object.entries(actions).map(([k, v]) => `${k}=${v}`).join(", ")}`;
|
|
903
|
+
return summary;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
// packages/core/src/env/debug/formatters/tree-formatter.ts
|
|
907
|
+
var Colors = {
|
|
908
|
+
reset: "\x1B[0m",
|
|
909
|
+
bold: "\x1B[1m",
|
|
910
|
+
dim: "\x1B[2m",
|
|
911
|
+
red: "\x1B[31m",
|
|
912
|
+
green: "\x1B[32m",
|
|
913
|
+
yellow: "\x1B[33m",
|
|
914
|
+
cyan: "\x1B[36m",
|
|
915
|
+
white: "\x1B[37m"
|
|
916
|
+
};
|
|
917
|
+
function color(text, colorCode) {
|
|
918
|
+
return `${colorCode}${text}${Colors.reset}`;
|
|
919
|
+
}
|
|
920
|
+
var CHARS = {
|
|
921
|
+
VERTICAL: "\u2502",
|
|
922
|
+
VERTICAL_CONNECTOR: "\u251C\u2500\u2500",
|
|
923
|
+
LAST_CONNECTOR: "\u2514\u2500\u2500",
|
|
924
|
+
SPACE: " ",
|
|
925
|
+
BAR: "\u2588",
|
|
926
|
+
EMPTY: "\u2591",
|
|
927
|
+
DASH: "\u2500"
|
|
928
|
+
};
|
|
929
|
+
|
|
930
|
+
class TreeFormatter {
|
|
931
|
+
options;
|
|
932
|
+
constructor(options = {}) {
|
|
933
|
+
this.options = {
|
|
934
|
+
showPercent: true,
|
|
935
|
+
showTimeBar: false,
|
|
936
|
+
barWidth: 10,
|
|
937
|
+
...options
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
colorByPercent(percent) {
|
|
941
|
+
if (percent >= 80)
|
|
942
|
+
return Colors.red;
|
|
943
|
+
if (percent >= 50)
|
|
944
|
+
return Colors.yellow;
|
|
945
|
+
if (percent >= 20)
|
|
946
|
+
return Colors.cyan;
|
|
947
|
+
return Colors.dim;
|
|
948
|
+
}
|
|
949
|
+
formatTree(node) {
|
|
950
|
+
const lines = [];
|
|
951
|
+
const totalDuration = node.durationMs || 0;
|
|
952
|
+
const percentBase = node.children.reduce((sum, c) => sum + (c.durationMs || 0), 0) || totalDuration;
|
|
953
|
+
lines.push(this.formatNode(node, totalDuration, percentBase, "", true, true));
|
|
954
|
+
this.formatChildren(node.children, "", lines, totalDuration, percentBase);
|
|
955
|
+
return lines.join(`
|
|
956
|
+
`);
|
|
957
|
+
}
|
|
958
|
+
formatNode(node, totalDuration, percentBase, prefix, isLast, isRoot) {
|
|
959
|
+
const connector = isRoot ? "" : isLast ? CHARS.LAST_CONNECTOR : CHARS.VERTICAL_CONNECTOR;
|
|
960
|
+
const prefixStr = isRoot ? "" : prefix;
|
|
961
|
+
let line = `${prefixStr}${connector} ${node.function}`;
|
|
962
|
+
if (node.durationMs !== undefined) {
|
|
963
|
+
line += color(` (${this.formatDuration(node.durationMs)})`, Colors.dim);
|
|
964
|
+
}
|
|
965
|
+
if (this.options.showPercent && percentBase > 0 && node.durationMs !== undefined && node.function !== "(root)" && !isRoot) {
|
|
966
|
+
const percent = Math.round(node.durationMs / percentBase * 100);
|
|
967
|
+
const percentStr = ` [${percent}%]`;
|
|
968
|
+
line += color(percentStr, this.colorByPercent(percent));
|
|
969
|
+
}
|
|
970
|
+
if (this.options.showTimeBar && node.durationMs !== undefined && totalDuration > 0 && !isRoot) {
|
|
971
|
+
line += " " + this.renderTimeBar(node.durationMs, totalDuration);
|
|
972
|
+
}
|
|
973
|
+
return isRoot ? color(line, Colors.bold) : line;
|
|
974
|
+
}
|
|
975
|
+
formatChildren(children, prefix, lines, totalDuration, percentBase) {
|
|
976
|
+
children.forEach((child, index) => {
|
|
977
|
+
const isLast = index === children.length - 1;
|
|
978
|
+
const newPrefix = prefix + (isLast ? " " : `${CHARS.VERTICAL} `);
|
|
979
|
+
lines.push(this.formatNode(child, totalDuration, percentBase, prefix, isLast, false));
|
|
980
|
+
if (child.children.length > 0) {
|
|
981
|
+
this.formatChildren(child.children, newPrefix, lines, totalDuration, percentBase);
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
renderTimeBar(duration, total) {
|
|
986
|
+
const width = this.options.barWidth || 10;
|
|
987
|
+
const filledWidth = Math.round(duration / total * width);
|
|
988
|
+
const emptyWidth = width - filledWidth;
|
|
989
|
+
const bar = CHARS.BAR.repeat(filledWidth) + CHARS.EMPTY.repeat(emptyWidth);
|
|
990
|
+
return color(`[${bar}]`, Colors.dim);
|
|
991
|
+
}
|
|
992
|
+
formatDuration(ms) {
|
|
993
|
+
if (ms < 1000) {
|
|
994
|
+
return `${ms}ms`;
|
|
995
|
+
} else if (ms < 60000) {
|
|
996
|
+
return `${(ms / 1000).toFixed(2)}s`;
|
|
997
|
+
} else if (ms < 3600000) {
|
|
998
|
+
const mins = Math.floor(ms / 60000);
|
|
999
|
+
const secs = Math.round(ms % 60000 / 1000);
|
|
1000
|
+
return `${mins}m ${secs}s`;
|
|
1001
|
+
} else {
|
|
1002
|
+
const hours = Math.floor(ms / 3600000);
|
|
1003
|
+
const mins = Math.floor(ms % 3600000 / 60000);
|
|
1004
|
+
return `${hours}h ${mins}m`;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
formatTrees(trees) {
|
|
1008
|
+
if (trees.length === 0) {
|
|
1009
|
+
return "No trace trees";
|
|
1010
|
+
}
|
|
1011
|
+
return trees.map((tree) => this.formatTree(tree)).join(`
|
|
1012
|
+
|
|
1013
|
+
`);
|
|
1014
|
+
}
|
|
1015
|
+
toJSON(trees) {
|
|
1016
|
+
return JSON.stringify(trees, null, 2);
|
|
1017
|
+
}
|
|
1018
|
+
formatSummary(node) {
|
|
1019
|
+
const rows = [];
|
|
1020
|
+
const total = node.durationMs || 0;
|
|
1021
|
+
const childrenTotal = node.children.reduce((sum, c) => sum + (c.durationMs || 0), 0);
|
|
1022
|
+
const percentBase = total > 0 ? total : childrenTotal;
|
|
1023
|
+
const collect = (n, depth) => {
|
|
1024
|
+
if (n.durationMs !== undefined) {
|
|
1025
|
+
rows.push({
|
|
1026
|
+
func: n.function,
|
|
1027
|
+
duration: n.durationMs,
|
|
1028
|
+
percent: percentBase > 0 ? Math.round(n.durationMs / percentBase * 100) : 0,
|
|
1029
|
+
depth
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
n.children.forEach((child) => collect(child, depth + 1));
|
|
1033
|
+
};
|
|
1034
|
+
collect(node, 0);
|
|
1035
|
+
rows.sort((a, b) => b.duration - a.duration);
|
|
1036
|
+
const lines = [];
|
|
1037
|
+
lines.push(color(`
|
|
1038
|
+
=== Duration Summary ===`, Colors.bold));
|
|
1039
|
+
lines.push(color(`(sorted by time consumption)
|
|
1040
|
+
`, Colors.dim));
|
|
1041
|
+
lines.push(color(` ${"\u2500".repeat(65)}`, Colors.dim));
|
|
1042
|
+
lines.push(color(` ${"FUNCTION".padEnd(35)} ${"TIME".padEnd(12)} %`, Colors.dim));
|
|
1043
|
+
lines.push(color(` ${"\u2500".repeat(65)}`, Colors.dim));
|
|
1044
|
+
for (const row of rows) {
|
|
1045
|
+
const indent = " ".repeat(row.depth);
|
|
1046
|
+
const funcDisplay = row.depth > 0 ? "\u2514\u2500 ".repeat(row.depth) + row.func : row.func;
|
|
1047
|
+
const percentStr = String(row.percent).padStart(3) + "%";
|
|
1048
|
+
const percentColor = row.percent >= 50 ? Colors.red : row.percent >= 20 ? Colors.yellow : Colors.dim;
|
|
1049
|
+
lines.push(` ${indent}${funcDisplay.substring(0, 35).padEnd(35)}${this.formatDuration(row.duration).padEnd(12)} ${color(percentStr, percentColor)}`);
|
|
1050
|
+
}
|
|
1051
|
+
return lines.join(`
|
|
1052
|
+
`);
|
|
1053
|
+
}
|
|
1054
|
+
formatTimeline(node) {
|
|
1055
|
+
const lines = [];
|
|
1056
|
+
const totalDuration = node.durationMs || 0;
|
|
1057
|
+
lines.push(color(`
|
|
1058
|
+
=== Timeline: ${node.function} ===`, Colors.bold));
|
|
1059
|
+
lines.push(color(`Total: ${this.formatDuration(totalDuration)}
|
|
1060
|
+
`, Colors.dim));
|
|
1061
|
+
const timeline = this.buildTimeline(node, totalDuration, 0, 0);
|
|
1062
|
+
lines.push(...timeline);
|
|
1063
|
+
return lines.join(`
|
|
1064
|
+
`);
|
|
1065
|
+
}
|
|
1066
|
+
buildTimeline(node, totalDuration, absoluteOffset, depth) {
|
|
1067
|
+
const lines = [];
|
|
1068
|
+
const width = 40;
|
|
1069
|
+
const startPos = Math.round(absoluteOffset / totalDuration * width);
|
|
1070
|
+
const durationWidth = node.durationMs !== undefined ? Math.max(2, Math.round(node.durationMs / totalDuration * width)) : 2;
|
|
1071
|
+
const bar = " ".repeat(Math.max(0, startPos)) + CHARS.BAR.repeat(durationWidth);
|
|
1072
|
+
const indent = " ".repeat(depth);
|
|
1073
|
+
const funcName = node.function.length > 25 ? "..." + node.function.slice(-22) : node.function;
|
|
1074
|
+
const time = node.durationMs !== undefined ? this.formatDuration(node.durationMs) : "-";
|
|
1075
|
+
const percent = node.durationMs !== undefined ? ` [${Math.round(node.durationMs / totalDuration * 100)}%]` : "";
|
|
1076
|
+
lines.push(`${indent}${funcName.padEnd(25)} ${bar} ${color(time + percent, Colors.dim)}`);
|
|
1077
|
+
let childOffset = absoluteOffset;
|
|
1078
|
+
for (const child of node.children) {
|
|
1079
|
+
lines.push(...this.buildTimeline(child, totalDuration, childOffset, depth + 1));
|
|
1080
|
+
childOffset += child.durationMs || 0;
|
|
1081
|
+
}
|
|
1082
|
+
return lines;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
// packages/core/src/env/debug/formatters/repl-formatter.ts
|
|
1086
|
+
class ReplFormatter {
|
|
1087
|
+
context = {};
|
|
1088
|
+
history = [];
|
|
1089
|
+
getWelcomeMessage() {
|
|
1090
|
+
return `========================================
|
|
1091
|
+
Debug REPL - Interactive Debugger
|
|
1092
|
+
========================================
|
|
1093
|
+
|
|
1094
|
+
Commands:
|
|
1095
|
+
list [limit] - List trace IDs
|
|
1096
|
+
trace [options] - Show trace details
|
|
1097
|
+
tree [options] - Show call tree
|
|
1098
|
+
help - Show this help
|
|
1099
|
+
exit - Exit REPL
|
|
1100
|
+
|
|
1101
|
+
Examples:
|
|
1102
|
+
list 10
|
|
1103
|
+
trace --func llm.invoke
|
|
1104
|
+
tree --trace trace-001
|
|
1105
|
+
|
|
1106
|
+
========================================
|
|
1107
|
+
`;
|
|
1108
|
+
}
|
|
1109
|
+
formatPrompt(context) {
|
|
1110
|
+
const ctx = { ...this.context, ...context };
|
|
1111
|
+
let prompt = "debug";
|
|
1112
|
+
if (ctx.traceId) {
|
|
1113
|
+
prompt += `:${ctx.traceId}`;
|
|
1114
|
+
} else if (ctx.function) {
|
|
1115
|
+
prompt += `:${ctx.function}`;
|
|
1116
|
+
}
|
|
1117
|
+
return `${prompt}> `;
|
|
1118
|
+
}
|
|
1119
|
+
getHelpText() {
|
|
1120
|
+
return `
|
|
1121
|
+
Available Commands:
|
|
1122
|
+
==================
|
|
1123
|
+
list [n] - List recent trace IDs (default: 10)
|
|
1124
|
+
trace [options] - Show trace details
|
|
1125
|
+
--func <name> - Filter by function name
|
|
1126
|
+
--trace <id> - Show specific trace
|
|
1127
|
+
--pretty - Pretty print output
|
|
1128
|
+
--limit <n> - Limit results
|
|
1129
|
+
tree [options] - Show call tree
|
|
1130
|
+
--func <name> - Show tree for function
|
|
1131
|
+
--trace <id> - Show tree for trace
|
|
1132
|
+
clear - Clear screen
|
|
1133
|
+
help - Show this help
|
|
1134
|
+
exit - Exit REPL
|
|
1135
|
+
`;
|
|
1136
|
+
}
|
|
1137
|
+
formatCommandResult(command, data) {
|
|
1138
|
+
switch (command) {
|
|
1139
|
+
case "list":
|
|
1140
|
+
return this.formatListResult(data);
|
|
1141
|
+
case "trace":
|
|
1142
|
+
return this.formatTraceResult(data);
|
|
1143
|
+
case "tree":
|
|
1144
|
+
return this.formatTreeResult(data);
|
|
1145
|
+
default:
|
|
1146
|
+
return `Unknown command: ${command}`;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
formatListResult(data) {
|
|
1150
|
+
const { traceIds, count } = data;
|
|
1151
|
+
if (traceIds.length === 0) {
|
|
1152
|
+
return "No traces found.";
|
|
1153
|
+
}
|
|
1154
|
+
let output = `
|
|
1155
|
+
`;
|
|
1156
|
+
output += ` TraceId First Time Count
|
|
1157
|
+
`;
|
|
1158
|
+
output += " " + "-".repeat(70) + `
|
|
1159
|
+
`;
|
|
1160
|
+
for (const trace of traceIds) {
|
|
1161
|
+
const time = trace.firstTime.replace("T", " ").substring(0, 19);
|
|
1162
|
+
output += ` ${trace.traceId.padEnd(18)} ${time.padEnd(22)} ${trace.count}
|
|
1163
|
+
`;
|
|
1164
|
+
}
|
|
1165
|
+
output += `
|
|
1166
|
+
Total: ${count} traces
|
|
1167
|
+
`;
|
|
1168
|
+
return output;
|
|
1169
|
+
}
|
|
1170
|
+
formatTraceResult(entries) {
|
|
1171
|
+
if (entries.length === 0) {
|
|
1172
|
+
return "No traces found.";
|
|
1173
|
+
}
|
|
1174
|
+
let output = "";
|
|
1175
|
+
let currentTraceId = null;
|
|
1176
|
+
for (const entry of entries) {
|
|
1177
|
+
if (entry.traceId !== currentTraceId) {
|
|
1178
|
+
currentTraceId = entry.traceId;
|
|
1179
|
+
output += `
|
|
1180
|
+
=== Trace: ${entry.traceId} ===
|
|
1181
|
+
|
|
1182
|
+
`;
|
|
1183
|
+
}
|
|
1184
|
+
if (entry.action === "enter") {
|
|
1185
|
+
output += `>>> ${entry.function} enter:`;
|
|
1186
|
+
if (entry.params) {
|
|
1187
|
+
output += `
|
|
1188
|
+
` + this.formatJsonCompact(entry.params);
|
|
1189
|
+
}
|
|
1190
|
+
output += `
|
|
1191
|
+
`;
|
|
1192
|
+
} else if (entry.action === "quit") {
|
|
1193
|
+
output += `<<< ${entry.function} quit`;
|
|
1194
|
+
if (entry.durationMs !== undefined) {
|
|
1195
|
+
output += ` (${entry.durationMs}ms)`;
|
|
1196
|
+
}
|
|
1197
|
+
if (entry.result) {
|
|
1198
|
+
output += `:
|
|
1199
|
+
` + this.formatJsonCompact(entry.result);
|
|
1200
|
+
}
|
|
1201
|
+
output += `
|
|
1202
|
+
`;
|
|
1203
|
+
} else if (entry.action === "error") {
|
|
1204
|
+
output += `!!! ${entry.function} error: ${entry.error}
|
|
1205
|
+
`;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
return output;
|
|
1209
|
+
}
|
|
1210
|
+
formatTreeResult(trees) {
|
|
1211
|
+
if (trees.length === 0) {
|
|
1212
|
+
return "No trees found.";
|
|
1213
|
+
}
|
|
1214
|
+
let output = "";
|
|
1215
|
+
for (const tree of trees) {
|
|
1216
|
+
output += this.formatTreeNode(tree, "");
|
|
1217
|
+
}
|
|
1218
|
+
return output;
|
|
1219
|
+
}
|
|
1220
|
+
formatTreeNode(node, prefix) {
|
|
1221
|
+
const duration = node.durationMs !== undefined ? ` (${node.durationMs}ms)` : "";
|
|
1222
|
+
let output = `${prefix}${node.function}${duration}
|
|
1223
|
+
`;
|
|
1224
|
+
const childPrefix = prefix + " ";
|
|
1225
|
+
for (let i = 0;i < node.children.length; i++) {
|
|
1226
|
+
const isLast = i === node.children.length - 1;
|
|
1227
|
+
const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
1228
|
+
output += this.formatTreeNode(node.children[i], childPrefix + connector);
|
|
1229
|
+
}
|
|
1230
|
+
return output;
|
|
1231
|
+
}
|
|
1232
|
+
formatJsonCompact(data, indent = 2) {
|
|
1233
|
+
const spaces = " ".repeat(indent);
|
|
1234
|
+
if (data === null || data === undefined) {
|
|
1235
|
+
return `${spaces}null
|
|
1236
|
+
`;
|
|
1237
|
+
}
|
|
1238
|
+
if (typeof data === "string") {
|
|
1239
|
+
if (data.length > 200) {
|
|
1240
|
+
return `${spaces}"${data.substring(0, 200)}..."
|
|
1241
|
+
`;
|
|
1242
|
+
}
|
|
1243
|
+
return `${spaces}"${data}"
|
|
1244
|
+
`;
|
|
1245
|
+
}
|
|
1246
|
+
if (typeof data === "number" || typeof data === "boolean") {
|
|
1247
|
+
return `${spaces}${data}
|
|
1248
|
+
`;
|
|
1249
|
+
}
|
|
1250
|
+
if (Array.isArray(data)) {
|
|
1251
|
+
if (data.length === 0)
|
|
1252
|
+
return `${spaces}[]
|
|
1253
|
+
`;
|
|
1254
|
+
let output = `${spaces}[
|
|
1255
|
+
`;
|
|
1256
|
+
for (const item of data) {
|
|
1257
|
+
output += this.formatJsonCompact(item, indent + 1);
|
|
1258
|
+
}
|
|
1259
|
+
output += `${spaces}]
|
|
1260
|
+
`;
|
|
1261
|
+
return output;
|
|
1262
|
+
}
|
|
1263
|
+
if (typeof data === "object") {
|
|
1264
|
+
const entries = Object.entries(data);
|
|
1265
|
+
if (entries.length === 0)
|
|
1266
|
+
return `${spaces}{}
|
|
1267
|
+
`;
|
|
1268
|
+
let output = `${spaces}{
|
|
1269
|
+
`;
|
|
1270
|
+
for (const [key, val] of entries) {
|
|
1271
|
+
output += `${spaces} ${key}: ${JSON.stringify(val).substring(0, 50)}`;
|
|
1272
|
+
if (JSON.stringify(val).length > 50) {
|
|
1273
|
+
output += "...";
|
|
1274
|
+
}
|
|
1275
|
+
output += `
|
|
1276
|
+
`;
|
|
1277
|
+
}
|
|
1278
|
+
output += `${spaces}}
|
|
1279
|
+
`;
|
|
1280
|
+
return output;
|
|
1281
|
+
}
|
|
1282
|
+
return `${spaces}${String(data)}
|
|
1283
|
+
`;
|
|
1284
|
+
}
|
|
1285
|
+
parseCommand(input) {
|
|
1286
|
+
const trimmed = input.trim();
|
|
1287
|
+
if (!trimmed) {
|
|
1288
|
+
return { command: "help", args: {} };
|
|
1289
|
+
}
|
|
1290
|
+
if (["exit", "quit", "q"].includes(trimmed.toLowerCase())) {
|
|
1291
|
+
return { command: "exit", args: {} };
|
|
1292
|
+
}
|
|
1293
|
+
if (trimmed.toLowerCase() === "clear") {
|
|
1294
|
+
return { command: "clear", args: {} };
|
|
1295
|
+
}
|
|
1296
|
+
if (["help", "?", "h"].includes(trimmed.toLowerCase())) {
|
|
1297
|
+
return { command: "help", args: {} };
|
|
1298
|
+
}
|
|
1299
|
+
const parts = trimmed.match(/(?:[^\s"]+|"[^"]*")+/g) || [];
|
|
1300
|
+
if (parts.length === 0) {
|
|
1301
|
+
return { command: "help", args: {} };
|
|
1302
|
+
}
|
|
1303
|
+
const command = parts[0]?.toLowerCase() ?? "";
|
|
1304
|
+
const args = {};
|
|
1305
|
+
for (let i = 1;i < parts.length; i++) {
|
|
1306
|
+
const part = parts[i];
|
|
1307
|
+
if (part.startsWith("--")) {
|
|
1308
|
+
const key = part.substring(2);
|
|
1309
|
+
const next = parts[i + 1];
|
|
1310
|
+
if (next && !next.startsWith("--")) {
|
|
1311
|
+
args[key] = next;
|
|
1312
|
+
i++;
|
|
1313
|
+
} else {
|
|
1314
|
+
args[key] = "true";
|
|
1315
|
+
}
|
|
1316
|
+
} else if (part.startsWith("-")) {
|
|
1317
|
+
const key = part.substring(1);
|
|
1318
|
+
const next = parts[i + 1];
|
|
1319
|
+
if (next && !next.startsWith("-")) {
|
|
1320
|
+
args[key] = next;
|
|
1321
|
+
i++;
|
|
1322
|
+
} else {
|
|
1323
|
+
args[key] = "true";
|
|
1324
|
+
}
|
|
1325
|
+
} else {
|
|
1326
|
+
if (command === "list" && !args.limit) {
|
|
1327
|
+
args.limit = part;
|
|
1328
|
+
} else if (command === "trace" && !args.func) {
|
|
1329
|
+
args.func = part;
|
|
1330
|
+
} else if (command === "tree" && !args.func) {
|
|
1331
|
+
args.func = part;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return { command, args };
|
|
1336
|
+
}
|
|
1337
|
+
setContext(context) {
|
|
1338
|
+
this.context = { ...this.context, ...context };
|
|
1339
|
+
}
|
|
1340
|
+
getContext() {
|
|
1341
|
+
return { ...this.context };
|
|
1342
|
+
}
|
|
1343
|
+
addToHistory(command) {
|
|
1344
|
+
if (command.trim() && this.history[this.history.length - 1] !== command) {
|
|
1345
|
+
this.history.push(command);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
getHistory() {
|
|
1349
|
+
return [...this.history];
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
// packages/core/src/env/component.ts
|
|
1353
|
+
class BaseComponent {
|
|
1354
|
+
_status = "created";
|
|
1355
|
+
_enabled = true;
|
|
1356
|
+
env;
|
|
1357
|
+
hookManager;
|
|
1358
|
+
constructor() {
|
|
1359
|
+
this.hookManager = new HookManager;
|
|
1360
|
+
}
|
|
1361
|
+
getStatus() {
|
|
1362
|
+
return this._status;
|
|
1363
|
+
}
|
|
1364
|
+
getConfig() {
|
|
1365
|
+
return {
|
|
1366
|
+
name: this.name,
|
|
1367
|
+
version: this.version,
|
|
1368
|
+
enabled: this._enabled,
|
|
1369
|
+
env: this.env
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
isEnvInitialized() {
|
|
1373
|
+
return this._status !== "created";
|
|
1374
|
+
}
|
|
1375
|
+
setStatus(status) {
|
|
1376
|
+
this._status = status;
|
|
1377
|
+
}
|
|
1378
|
+
getEnv() {
|
|
1379
|
+
return this.env;
|
|
1380
|
+
}
|
|
1381
|
+
async init(config) {
|
|
1382
|
+
if (config?.env) {
|
|
1383
|
+
this.env = config.env;
|
|
1384
|
+
}
|
|
1385
|
+
this.setStatus("initializing");
|
|
1386
|
+
try {
|
|
1387
|
+
if (config?.name)
|
|
1388
|
+
Object.defineProperty(this, "name", { value: config.name, writable: false });
|
|
1389
|
+
if (config?.version)
|
|
1390
|
+
Object.defineProperty(this, "version", { value: config.version, writable: false });
|
|
1391
|
+
} catch {}
|
|
1392
|
+
if (config?.enabled !== undefined)
|
|
1393
|
+
this._enabled = config.enabled;
|
|
1394
|
+
this.hookManager.setComponentInfo(this.name, this.version);
|
|
1395
|
+
await this.onInit();
|
|
1396
|
+
this.setStatus("running");
|
|
1397
|
+
}
|
|
1398
|
+
async start() {
|
|
1399
|
+
if (this._started)
|
|
1400
|
+
return;
|
|
1401
|
+
this._started = true;
|
|
1402
|
+
await this.onStart();
|
|
1403
|
+
this.setStatus("running");
|
|
1404
|
+
}
|
|
1405
|
+
async stop() {
|
|
1406
|
+
this.setStatus("stopping");
|
|
1407
|
+
this.hookManager.clear();
|
|
1408
|
+
await this.onStop();
|
|
1409
|
+
this.setStatus("stopped");
|
|
1410
|
+
}
|
|
1411
|
+
async onInit() {}
|
|
1412
|
+
async onStart() {}
|
|
1413
|
+
async onStop() {}
|
|
1414
|
+
registerHook(hookPoint, hook) {
|
|
1415
|
+
this.hookManager.register(hookPoint, hook);
|
|
1416
|
+
}
|
|
1417
|
+
addHook(hookPoint, name, fn, priority) {
|
|
1418
|
+
this.hookManager.register(hookPoint, createHook({ name, priority }, fn));
|
|
1419
|
+
}
|
|
1420
|
+
removeHook(hookPoint, name) {
|
|
1421
|
+
return this.hookManager.unregister(hookPoint, name);
|
|
1422
|
+
}
|
|
1423
|
+
async executeHooks(hookPoint, data, metadata) {
|
|
1424
|
+
await this.hookManager.execute(hookPoint, data, metadata);
|
|
1425
|
+
}
|
|
1426
|
+
getConfigComponent() {
|
|
1427
|
+
return this.env?.getComponent("config");
|
|
1428
|
+
}
|
|
1429
|
+
getRuntimeConfig(key, defaultValue) {
|
|
1430
|
+
const configComponent = this.getConfigComponent();
|
|
1431
|
+
if (configComponent) {
|
|
1432
|
+
const value = configComponent.get(key);
|
|
1433
|
+
if (value !== undefined) {
|
|
1434
|
+
return value;
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
return defaultValue;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// packages/core/src/env/debug/reader/log-reader.ts
|
|
1442
|
+
import * as fs from "fs";
|
|
1443
|
+
import * as readline from "readline";
|
|
1444
|
+
import * as path from "path";
|
|
1445
|
+
import { glob } from "glob";
|
|
1446
|
+
function getXDGDataHome() {
|
|
1447
|
+
return process.env.XDG_DATA_HOME || path.join(process.env.HOME || "/home/" + (process.env.USER || "user"), ".local", "share");
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
class LogReader {
|
|
1451
|
+
logDir;
|
|
1452
|
+
constructor(options) {
|
|
1453
|
+
this.logDir = options?.logDir || path.join(getXDGDataHome(), "roy-agent", "logs");
|
|
1454
|
+
}
|
|
1455
|
+
getLogPath(filename) {
|
|
1456
|
+
if (!filename) {
|
|
1457
|
+
return path.join(this.logDir, "app.log");
|
|
1458
|
+
}
|
|
1459
|
+
if (path.isAbsolute(filename)) {
|
|
1460
|
+
return filename;
|
|
1461
|
+
}
|
|
1462
|
+
return path.join(this.logDir, filename);
|
|
1463
|
+
}
|
|
1464
|
+
async findLogFiles(pattern = "*.log") {
|
|
1465
|
+
try {
|
|
1466
|
+
const normalizedDir = this.logDir.replace(/\\/g, "/");
|
|
1467
|
+
const fullPattern = `${normalizedDir}/${pattern}`;
|
|
1468
|
+
const files = await glob(fullPattern);
|
|
1469
|
+
return files.sort();
|
|
1470
|
+
} catch {
|
|
1471
|
+
return [];
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
getDefaultLogDir() {
|
|
1475
|
+
return this.logDir;
|
|
1476
|
+
}
|
|
1477
|
+
async* readLines(filepath, options) {
|
|
1478
|
+
const sinceTime = options?.since ? this.parseTime(options.since) : null;
|
|
1479
|
+
const untilTime = options?.until ? this.parseTime(options.until) : null;
|
|
1480
|
+
if (!fs.existsSync(filepath)) {
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1483
|
+
const fileStream = fs.createReadStream(filepath, { encoding: "utf-8" });
|
|
1484
|
+
const rl = readline.createInterface({
|
|
1485
|
+
input: fileStream,
|
|
1486
|
+
crlfDelay: Infinity
|
|
1487
|
+
});
|
|
1488
|
+
try {
|
|
1489
|
+
for await (const line of rl) {
|
|
1490
|
+
if (!line.trim())
|
|
1491
|
+
continue;
|
|
1492
|
+
if (sinceTime || untilTime) {
|
|
1493
|
+
const lineTime = this.extractTimestamp(line);
|
|
1494
|
+
if (lineTime) {
|
|
1495
|
+
if (sinceTime && lineTime < sinceTime)
|
|
1496
|
+
continue;
|
|
1497
|
+
if (untilTime && lineTime > untilTime)
|
|
1498
|
+
continue;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
yield line;
|
|
1502
|
+
}
|
|
1503
|
+
} finally {
|
|
1504
|
+
rl.close();
|
|
1505
|
+
fileStream.destroy();
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
extractTimestamp(line) {
|
|
1509
|
+
const match = line.match(/^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2}:\d{2})/);
|
|
1510
|
+
if (!match)
|
|
1511
|
+
return null;
|
|
1512
|
+
const datePart = match[1];
|
|
1513
|
+
const timePart = match[2];
|
|
1514
|
+
const isoString = `${datePart}T${timePart}`;
|
|
1515
|
+
const timestamp = new Date(isoString).getTime();
|
|
1516
|
+
return isNaN(timestamp) ? null : timestamp;
|
|
1517
|
+
}
|
|
1518
|
+
parseTime(timeStr) {
|
|
1519
|
+
const now = Date.now();
|
|
1520
|
+
const normalizedStr = timeStr.replace("T", " ");
|
|
1521
|
+
const absoluteTime = new Date(normalizedStr).getTime();
|
|
1522
|
+
if (!isNaN(absoluteTime) && absoluteTime > 0) {
|
|
1523
|
+
return absoluteTime;
|
|
1524
|
+
}
|
|
1525
|
+
const originalTime = new Date(timeStr).getTime();
|
|
1526
|
+
if (!isNaN(originalTime) && originalTime > 0) {
|
|
1527
|
+
return originalTime;
|
|
1528
|
+
}
|
|
1529
|
+
const relativeMatch = timeStr.match(/^(\d+)([hms])$/);
|
|
1530
|
+
if (relativeMatch) {
|
|
1531
|
+
const value = parseInt(relativeMatch[1], 10);
|
|
1532
|
+
const unit = relativeMatch[2];
|
|
1533
|
+
switch (unit) {
|
|
1534
|
+
case "h":
|
|
1535
|
+
return now - value * 60 * 60 * 1000;
|
|
1536
|
+
case "m":
|
|
1537
|
+
return now - value * 60 * 1000;
|
|
1538
|
+
case "s":
|
|
1539
|
+
return now - value * 1000;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
return now;
|
|
1543
|
+
}
|
|
1544
|
+
async readFile(filepath) {
|
|
1545
|
+
return fs.promises.readFile(filepath, "utf-8");
|
|
1546
|
+
}
|
|
1547
|
+
exists(filepath) {
|
|
1548
|
+
return fs.existsSync(filepath);
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
// packages/core/src/env/debug/reader/span-db-reader.ts
|
|
1553
|
+
class SpanDbReader {
|
|
1554
|
+
storage = null;
|
|
1555
|
+
dbPath = null;
|
|
1556
|
+
sqliteDb = null;
|
|
1557
|
+
constructor(options) {
|
|
1558
|
+
if (options?.storage) {
|
|
1559
|
+
this.storage = options.storage;
|
|
1560
|
+
} else if (options?.dbPath) {
|
|
1561
|
+
this.dbPath = options.dbPath;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
async initialize() {
|
|
1565
|
+
if (this.storage) {
|
|
1566
|
+
await this.storage.initialize();
|
|
1567
|
+
} else if (this.dbPath) {
|
|
1568
|
+
await this.initSqliteStorage();
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
async initSqliteStorage() {
|
|
1572
|
+
try {
|
|
1573
|
+
const { SQLiteSpanStorage: SQLiteSpanStorage2 } = await Promise.resolve().then(() => exports_span_storage);
|
|
1574
|
+
this.storage = new SQLiteSpanStorage2(this.dbPath);
|
|
1575
|
+
await this.storage.initialize();
|
|
1576
|
+
} catch (error) {
|
|
1577
|
+
throw new Error(`Failed to initialize SQLite storage: ${error}`);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
async getTraceEntries(traceId) {
|
|
1581
|
+
if (!this.storage) {
|
|
1582
|
+
throw new Error("Storage not initialized. Call initialize() first.");
|
|
1583
|
+
}
|
|
1584
|
+
const spans = this.storage.findByTraceId(traceId);
|
|
1585
|
+
if (spans.length === 0) {
|
|
1586
|
+
return [];
|
|
1587
|
+
}
|
|
1588
|
+
return this.spansToEntries(spans);
|
|
1589
|
+
}
|
|
1590
|
+
async listTraceIds(limit = 20) {
|
|
1591
|
+
if (!this.storage) {
|
|
1592
|
+
throw new Error("Storage not initialized. Call initialize() first.");
|
|
1593
|
+
}
|
|
1594
|
+
const traceInfos = this.storage.listTraces(limit);
|
|
1595
|
+
const traceIds = traceInfos.map((info) => ({
|
|
1596
|
+
traceId: info.traceId,
|
|
1597
|
+
firstTime: new Date(info.startTime).toISOString(),
|
|
1598
|
+
lastTime: info.endTime ? new Date(info.endTime).toISOString() : undefined,
|
|
1599
|
+
count: info.spanCount
|
|
1600
|
+
}));
|
|
1601
|
+
return {
|
|
1602
|
+
traceIds,
|
|
1603
|
+
count: traceInfos.length
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
spansToEntries(spans) {
|
|
1607
|
+
const entries = [];
|
|
1608
|
+
for (const span of spans) {
|
|
1609
|
+
entries.push({
|
|
1610
|
+
traceId: span.traceId,
|
|
1611
|
+
timestamp: new Date(span.startTime).toISOString(),
|
|
1612
|
+
function: span.name,
|
|
1613
|
+
action: "enter",
|
|
1614
|
+
params: this.attributesToParams(span.attributes),
|
|
1615
|
+
spanId: span.spanId,
|
|
1616
|
+
parentSpanId: span.parentSpanId
|
|
1617
|
+
});
|
|
1618
|
+
if (span.endTime) {
|
|
1619
|
+
entries.push({
|
|
1620
|
+
traceId: span.traceId,
|
|
1621
|
+
timestamp: new Date(span.endTime).toISOString(),
|
|
1622
|
+
function: span.name,
|
|
1623
|
+
action: "quit",
|
|
1624
|
+
result: span.result,
|
|
1625
|
+
error: span.error,
|
|
1626
|
+
durationMs: span.endTime - span.startTime,
|
|
1627
|
+
spanId: span.spanId,
|
|
1628
|
+
parentSpanId: span.parentSpanId
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
entries.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
1633
|
+
return entries;
|
|
1634
|
+
}
|
|
1635
|
+
attributesToParams(attributes) {
|
|
1636
|
+
if (!attributes)
|
|
1637
|
+
return [];
|
|
1638
|
+
return Object.entries(attributes).map(([key, value]) => ({
|
|
1639
|
+
key,
|
|
1640
|
+
value
|
|
1641
|
+
}));
|
|
1642
|
+
}
|
|
1643
|
+
getStorage() {
|
|
1644
|
+
return this.storage;
|
|
1645
|
+
}
|
|
1646
|
+
close() {
|
|
1647
|
+
if (this.storage) {
|
|
1648
|
+
this.storage.close();
|
|
1649
|
+
this.storage = null;
|
|
1650
|
+
}
|
|
1651
|
+
if (this.sqliteDb) {
|
|
1652
|
+
this.sqliteDb.close();
|
|
1653
|
+
this.sqliteDb = null;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
// packages/core/src/env/debug/parser/regex-parser.ts
|
|
1659
|
+
var TRACE_LINE_REGEX = /^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\.\d{3})\s+\[(\w+)\]\s+\[([^\]]*)\]\[([^\]]*)\]\s+\[TRACE\]\s+(>>>|<<<|!!!)\s+(.+?)(?:\s+\[requestId=([^\]]+)\])?\s+(enter|quit|error):\s+(.*)$/;
|
|
1660
|
+
var REQUEST_ID_IN_CATEGORY_REGEX = /\[requestId=([^\]]+)\]/;
|
|
1661
|
+
|
|
1662
|
+
class RegexParser {
|
|
1663
|
+
parseLogLine(line) {
|
|
1664
|
+
const trimmedLine = line.trim();
|
|
1665
|
+
if (!trimmedLine) {
|
|
1666
|
+
return null;
|
|
1667
|
+
}
|
|
1668
|
+
const match = trimmedLine.match(TRACE_LINE_REGEX);
|
|
1669
|
+
if (!match) {
|
|
1670
|
+
return null;
|
|
1671
|
+
}
|
|
1672
|
+
const [, timestamp, level, source, category, action, method, requestId, actionType, rawArgs] = match;
|
|
1673
|
+
return {
|
|
1674
|
+
raw: trimmedLine,
|
|
1675
|
+
timestamp: timestamp.replace(" ", "T"),
|
|
1676
|
+
level,
|
|
1677
|
+
source,
|
|
1678
|
+
category,
|
|
1679
|
+
action,
|
|
1680
|
+
method,
|
|
1681
|
+
actionType,
|
|
1682
|
+
rawArgs,
|
|
1683
|
+
requestId: requestId || null
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
parseArgs(raw) {
|
|
1687
|
+
if (!raw || !raw.trim()) {
|
|
1688
|
+
return null;
|
|
1689
|
+
}
|
|
1690
|
+
const trimmed = raw.trim();
|
|
1691
|
+
try {
|
|
1692
|
+
const parsed = JSON.parse(trimmed);
|
|
1693
|
+
if (Array.isArray(parsed)) {
|
|
1694
|
+
return parsed;
|
|
1695
|
+
}
|
|
1696
|
+
return parsed;
|
|
1697
|
+
} catch {
|
|
1698
|
+
return trimmed;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
isTraceLine(line) {
|
|
1702
|
+
return line.includes("[TRACE]") && (line.includes(">>>") || line.includes("<<<") || line.includes("!!!"));
|
|
1703
|
+
}
|
|
1704
|
+
filterByLevel(lines, level) {
|
|
1705
|
+
if (!level) {
|
|
1706
|
+
return lines;
|
|
1707
|
+
}
|
|
1708
|
+
const levelPattern = `[${level.toUpperCase()}]`;
|
|
1709
|
+
return lines.filter((line) => {
|
|
1710
|
+
if (!this.isTraceLine(line)) {
|
|
1711
|
+
return false;
|
|
1712
|
+
}
|
|
1713
|
+
return line.includes(levelPattern);
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
filterByFunction(lines, functionName) {
|
|
1717
|
+
if (!functionName) {
|
|
1718
|
+
return lines;
|
|
1719
|
+
}
|
|
1720
|
+
return lines.filter((line) => {
|
|
1721
|
+
return line.includes(functionName);
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
extractTraceId(line) {
|
|
1725
|
+
const categoryMatch = line.match(REQUEST_ID_IN_CATEGORY_REGEX);
|
|
1726
|
+
if (categoryMatch) {
|
|
1727
|
+
return categoryMatch[1];
|
|
1728
|
+
}
|
|
1729
|
+
const match = line.match(TRACE_LINE_REGEX);
|
|
1730
|
+
if (match && match[7]) {
|
|
1731
|
+
return match[7];
|
|
1732
|
+
}
|
|
1733
|
+
return null;
|
|
1734
|
+
}
|
|
1735
|
+
extractTraceIds(lines) {
|
|
1736
|
+
const traceIdMap = new Map;
|
|
1737
|
+
for (const line of lines) {
|
|
1738
|
+
if (!this.isTraceLine(line))
|
|
1739
|
+
continue;
|
|
1740
|
+
const traceId = this.extractTraceId(line);
|
|
1741
|
+
const parsed = this.parseLogLine(line);
|
|
1742
|
+
if (traceId && parsed) {
|
|
1743
|
+
const existing = traceIdMap.get(traceId) || [];
|
|
1744
|
+
existing.push(line);
|
|
1745
|
+
traceIdMap.set(traceId, existing);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
return traceIdMap;
|
|
1749
|
+
}
|
|
1750
|
+
parseLines(lines) {
|
|
1751
|
+
const results = [];
|
|
1752
|
+
for (const line of lines) {
|
|
1753
|
+
const parsed = this.parseLogLine(line);
|
|
1754
|
+
if (parsed) {
|
|
1755
|
+
results.push(parsed);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
return results;
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
// packages/core/src/env/debug/parser/span-builder.ts
|
|
1763
|
+
class SpanBuilder {
|
|
1764
|
+
parser;
|
|
1765
|
+
constructor() {
|
|
1766
|
+
this.parser = new RegexParser;
|
|
1767
|
+
}
|
|
1768
|
+
buildTraceEntries(lines) {
|
|
1769
|
+
const entries = [];
|
|
1770
|
+
for (const line of lines) {
|
|
1771
|
+
const entry = this.buildTraceEntry(line);
|
|
1772
|
+
if (entry) {
|
|
1773
|
+
entries.push(entry);
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
entries.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
1777
|
+
return entries;
|
|
1778
|
+
}
|
|
1779
|
+
buildTraceEntry(line) {
|
|
1780
|
+
const traceId = line.requestId || this.extractTraceId(line.raw);
|
|
1781
|
+
const entry = {
|
|
1782
|
+
traceId: traceId || this.generateVirtualTraceId(line.method, line.timestamp),
|
|
1783
|
+
timestamp: line.timestamp,
|
|
1784
|
+
function: line.method,
|
|
1785
|
+
action: line.actionType
|
|
1786
|
+
};
|
|
1787
|
+
switch (line.actionType) {
|
|
1788
|
+
case "enter":
|
|
1789
|
+
entry.params = this.parser.parseArgs(line.rawArgs);
|
|
1790
|
+
break;
|
|
1791
|
+
case "quit":
|
|
1792
|
+
entry.result = this.parser.parseArgs(line.rawArgs);
|
|
1793
|
+
break;
|
|
1794
|
+
case "error":
|
|
1795
|
+
entry.error = line.rawArgs;
|
|
1796
|
+
break;
|
|
1797
|
+
}
|
|
1798
|
+
return entry;
|
|
1799
|
+
}
|
|
1800
|
+
generateVirtualTraceId(functionName, timestamp) {
|
|
1801
|
+
const base = `${functionName}:${timestamp}`;
|
|
1802
|
+
let hash = 0;
|
|
1803
|
+
for (let i = 0;i < base.length; i++) {
|
|
1804
|
+
const char = base.charCodeAt(i);
|
|
1805
|
+
hash = (hash << 5) - hash + char;
|
|
1806
|
+
hash = hash & hash;
|
|
1807
|
+
}
|
|
1808
|
+
return `vtrace_${Math.abs(hash).toString(36)}`;
|
|
1809
|
+
}
|
|
1810
|
+
extractTraceId(line) {
|
|
1811
|
+
return this.parser.extractTraceId(line);
|
|
1812
|
+
}
|
|
1813
|
+
calculateDuration(enter, quit) {
|
|
1814
|
+
try {
|
|
1815
|
+
const enterTime = new Date(enter.timestamp).getTime();
|
|
1816
|
+
const quitTime = new Date(quit.timestamp).getTime();
|
|
1817
|
+
if (isNaN(enterTime) || isNaN(quitTime)) {
|
|
1818
|
+
return 0;
|
|
1819
|
+
}
|
|
1820
|
+
return quitTime - enterTime;
|
|
1821
|
+
} catch {
|
|
1822
|
+
return 0;
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
pairEnterQuit(entries) {
|
|
1826
|
+
const paired = [];
|
|
1827
|
+
const enterStack = [];
|
|
1828
|
+
for (const entry of entries) {
|
|
1829
|
+
if (entry.action === "enter") {
|
|
1830
|
+
enterStack.push(entry);
|
|
1831
|
+
} else if (entry.action === "quit" && enterStack.length > 0) {
|
|
1832
|
+
const enter = enterStack.pop();
|
|
1833
|
+
const duration = this.calculateDuration(enter, entry);
|
|
1834
|
+
paired.push({
|
|
1835
|
+
enter,
|
|
1836
|
+
quit: entry,
|
|
1837
|
+
durationMs: duration
|
|
1838
|
+
});
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
return paired;
|
|
1842
|
+
}
|
|
1843
|
+
buildCallTree(entries) {
|
|
1844
|
+
if (entries.length === 0) {
|
|
1845
|
+
return null;
|
|
1846
|
+
}
|
|
1847
|
+
const hasSpanHierarchy = entries.some((e) => e.spanId !== undefined || e.parentSpanId !== undefined);
|
|
1848
|
+
if (hasSpanHierarchy) {
|
|
1849
|
+
return this.buildCallTreeFromHierarchy(entries);
|
|
1850
|
+
}
|
|
1851
|
+
return this.buildCallTreeFromPairs(entries);
|
|
1852
|
+
}
|
|
1853
|
+
buildCallTreeFromHierarchy(entries) {
|
|
1854
|
+
const nodeMap = new Map;
|
|
1855
|
+
const rootNodes = [];
|
|
1856
|
+
const addedToRoots = new Set;
|
|
1857
|
+
const enterEntries = entries.filter((e) => e.action === "enter");
|
|
1858
|
+
for (const entry of enterEntries) {
|
|
1859
|
+
const spanId = entry.spanId;
|
|
1860
|
+
const parentSpanId = entry.parentSpanId;
|
|
1861
|
+
if (spanId && nodeMap.has(spanId)) {
|
|
1862
|
+
continue;
|
|
1863
|
+
}
|
|
1864
|
+
const node = {
|
|
1865
|
+
traceId: entry.traceId,
|
|
1866
|
+
function: entry.function,
|
|
1867
|
+
startTime: entry.timestamp,
|
|
1868
|
+
endTime: undefined,
|
|
1869
|
+
durationMs: entry.durationMs,
|
|
1870
|
+
children: [],
|
|
1871
|
+
entry,
|
|
1872
|
+
spanId,
|
|
1873
|
+
parentSpanId
|
|
1874
|
+
};
|
|
1875
|
+
if (spanId) {
|
|
1876
|
+
nodeMap.set(spanId, node);
|
|
1877
|
+
}
|
|
1878
|
+
if (!parentSpanId || !nodeMap.has(parentSpanId)) {
|
|
1879
|
+
rootNodes.push(node);
|
|
1880
|
+
if (spanId)
|
|
1881
|
+
addedToRoots.add(spanId);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
const quitEntries = entries.filter((e) => e.action === "quit");
|
|
1885
|
+
for (const quit of quitEntries) {
|
|
1886
|
+
if (quit.spanId) {
|
|
1887
|
+
const node = nodeMap.get(quit.spanId);
|
|
1888
|
+
if (node) {
|
|
1889
|
+
node.endTime = quit.timestamp;
|
|
1890
|
+
node.durationMs = quit.durationMs || (node.endTime && node.startTime ? new Date(node.endTime).getTime() - new Date(node.startTime).getTime() : undefined);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
for (const node of nodeMap.values()) {
|
|
1895
|
+
if (node.parentSpanId && nodeMap.has(node.parentSpanId)) {
|
|
1896
|
+
const parent = nodeMap.get(node.parentSpanId);
|
|
1897
|
+
if (parent.traceId === node.traceId) {
|
|
1898
|
+
parent.children.push(node);
|
|
1899
|
+
rootNodes.splice(rootNodes.indexOf(node), 1);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
if (rootNodes.length === 0) {
|
|
1904
|
+
for (const entry of enterEntries) {
|
|
1905
|
+
if (!entry.parentSpanId) {
|
|
1906
|
+
const spanId = entry.spanId;
|
|
1907
|
+
if (spanId && nodeMap.has(spanId) && !addedToRoots.has(spanId)) {
|
|
1908
|
+
rootNodes.push(nodeMap.get(spanId));
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
rootNodes.sort((a, b) => a.startTime.localeCompare(b.startTime));
|
|
1914
|
+
if (rootNodes.length === 0) {
|
|
1915
|
+
return null;
|
|
1916
|
+
}
|
|
1917
|
+
if (rootNodes.length === 1) {
|
|
1918
|
+
return rootNodes[0];
|
|
1919
|
+
}
|
|
1920
|
+
const allStartTimes = rootNodes.map((n) => new Date(n.startTime).getTime());
|
|
1921
|
+
const allEndTimes = rootNodes.map((n) => n.endTime ? new Date(n.endTime).getTime() : 0).filter((t) => t > 0);
|
|
1922
|
+
return {
|
|
1923
|
+
traceId: entries[0]?.traceId || "merged",
|
|
1924
|
+
function: "(root)",
|
|
1925
|
+
children: rootNodes,
|
|
1926
|
+
startTime: new Date(Math.min(...allStartTimes)).toISOString(),
|
|
1927
|
+
endTime: allEndTimes.length > 0 ? new Date(Math.max(...allEndTimes)).toISOString() : undefined,
|
|
1928
|
+
durationMs: allEndTimes.length > 0 && allStartTimes.length > 0 ? Math.max(...allEndTimes) - Math.min(...allStartTimes) : undefined
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
buildCallTreeFromPairs(entries) {
|
|
1932
|
+
const sortedEntries = [...entries].sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
1933
|
+
const paired = this.pairEnterQuit(sortedEntries);
|
|
1934
|
+
if (paired.length === 0) {
|
|
1935
|
+
return null;
|
|
1936
|
+
}
|
|
1937
|
+
const stack = [];
|
|
1938
|
+
let root = null;
|
|
1939
|
+
for (const entry of sortedEntries) {
|
|
1940
|
+
if (entry.action === "enter") {
|
|
1941
|
+
const span = paired.find((p) => p.enter === entry);
|
|
1942
|
+
if (!span)
|
|
1943
|
+
continue;
|
|
1944
|
+
const node = this.buildNodeFromPaired(span);
|
|
1945
|
+
if (stack.length === 0) {
|
|
1946
|
+
root = node;
|
|
1947
|
+
} else {
|
|
1948
|
+
stack[stack.length - 1].node.children.push(node);
|
|
1949
|
+
}
|
|
1950
|
+
stack.push({ node, paired: span });
|
|
1951
|
+
} else if (entry.action === "quit" && stack.length > 0) {
|
|
1952
|
+
stack.pop();
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
return root;
|
|
1956
|
+
}
|
|
1957
|
+
buildNodeFromPaired(span) {
|
|
1958
|
+
return {
|
|
1959
|
+
traceId: span.enter.traceId,
|
|
1960
|
+
function: span.enter.function,
|
|
1961
|
+
startTime: span.enter.timestamp,
|
|
1962
|
+
endTime: span.quit.timestamp,
|
|
1963
|
+
durationMs: span.durationMs,
|
|
1964
|
+
children: [],
|
|
1965
|
+
entry: span.enter
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
buildMergedTree(entriesByFunction) {
|
|
1969
|
+
const allEntries = [];
|
|
1970
|
+
for (const entries of entriesByFunction.values()) {
|
|
1971
|
+
allEntries.push(...entries);
|
|
1972
|
+
}
|
|
1973
|
+
allEntries.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
1974
|
+
return this.buildCallTree(allEntries);
|
|
1975
|
+
}
|
|
1976
|
+
getDurationSummary(entries) {
|
|
1977
|
+
const summary = new Map;
|
|
1978
|
+
const paired = this.pairEnterQuit(entries);
|
|
1979
|
+
for (const span of paired) {
|
|
1980
|
+
const func = span.enter.function;
|
|
1981
|
+
const existing = summary.get(func) || { count: 0, totalMs: 0, avgMs: 0 };
|
|
1982
|
+
existing.count++;
|
|
1983
|
+
existing.totalMs += span.durationMs;
|
|
1984
|
+
existing.avgMs = Math.round(existing.totalMs / existing.count);
|
|
1985
|
+
summary.set(func, existing);
|
|
1986
|
+
}
|
|
1987
|
+
return summary;
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
// packages/core/src/env/debug/debug-component.ts
|
|
1992
|
+
class DebugComponent extends BaseComponent {
|
|
1993
|
+
name = "debug";
|
|
1994
|
+
version = "1.0.0";
|
|
1995
|
+
reader;
|
|
1996
|
+
spanDbReader = null;
|
|
1997
|
+
parser;
|
|
1998
|
+
spanBuilder;
|
|
1999
|
+
traceFormatter;
|
|
2000
|
+
treeFormatter;
|
|
2001
|
+
options;
|
|
2002
|
+
constructor(options) {
|
|
2003
|
+
super();
|
|
2004
|
+
this.options = options || {};
|
|
2005
|
+
this.reader = new LogReader({ logDir: this.options.logDir });
|
|
2006
|
+
this.parser = new RegexParser;
|
|
2007
|
+
this.spanBuilder = new SpanBuilder;
|
|
2008
|
+
this.traceFormatter = new TraceFormatter;
|
|
2009
|
+
this.treeFormatter = new TreeFormatter;
|
|
2010
|
+
}
|
|
2011
|
+
async initSpanDbReader(dbPath) {
|
|
2012
|
+
if (!this.spanDbReader) {
|
|
2013
|
+
this.spanDbReader = new SpanDbReader({ dbPath });
|
|
2014
|
+
await this.spanDbReader.initialize();
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
getSpanDbReader() {
|
|
2018
|
+
return this.spanDbReader;
|
|
2019
|
+
}
|
|
2020
|
+
async getLiveTrace(traceId) {
|
|
2021
|
+
try {
|
|
2022
|
+
const { getTracerProvider: getTracerProvider2 } = await Promise.resolve().then(() => (init_tracer_provider(), exports_tracer_provider));
|
|
2023
|
+
const provider = getTracerProvider2();
|
|
2024
|
+
if (!provider.isInitialized()) {
|
|
2025
|
+
await provider.initialize();
|
|
2026
|
+
}
|
|
2027
|
+
const storage = provider.getStorage();
|
|
2028
|
+
const spans = storage.findByTraceId(traceId);
|
|
2029
|
+
if (spans.length === 0) {
|
|
2030
|
+
return null;
|
|
2031
|
+
}
|
|
2032
|
+
const flattenSpans = (spanList) => {
|
|
2033
|
+
const result = [];
|
|
2034
|
+
for (const span of spanList) {
|
|
2035
|
+
result.push({
|
|
2036
|
+
traceId: span.traceId,
|
|
2037
|
+
spanId: span.spanId,
|
|
2038
|
+
parentSpanId: span.parentSpanId,
|
|
2039
|
+
function: span.name,
|
|
2040
|
+
action: "enter",
|
|
2041
|
+
timestamp: new Date(span.startTime).toISOString(),
|
|
2042
|
+
params: Object.entries(span.attributes || {}).map(([k, v]) => ({ key: k, value: v }))
|
|
2043
|
+
});
|
|
2044
|
+
if (span.children && span.children.length > 0) {
|
|
2045
|
+
result.push(...flattenSpans(span.children));
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
return result;
|
|
2049
|
+
};
|
|
2050
|
+
const flatSpans = flattenSpans(spans);
|
|
2051
|
+
const tree = this.spanBuilder.buildCallTree(flatSpans);
|
|
2052
|
+
const rootSpans = spans.filter((s) => !s.parentSpanId);
|
|
2053
|
+
return {
|
|
2054
|
+
traceId,
|
|
2055
|
+
spans,
|
|
2056
|
+
callTree: tree,
|
|
2057
|
+
spanCount: flatSpans.length,
|
|
2058
|
+
rootSpans
|
|
2059
|
+
};
|
|
2060
|
+
} catch (error) {
|
|
2061
|
+
console.error("Failed to get trace from live storage:", error);
|
|
2062
|
+
return null;
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
async listLiveTraces(limit = 20) {
|
|
2066
|
+
try {
|
|
2067
|
+
const { getTracerProvider: getTracerProvider2 } = await Promise.resolve().then(() => (init_tracer_provider(), exports_tracer_provider));
|
|
2068
|
+
const provider = getTracerProvider2();
|
|
2069
|
+
if (!provider.isInitialized()) {
|
|
2070
|
+
await provider.initialize();
|
|
2071
|
+
}
|
|
2072
|
+
const storage = provider.getStorage();
|
|
2073
|
+
const traces = storage.listTraces(limit);
|
|
2074
|
+
return {
|
|
2075
|
+
traces,
|
|
2076
|
+
count: traces.length
|
|
2077
|
+
};
|
|
2078
|
+
} catch (error) {
|
|
2079
|
+
console.error("Failed to list live traces:", error);
|
|
2080
|
+
return { traces: [], count: 0 };
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
async formatLiveTrace(traceId) {
|
|
2084
|
+
try {
|
|
2085
|
+
const { getTracerProvider: getTracerProvider2 } = await Promise.resolve().then(() => (init_tracer_provider(), exports_tracer_provider));
|
|
2086
|
+
const provider = getTracerProvider2();
|
|
2087
|
+
if (!provider.isInitialized()) {
|
|
2088
|
+
await provider.initialize();
|
|
2089
|
+
}
|
|
2090
|
+
const storage = provider.getStorage();
|
|
2091
|
+
const spans = storage.findByTraceId(traceId);
|
|
2092
|
+
if (spans.length === 0) {
|
|
2093
|
+
return `No trace found for traceId: ${traceId}`;
|
|
2094
|
+
}
|
|
2095
|
+
const flattenSpans = (spanList) => {
|
|
2096
|
+
const result = [];
|
|
2097
|
+
for (const span of spanList) {
|
|
2098
|
+
result.push({
|
|
2099
|
+
traceId: span.traceId,
|
|
2100
|
+
spanId: span.spanId,
|
|
2101
|
+
parentSpanId: span.parentSpanId,
|
|
2102
|
+
function: span.name,
|
|
2103
|
+
action: "enter",
|
|
2104
|
+
timestamp: new Date(span.startTime).toISOString(),
|
|
2105
|
+
params: Object.entries(span.attributes || {}).map(([k, v]) => ({ key: k, value: v }))
|
|
2106
|
+
});
|
|
2107
|
+
if (span.children && span.children.length > 0) {
|
|
2108
|
+
result.push(...flattenSpans(span.children));
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
return result;
|
|
2112
|
+
};
|
|
2113
|
+
const flatSpans = flattenSpans(spans);
|
|
2114
|
+
const tree = this.spanBuilder.buildCallTree(flatSpans);
|
|
2115
|
+
if (!tree) {
|
|
2116
|
+
return "No trace structure found";
|
|
2117
|
+
}
|
|
2118
|
+
return this.treeFormatter.formatTree(tree);
|
|
2119
|
+
} catch (error) {
|
|
2120
|
+
return `Failed to format trace: ${error}`;
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
async init(config) {
|
|
2124
|
+
await super.init(config);
|
|
2125
|
+
this.setStatus("running");
|
|
2126
|
+
}
|
|
2127
|
+
async stop() {
|
|
2128
|
+
this.setStatus("stopping");
|
|
2129
|
+
this.setStatus("stopped");
|
|
2130
|
+
}
|
|
2131
|
+
async getTraces(options) {
|
|
2132
|
+
if (options?.source === "sqlite") {
|
|
2133
|
+
return this.getTracesFromSqlite(options);
|
|
2134
|
+
}
|
|
2135
|
+
const logFile = options?.logFile || this.reader.getLogPath();
|
|
2136
|
+
const lines = [];
|
|
2137
|
+
for await (const line of this.reader.readLines(logFile, options)) {
|
|
2138
|
+
if (options?.function && !line.includes(options.function)) {
|
|
2139
|
+
continue;
|
|
2140
|
+
}
|
|
2141
|
+
if (options?.traceId && !line.includes(`requestId=${options.traceId}`)) {
|
|
2142
|
+
continue;
|
|
2143
|
+
}
|
|
2144
|
+
if (this.parser.isTraceLine(line)) {
|
|
2145
|
+
lines.push(line);
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
const parsedLines = this.parser.parseLines(lines);
|
|
2149
|
+
const entries = this.spanBuilder.buildTraceEntries(parsedLines);
|
|
2150
|
+
let filteredEntries = entries;
|
|
2151
|
+
if (options?.function) {
|
|
2152
|
+
filteredEntries = entries.filter((e) => e.function.includes(options.function));
|
|
2153
|
+
}
|
|
2154
|
+
const paired = this.spanBuilder.pairEnterQuit(filteredEntries);
|
|
2155
|
+
const result = [];
|
|
2156
|
+
for (const span of paired) {
|
|
2157
|
+
result.push({
|
|
2158
|
+
...span.enter,
|
|
2159
|
+
durationMs: span.durationMs
|
|
2160
|
+
});
|
|
2161
|
+
if (span.quit) {
|
|
2162
|
+
result.push({
|
|
2163
|
+
...span.quit,
|
|
2164
|
+
durationMs: span.durationMs
|
|
2165
|
+
});
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
return result;
|
|
2169
|
+
}
|
|
2170
|
+
async getTracesFromSqlite(options) {
|
|
2171
|
+
if (!options?.traceId) {
|
|
2172
|
+
return [];
|
|
2173
|
+
}
|
|
2174
|
+
await this.initSpanDbReader(options.dbPath);
|
|
2175
|
+
const reader = this.getSpanDbReader();
|
|
2176
|
+
if (!reader) {
|
|
2177
|
+
throw new Error("Failed to initialize span database reader");
|
|
2178
|
+
}
|
|
2179
|
+
const entries = await reader.getTraceEntries(options.traceId);
|
|
2180
|
+
if (options?.function) {
|
|
2181
|
+
return entries.filter((e) => e.function.includes(options.function));
|
|
2182
|
+
}
|
|
2183
|
+
return entries;
|
|
2184
|
+
}
|
|
2185
|
+
async listTraceIds(options) {
|
|
2186
|
+
if (options?.source === "sqlite") {
|
|
2187
|
+
return this.listTraceIdsFromSqlite(options);
|
|
2188
|
+
}
|
|
2189
|
+
const logFile = options?.logFile || this.reader.getLogPath();
|
|
2190
|
+
const limit = options?.limit || 20;
|
|
2191
|
+
const traceMap = new Map;
|
|
2192
|
+
for await (const line of this.reader.readLines(logFile, options)) {
|
|
2193
|
+
if (!this.parser.isTraceLine(line))
|
|
2194
|
+
continue;
|
|
2195
|
+
const parsed = this.parser.parseLogLine(line);
|
|
2196
|
+
if (!parsed)
|
|
2197
|
+
continue;
|
|
2198
|
+
const traceId = this.parser.extractTraceId(line) || this.spanBuilder.generateVirtualTraceId(parsed.method, parsed.timestamp);
|
|
2199
|
+
const existing = traceMap.get(traceId);
|
|
2200
|
+
if (existing) {
|
|
2201
|
+
existing.lastTime = parsed.timestamp;
|
|
2202
|
+
existing.count++;
|
|
2203
|
+
} else {
|
|
2204
|
+
traceMap.set(traceId, {
|
|
2205
|
+
firstTime: parsed.timestamp,
|
|
2206
|
+
lastTime: parsed.timestamp,
|
|
2207
|
+
count: 1
|
|
2208
|
+
});
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
const traceIds = [];
|
|
2212
|
+
for (const [traceId, info] of traceMap.entries()) {
|
|
2213
|
+
traceIds.push({
|
|
2214
|
+
traceId,
|
|
2215
|
+
firstTime: info.firstTime,
|
|
2216
|
+
lastTime: info.lastTime,
|
|
2217
|
+
count: info.count
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
traceIds.sort((a, b) => b.firstTime.localeCompare(a.firstTime));
|
|
2221
|
+
return {
|
|
2222
|
+
traceIds: traceIds.slice(0, limit),
|
|
2223
|
+
count: traceIds.length
|
|
2224
|
+
};
|
|
2225
|
+
}
|
|
2226
|
+
async listTraceIdsFromSqlite(options) {
|
|
2227
|
+
const limit = options?.limit || 20;
|
|
2228
|
+
await this.initSpanDbReader(options?.dbPath);
|
|
2229
|
+
const reader = this.getSpanDbReader();
|
|
2230
|
+
if (!reader) {
|
|
2231
|
+
throw new Error("Failed to initialize span database reader");
|
|
2232
|
+
}
|
|
2233
|
+
return reader.listTraceIds(limit);
|
|
2234
|
+
}
|
|
2235
|
+
async getCallTree(options) {
|
|
2236
|
+
if (!options?.traceId) {
|
|
2237
|
+
return null;
|
|
2238
|
+
}
|
|
2239
|
+
const traces = await this.getTraces({
|
|
2240
|
+
...options,
|
|
2241
|
+
traceId: options.traceId
|
|
2242
|
+
});
|
|
2243
|
+
if (traces.length === 0) {
|
|
2244
|
+
return null;
|
|
2245
|
+
}
|
|
2246
|
+
return this.spanBuilder.buildCallTree(traces);
|
|
2247
|
+
}
|
|
2248
|
+
getReader() {
|
|
2249
|
+
return this.reader;
|
|
2250
|
+
}
|
|
2251
|
+
getParser() {
|
|
2252
|
+
return this.parser;
|
|
2253
|
+
}
|
|
2254
|
+
getSpanBuilder() {
|
|
2255
|
+
return this.spanBuilder;
|
|
2256
|
+
}
|
|
2257
|
+
getTraceFormatter() {
|
|
2258
|
+
return this.traceFormatter;
|
|
2259
|
+
}
|
|
2260
|
+
getTreeFormatter() {
|
|
2261
|
+
return this.treeFormatter;
|
|
2262
|
+
}
|
|
2263
|
+
formatTraces(entries, options) {
|
|
2264
|
+
return this.traceFormatter.formatEntries(entries, options);
|
|
2265
|
+
}
|
|
2266
|
+
formatTraceEntry(entry, options) {
|
|
2267
|
+
return this.traceFormatter.formatEntry(entry, options);
|
|
2268
|
+
}
|
|
2269
|
+
tracesToJSON(entries) {
|
|
2270
|
+
return this.traceFormatter.toJSON(entries);
|
|
2271
|
+
}
|
|
2272
|
+
tracesToSummary(entries) {
|
|
2273
|
+
return this.traceFormatter.toSummary(entries);
|
|
2274
|
+
}
|
|
2275
|
+
formatTree(node) {
|
|
2276
|
+
return this.treeFormatter.formatTree(node);
|
|
2277
|
+
}
|
|
2278
|
+
formatTrees(trees) {
|
|
2279
|
+
return this.treeFormatter.formatTrees(trees);
|
|
2280
|
+
}
|
|
2281
|
+
treesToJSON(trees) {
|
|
2282
|
+
return this.treeFormatter.toJSON(trees);
|
|
2283
|
+
}
|
|
2284
|
+
formatTreeSummary(node) {
|
|
2285
|
+
return this.treeFormatter.formatSummary(node);
|
|
2286
|
+
}
|
|
2287
|
+
formatTreeTimeline(node) {
|
|
2288
|
+
return this.treeFormatter.formatTimeline(node);
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
export {
|
|
2292
|
+
TreeFormatter,
|
|
2293
|
+
TraceFormatter,
|
|
2294
|
+
SpanDbReader,
|
|
2295
|
+
SpanBuilder,
|
|
2296
|
+
ReplFormatter,
|
|
2297
|
+
RegexParser,
|
|
2298
|
+
LogReader,
|
|
2299
|
+
DebugComponent
|
|
2300
|
+
};
|