@probelabs/probe 0.6.0-rc100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +583 -0
- package/bin/.gitkeep +0 -0
- package/bin/probe +158 -0
- package/bin/probe-binary +0 -0
- package/build/agent/ProbeAgent.d.ts +199 -0
- package/build/agent/ProbeAgent.js +1486 -0
- package/build/agent/acp/README.md +347 -0
- package/build/agent/acp/connection.js +237 -0
- package/build/agent/acp/connection.test.js +311 -0
- package/build/agent/acp/examples/simple-client.js +212 -0
- package/build/agent/acp/examples/tool-lifecycle.js +230 -0
- package/build/agent/acp/final-test.js +173 -0
- package/build/agent/acp/index.js +5 -0
- package/build/agent/acp/integration.test.js +385 -0
- package/build/agent/acp/manual-test.js +410 -0
- package/build/agent/acp/protocol-test.js +190 -0
- package/build/agent/acp/server.js +448 -0
- package/build/agent/acp/server.test.js +371 -0
- package/build/agent/acp/test-runner.js +216 -0
- package/build/agent/acp/test-utils/README.md +315 -0
- package/build/agent/acp/test-utils/acp-tester.js +484 -0
- package/build/agent/acp/test-utils/mock-acp-client.js +434 -0
- package/build/agent/acp/tools.js +368 -0
- package/build/agent/acp/tools.test.js +334 -0
- package/build/agent/acp/types.js +218 -0
- package/build/agent/acp/types.test.js +327 -0
- package/build/agent/appTracer.js +360 -0
- package/build/agent/fileSpanExporter.js +169 -0
- package/build/agent/index.js +7426 -0
- package/build/agent/mcp/client.js +338 -0
- package/build/agent/mcp/config.js +313 -0
- package/build/agent/mcp/index.js +64 -0
- package/build/agent/mcp/xmlBridge.js +371 -0
- package/build/agent/mockProvider.js +53 -0
- package/build/agent/probeTool.js +257 -0
- package/build/agent/schemaUtils.js +1726 -0
- package/build/agent/simpleTelemetry.js +267 -0
- package/build/agent/telemetry.js +225 -0
- package/build/agent/tokenCounter.js +395 -0
- package/build/agent/tools.js +163 -0
- package/build/cli.js +49 -0
- package/build/delegate.js +267 -0
- package/build/directory-resolver.js +237 -0
- package/build/downloader.js +750 -0
- package/build/extract.js +149 -0
- package/build/index.js +70 -0
- package/build/mcp/index.js +514 -0
- package/build/mcp/index.ts +608 -0
- package/build/query.js +116 -0
- package/build/search.js +247 -0
- package/build/tools/common.js +410 -0
- package/build/tools/index.js +40 -0
- package/build/tools/langchain.js +88 -0
- package/build/tools/system-message.js +121 -0
- package/build/tools/vercel.js +271 -0
- package/build/utils/file-lister.js +193 -0
- package/build/utils.js +128 -0
- package/cjs/agent/ProbeAgent.cjs +5829 -0
- package/cjs/index.cjs +6217 -0
- package/cjs/package.json +3 -0
- package/index.d.ts +401 -0
- package/package.json +114 -0
- package/scripts/postinstall.js +172 -0
- package/src/agent/ProbeAgent.d.ts +199 -0
- package/src/agent/ProbeAgent.js +1486 -0
- package/src/agent/acp/README.md +347 -0
- package/src/agent/acp/connection.js +237 -0
- package/src/agent/acp/connection.test.js +311 -0
- package/src/agent/acp/examples/simple-client.js +212 -0
- package/src/agent/acp/examples/tool-lifecycle.js +230 -0
- package/src/agent/acp/final-test.js +173 -0
- package/src/agent/acp/index.js +5 -0
- package/src/agent/acp/integration.test.js +385 -0
- package/src/agent/acp/manual-test.js +410 -0
- package/src/agent/acp/protocol-test.js +190 -0
- package/src/agent/acp/server.js +448 -0
- package/src/agent/acp/server.test.js +371 -0
- package/src/agent/acp/test-runner.js +216 -0
- package/src/agent/acp/test-utils/README.md +315 -0
- package/src/agent/acp/test-utils/acp-tester.js +484 -0
- package/src/agent/acp/test-utils/mock-acp-client.js +434 -0
- package/src/agent/acp/tools.js +368 -0
- package/src/agent/acp/tools.test.js +334 -0
- package/src/agent/acp/types.js +218 -0
- package/src/agent/acp/types.test.js +327 -0
- package/src/agent/appTracer.js +360 -0
- package/src/agent/fileSpanExporter.js +169 -0
- package/src/agent/index.js +813 -0
- package/src/agent/mcp/client.js +338 -0
- package/src/agent/mcp/config.js +313 -0
- package/src/agent/mcp/index.js +64 -0
- package/src/agent/mcp/xmlBridge.js +371 -0
- package/src/agent/mockProvider.js +53 -0
- package/src/agent/probeTool.js +257 -0
- package/src/agent/schemaUtils.js +1726 -0
- package/src/agent/simpleTelemetry.js +267 -0
- package/src/agent/telemetry.js +225 -0
- package/src/agent/tokenCounter.js +395 -0
- package/src/agent/tools.js +163 -0
- package/src/cli.js +49 -0
- package/src/delegate.js +267 -0
- package/src/directory-resolver.js +237 -0
- package/src/downloader.js +750 -0
- package/src/extract.js +149 -0
- package/src/index.js +70 -0
- package/src/mcp/index.ts +608 -0
- package/src/query.js +116 -0
- package/src/search.js +247 -0
- package/src/tools/common.js +410 -0
- package/src/tools/index.js +40 -0
- package/src/tools/langchain.js +88 -0
- package/src/tools/system-message.js +121 -0
- package/src/tools/vercel.js +271 -0
- package/src/utils/file-lister.js +193 -0
- package/src/utils.js +128 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, createWriteStream } from 'fs';
|
|
2
|
+
import { dirname } from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Simple telemetry implementation for probe-agent
|
|
6
|
+
* This provides basic tracing functionality without complex OpenTelemetry dependencies
|
|
7
|
+
*/
|
|
8
|
+
export class SimpleTelemetry {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.serviceName = options.serviceName || 'probe-agent';
|
|
11
|
+
this.enableFile = options.enableFile || false;
|
|
12
|
+
this.enableConsole = options.enableConsole || false;
|
|
13
|
+
this.filePath = options.filePath || './traces.jsonl';
|
|
14
|
+
this.stream = null;
|
|
15
|
+
|
|
16
|
+
if (this.enableFile) {
|
|
17
|
+
this.initializeFileExporter();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
initializeFileExporter() {
|
|
22
|
+
try {
|
|
23
|
+
const dir = dirname(this.filePath);
|
|
24
|
+
if (!existsSync(dir)) {
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
this.stream = createWriteStream(this.filePath, { flags: 'a' });
|
|
29
|
+
this.stream.on('error', (error) => {
|
|
30
|
+
console.error(`[SimpleTelemetry] Stream error: ${error.message}`);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
console.log(`[SimpleTelemetry] File exporter initialized: ${this.filePath}`);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error(`[SimpleTelemetry] Failed to initialize file exporter: ${error.message}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
createSpan(name, attributes = {}) {
|
|
40
|
+
const span = {
|
|
41
|
+
traceId: this.generateTraceId(),
|
|
42
|
+
spanId: this.generateSpanId(),
|
|
43
|
+
name,
|
|
44
|
+
startTime: Date.now(),
|
|
45
|
+
attributes: { ...attributes, service: this.serviceName },
|
|
46
|
+
events: [],
|
|
47
|
+
status: 'OK'
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
...span,
|
|
52
|
+
addEvent: (eventName, eventAttributes = {}) => {
|
|
53
|
+
span.events.push({
|
|
54
|
+
name: eventName,
|
|
55
|
+
time: Date.now(),
|
|
56
|
+
attributes: eventAttributes
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
setAttributes: (attrs) => {
|
|
60
|
+
Object.assign(span.attributes, attrs);
|
|
61
|
+
},
|
|
62
|
+
setStatus: (status) => {
|
|
63
|
+
span.status = status;
|
|
64
|
+
},
|
|
65
|
+
end: () => {
|
|
66
|
+
span.endTime = Date.now();
|
|
67
|
+
span.duration = span.endTime - span.startTime;
|
|
68
|
+
this.exportSpan(span);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
exportSpan(span) {
|
|
74
|
+
const spanData = {
|
|
75
|
+
...span,
|
|
76
|
+
timestamp: new Date().toISOString()
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
if (this.enableConsole) {
|
|
80
|
+
console.log('[Trace]', JSON.stringify(spanData, null, 2));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (this.enableFile && this.stream) {
|
|
84
|
+
this.stream.write(JSON.stringify(spanData) + '\n');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
generateTraceId() {
|
|
89
|
+
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
generateSpanId() {
|
|
93
|
+
return Math.random().toString(36).substring(2, 10);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async flush() {
|
|
97
|
+
if (this.stream) {
|
|
98
|
+
return new Promise((resolve) => {
|
|
99
|
+
this.stream.once('drain', resolve);
|
|
100
|
+
if (!this.stream.writableNeedDrain) {
|
|
101
|
+
resolve();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async shutdown() {
|
|
108
|
+
if (this.stream) {
|
|
109
|
+
return new Promise((resolve) => {
|
|
110
|
+
this.stream.end(() => {
|
|
111
|
+
console.log(`[SimpleTelemetry] File stream closed: ${this.filePath}`);
|
|
112
|
+
resolve();
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Simple tracer for application-level tracing
|
|
121
|
+
*/
|
|
122
|
+
export class SimpleAppTracer {
|
|
123
|
+
constructor(telemetry, sessionId = null) {
|
|
124
|
+
this.telemetry = telemetry;
|
|
125
|
+
this.sessionId = sessionId || this.generateSessionId();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
generateSessionId() {
|
|
129
|
+
return Math.random().toString(36).substring(2, 15);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
isEnabled() {
|
|
133
|
+
return this.telemetry !== null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
createSessionSpan(attributes = {}) {
|
|
137
|
+
if (!this.isEnabled()) return null;
|
|
138
|
+
|
|
139
|
+
return this.telemetry.createSpan('agent.session', {
|
|
140
|
+
'session.id': this.sessionId,
|
|
141
|
+
...attributes
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
createAISpan(modelName, provider, attributes = {}) {
|
|
146
|
+
if (!this.isEnabled()) return null;
|
|
147
|
+
|
|
148
|
+
return this.telemetry.createSpan('ai.request', {
|
|
149
|
+
'ai.model': modelName,
|
|
150
|
+
'ai.provider': provider,
|
|
151
|
+
'session.id': this.sessionId,
|
|
152
|
+
...attributes
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
createToolSpan(toolName, attributes = {}) {
|
|
157
|
+
if (!this.isEnabled()) return null;
|
|
158
|
+
|
|
159
|
+
return this.telemetry.createSpan('tool.call', {
|
|
160
|
+
'tool.name': toolName,
|
|
161
|
+
'session.id': this.sessionId,
|
|
162
|
+
...attributes
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
addEvent(name, attributes = {}) {
|
|
167
|
+
// For simplicity, just log events when no active span
|
|
168
|
+
if (this.telemetry && this.telemetry.enableConsole) {
|
|
169
|
+
console.log('[Event]', name, attributes);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Record delegation events
|
|
175
|
+
*/
|
|
176
|
+
recordDelegationEvent(eventType, data = {}) {
|
|
177
|
+
if (!this.isEnabled()) return;
|
|
178
|
+
|
|
179
|
+
this.addEvent(`delegation.${eventType}`, {
|
|
180
|
+
'session.id': this.sessionId,
|
|
181
|
+
...data
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Record JSON validation events
|
|
187
|
+
*/
|
|
188
|
+
recordJsonValidationEvent(eventType, data = {}) {
|
|
189
|
+
if (!this.isEnabled()) return;
|
|
190
|
+
|
|
191
|
+
this.addEvent(`json_validation.${eventType}`, {
|
|
192
|
+
'session.id': this.sessionId,
|
|
193
|
+
...data
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Record Mermaid validation events
|
|
199
|
+
*/
|
|
200
|
+
recordMermaidValidationEvent(eventType, data = {}) {
|
|
201
|
+
if (!this.isEnabled()) return;
|
|
202
|
+
|
|
203
|
+
this.addEvent(`mermaid_validation.${eventType}`, {
|
|
204
|
+
'session.id': this.sessionId,
|
|
205
|
+
...data
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
setAttributes(attributes) {
|
|
210
|
+
// For simplicity, just log attributes when no active span
|
|
211
|
+
if (this.telemetry && this.telemetry.enableConsole) {
|
|
212
|
+
console.log('[Attributes]', attributes);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async withSpan(spanName, fn, attributes = {}) {
|
|
217
|
+
if (!this.isEnabled()) {
|
|
218
|
+
return fn();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const span = this.telemetry.createSpan(spanName, {
|
|
222
|
+
'session.id': this.sessionId,
|
|
223
|
+
...attributes
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
const result = await fn();
|
|
228
|
+
span.setStatus('OK');
|
|
229
|
+
return result;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
span.setStatus('ERROR');
|
|
232
|
+
span.addEvent('exception', {
|
|
233
|
+
'exception.message': error.message,
|
|
234
|
+
'exception.stack': error.stack
|
|
235
|
+
});
|
|
236
|
+
throw error;
|
|
237
|
+
} finally {
|
|
238
|
+
span.end();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async flush() {
|
|
243
|
+
if (this.telemetry) {
|
|
244
|
+
await this.telemetry.flush();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async shutdown() {
|
|
249
|
+
if (this.telemetry) {
|
|
250
|
+
await this.telemetry.shutdown();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Initialize simple telemetry from CLI options
|
|
257
|
+
*/
|
|
258
|
+
export function initializeSimpleTelemetryFromOptions(options) {
|
|
259
|
+
const telemetry = new SimpleTelemetry({
|
|
260
|
+
serviceName: 'probe-agent',
|
|
261
|
+
enableFile: options.traceFile !== undefined,
|
|
262
|
+
enableConsole: options.traceConsole,
|
|
263
|
+
filePath: options.traceFile || './traces.jsonl'
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
return telemetry;
|
|
267
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import nodeSDKPkg from '@opentelemetry/sdk-node';
|
|
2
|
+
import resourcesPkg from '@opentelemetry/resources';
|
|
3
|
+
import semanticConventionsPkg from '@opentelemetry/semantic-conventions';
|
|
4
|
+
import { trace, context, SpanStatusCode } from '@opentelemetry/api';
|
|
5
|
+
import otlpPkg from '@opentelemetry/exporter-trace-otlp-http';
|
|
6
|
+
import spanPkg from '@opentelemetry/sdk-trace-base';
|
|
7
|
+
|
|
8
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
9
|
+
import { dirname } from 'path';
|
|
10
|
+
import { FileSpanExporter } from './fileSpanExporter.js';
|
|
11
|
+
|
|
12
|
+
const { NodeSDK } = nodeSDKPkg;
|
|
13
|
+
const { resourceFromAttributes } = resourcesPkg;
|
|
14
|
+
const { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } = semanticConventionsPkg;
|
|
15
|
+
const { OTLPTraceExporter } = otlpPkg;
|
|
16
|
+
const { BatchSpanProcessor, ConsoleSpanExporter } = spanPkg;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Custom OpenTelemetry configuration for probe-agent
|
|
20
|
+
*/
|
|
21
|
+
export class TelemetryConfig {
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.serviceName = options.serviceName || 'probe-agent';
|
|
24
|
+
this.serviceVersion = options.serviceVersion || '1.0.0';
|
|
25
|
+
this.enableFile = options.enableFile || false;
|
|
26
|
+
this.enableRemote = options.enableRemote || false;
|
|
27
|
+
this.enableConsole = options.enableConsole || false;
|
|
28
|
+
this.filePath = options.filePath || './traces.jsonl';
|
|
29
|
+
this.remoteEndpoint = options.remoteEndpoint || 'http://localhost:4318/v1/traces';
|
|
30
|
+
this.sdk = null;
|
|
31
|
+
this.tracer = null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Initialize OpenTelemetry SDK
|
|
36
|
+
*/
|
|
37
|
+
initialize() {
|
|
38
|
+
if (this.sdk) {
|
|
39
|
+
console.warn('Telemetry already initialized');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const resource = resourceFromAttributes({
|
|
44
|
+
[ATTR_SERVICE_NAME]: this.serviceName,
|
|
45
|
+
[ATTR_SERVICE_VERSION]: this.serviceVersion,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const spanProcessors = [];
|
|
49
|
+
|
|
50
|
+
// Add file exporter if enabled
|
|
51
|
+
if (this.enableFile) {
|
|
52
|
+
try {
|
|
53
|
+
// Ensure the directory exists
|
|
54
|
+
const dir = dirname(this.filePath);
|
|
55
|
+
if (!existsSync(dir)) {
|
|
56
|
+
mkdirSync(dir, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const fileExporter = new FileSpanExporter(this.filePath);
|
|
60
|
+
spanProcessors.push(new BatchSpanProcessor(fileExporter, {
|
|
61
|
+
maxQueueSize: 2048,
|
|
62
|
+
maxExportBatchSize: 512,
|
|
63
|
+
scheduledDelayMillis: 500,
|
|
64
|
+
exportTimeoutMillis: 30000,
|
|
65
|
+
}));
|
|
66
|
+
console.log(`[Telemetry] File exporter enabled, writing to: ${this.filePath}`);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error(`[Telemetry] Failed to initialize file exporter: ${error.message}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Add remote exporter if enabled
|
|
73
|
+
if (this.enableRemote) {
|
|
74
|
+
try {
|
|
75
|
+
const remoteExporter = new OTLPTraceExporter({
|
|
76
|
+
url: this.remoteEndpoint,
|
|
77
|
+
});
|
|
78
|
+
spanProcessors.push(new BatchSpanProcessor(remoteExporter, {
|
|
79
|
+
maxQueueSize: 2048,
|
|
80
|
+
maxExportBatchSize: 512,
|
|
81
|
+
scheduledDelayMillis: 500,
|
|
82
|
+
exportTimeoutMillis: 30000,
|
|
83
|
+
}));
|
|
84
|
+
console.log(`[Telemetry] Remote exporter enabled, endpoint: ${this.remoteEndpoint}`);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error(`[Telemetry] Failed to initialize remote exporter: ${error.message}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Add console exporter if enabled (useful for debugging)
|
|
91
|
+
if (this.enableConsole) {
|
|
92
|
+
const consoleExporter = new ConsoleSpanExporter();
|
|
93
|
+
spanProcessors.push(new BatchSpanProcessor(consoleExporter, {
|
|
94
|
+
maxQueueSize: 2048,
|
|
95
|
+
maxExportBatchSize: 512,
|
|
96
|
+
scheduledDelayMillis: 500,
|
|
97
|
+
exportTimeoutMillis: 30000,
|
|
98
|
+
}));
|
|
99
|
+
console.log(`[Telemetry] Console exporter enabled`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (spanProcessors.length === 0) {
|
|
103
|
+
console.log('[Telemetry] No exporters configured, telemetry will not be collected');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.sdk = new NodeSDK({
|
|
108
|
+
resource,
|
|
109
|
+
spanProcessors,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
this.sdk.start();
|
|
114
|
+
this.tracer = trace.getTracer(this.serviceName, this.serviceVersion);
|
|
115
|
+
console.log(`[Telemetry] OpenTelemetry SDK initialized successfully`);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.error(`[Telemetry] Failed to start OpenTelemetry SDK: ${error.message}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get the tracer instance
|
|
123
|
+
*/
|
|
124
|
+
getTracer() {
|
|
125
|
+
return this.tracer;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Create a span with the given name and attributes
|
|
130
|
+
*/
|
|
131
|
+
createSpan(name, attributes = {}) {
|
|
132
|
+
if (!this.tracer) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return this.tracer.startSpan(name, {
|
|
137
|
+
attributes,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Wrap a function to automatically create spans
|
|
143
|
+
*/
|
|
144
|
+
wrapFunction(name, fn, attributes = {}) {
|
|
145
|
+
if (!this.tracer) {
|
|
146
|
+
return fn;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return async (...args) => {
|
|
150
|
+
const span = this.createSpan(name, attributes);
|
|
151
|
+
if (!span) {
|
|
152
|
+
return fn(...args);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
const result = await context.with(trace.setSpan(context.active(), span), () => fn(...args));
|
|
157
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
158
|
+
return result;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
span.setStatus({
|
|
161
|
+
code: SpanStatusCode.ERROR,
|
|
162
|
+
message: error.message,
|
|
163
|
+
});
|
|
164
|
+
span.recordException(error);
|
|
165
|
+
throw error;
|
|
166
|
+
} finally {
|
|
167
|
+
span.end();
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Force flush all pending spans
|
|
174
|
+
*/
|
|
175
|
+
async forceFlush() {
|
|
176
|
+
if (this.sdk) {
|
|
177
|
+
try {
|
|
178
|
+
const tracerProvider = trace.getTracerProvider();
|
|
179
|
+
|
|
180
|
+
if (tracerProvider && typeof tracerProvider.forceFlush === 'function') {
|
|
181
|
+
await tracerProvider.forceFlush();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Add a small delay to ensure file writes complete
|
|
185
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
186
|
+
|
|
187
|
+
console.log('[Telemetry] OpenTelemetry spans flushed successfully');
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error(`[Telemetry] Failed to flush OpenTelemetry spans: ${error.message}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Shutdown telemetry
|
|
196
|
+
*/
|
|
197
|
+
async shutdown() {
|
|
198
|
+
if (this.sdk) {
|
|
199
|
+
try {
|
|
200
|
+
await this.sdk.shutdown();
|
|
201
|
+
console.log('[Telemetry] OpenTelemetry SDK shutdown successfully');
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.error(`[Telemetry] Failed to shutdown OpenTelemetry SDK: ${error.message}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Initialize telemetry from CLI options
|
|
211
|
+
*/
|
|
212
|
+
export function initializeTelemetryFromOptions(options) {
|
|
213
|
+
const config = new TelemetryConfig({
|
|
214
|
+
serviceName: 'probe-agent',
|
|
215
|
+
serviceVersion: '1.0.0',
|
|
216
|
+
enableFile: options.traceFile !== undefined,
|
|
217
|
+
enableRemote: options.traceRemote !== undefined,
|
|
218
|
+
enableConsole: options.traceConsole,
|
|
219
|
+
filePath: options.traceFile || './traces.jsonl',
|
|
220
|
+
remoteEndpoint: options.traceRemote || 'http://localhost:4318/v1/traces',
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
config.initialize();
|
|
224
|
+
return config;
|
|
225
|
+
}
|